Skip to content

Conversation

@renovate
Copy link
Contributor

@renovate renovate bot commented Jan 20, 2026

ℹ️ Note

This PR body was truncated due to platform limits.

This PR contains the following updates:

Package Change Age Confidence
@mastra/core (source) ^0.24.9^1.0.0 age confidence

Release Notes

mastra-ai/mastra (@​mastra/core)

v1.1.0

Compare Source

Minor Changes
  • Restructured stored agents to use a thin metadata record with versioned configuration snapshots. (#​12488)

    The agent record now only stores metadata fields (id, status, activeVersionId, authorId, metadata, timestamps). All configuration fields (name, instructions, model, tools, etc.) live exclusively in version snapshot rows, enabling full version history and rollback.

    Key changes:

    • Stored Agent records are now thin metadata-only (StorageAgentType)
    • All config lives in version snapshots (StorageAgentSnapshotType)
    • New resolved type (StorageResolvedAgentType) merges agent record + active version config
    • Renamed ownerId to authorId for multi-tenant filtering
    • Changed memory field type from string to Record<string, unknown>
    • Added status field ('draft' | 'published') to agent records
    • Flattened CreateAgent/UpdateAgent input types (config fields at top level, no nested snapshot)
    • Version config columns are top-level in the agent_versions table (no single snapshot jsonb column)
    • List endpoints return resolved agents (thin record + active version config)
    • Auto-versioning on update with retention limits and race condition handling
  • Added dynamic agent management with CRUD operations and version tracking (#​12038)

    New Features:

    • Create, edit, and delete agents directly from the Mastra Studio UI
    • Full version history for agents with compare and restore capabilities
    • Visual diff viewer to compare agent configurations across versions
    • Agent creation modal with comprehensive configuration options (model selection, instructions, tools, workflows, sub-agents, memory)
    • AI-powered instruction enhancement

    Storage:

    • New storage interfaces for stored agents and agent versions
    • PostgreSQL, LibSQL, and MongoDB implementations included
    • In-memory storage for development and testing

    API:

    • RESTful endpoints for agent CRUD operations
    • Version management endpoints (create, list, activate, restore, delete, compare)
    • Automatic versioning on agent updates when enabled

    Client SDK:

    • JavaScript client with full support for stored agents and versions
    • Type-safe methods for all CRUD and version operations

    Usage Example:

    // Server-side: Configure storage
    import { Mastra } from '@&#8203;mastra/core';
    import { PgAgentsStorage } from '@&#8203;mastra/pg';
    
    const mastra = new Mastra({
      agents: { agentOne },
      storage: {
        agents: new PgAgentsStorage({
          connectionString: process.env.DATABASE_URL,
        }),
      },
    });
    
    // Client-side: Use the SDK
    import { MastraClient } from '@&#8203;mastra/client-js';
    
    const client = new MastraClient({ baseUrl: 'http://localhost:3000' });
    
    // Create a stored agent
    const agent = await client.createStoredAgent({
      name: 'Customer Support Agent',
      description: 'Handles customer inquiries',
      model: { provider: 'ANTHROPIC', name: 'claude-sonnet-4-5' },
      instructions: 'You are a helpful customer support agent...',
      tools: ['search', 'email'],
    });
    
    // Create a version snapshot
    await client.storedAgent(agent.id).createVersion({
      name: 'v1.0 - Initial release',
      changeMessage: 'First production version',
    });
    
    // Compare versions
    const diff = await client.storedAgent(agent.id).compareVersions('version-1', 'version-2');

    Why:
    This feature enables teams to manage agents dynamically without code changes, making it easier to iterate on agent configurations and maintain a complete audit trail of changes.

  • Added unified Workspace API for agent filesystem access, code execution, and search capabilities. (#​11986)

    New Workspace class combines filesystem, sandbox, and search into a single interface that agents can use for file operations, command execution, and content search.

    Key features:

    • Filesystem operations (read, write, copy, move, delete) through pluggable providers
    • Code and command execution in secure sandboxed environments with optional OS-level isolation
    • Keyword search, semantic search, and hybrid search modes
    • Skills system for discovering and using SKILL.md instruction files
    • Safety controls including read-before-write guards, approval flows, and read-only mode

    Usage:

    import { Workspace, LocalFilesystem, LocalSandbox } from '@&#8203;mastra/core/workspace';
    
    const workspace = new Workspace({
      filesystem: new LocalFilesystem({ basePath: './workspace' }),
      sandbox: new LocalSandbox({ workingDirectory: './workspace' }),
      bm25: true,
    });
    
    const agent = new Agent({
      workspace,
      // Agent automatically receives workspace tools
    });
  • Added status field to listTraces response. The status field indicates the trace state: success (completed without error), error (has error), or running (still in progress). This makes it easier to filter and display traces by their current state without having to derive it from the error and endedAt fields. (#​12213)

  • Added RequestContext.all to access the entire RequestContext object values. (#​12259)

    const { userId, featureFlags } = requestContext.all;

    Added requestContextSchema support to tools, agents, workflows, and steps. Define a Zod schema to validate and type requestContext values at runtime.

    Tool example:

    import { createTool } from '@&#8203;mastra/core/tools';
    import { z } from 'zod';
    
    const myTool = createTool({
      id: 'my-tool',
      inputSchema: z.object({ query: z.string() }),
      requestContextSchema: z.object({
        userId: z.string(),
        apiKey: z.string(),
      }),
      execute: async (input, context) => {
        // context.requestContext is typed as RequestContext<{ userId: string, apiKey: string }>
        const userId = context.requestContext?.get('userId');
        return { result: 'success' };
      },
    });

    Agent example:

    import { Agent } from '@&#8203;mastra/core/agent';
    import { z } from 'zod';
    
    const agent = new Agent({
      name: 'my-agent',
      model: openai('gpt-4o'),
      requestContextSchema: z.object({
        userId: z.string(),
        featureFlags: z
          .object({
            debugMode: z.boolean().optional(),
            enableSearch: z.boolean().optional(),
          })
          .optional(),
      }),
      instructions: ({ requestContext }) => {
        // Access validated context values with type safety
        const { userId, featureFlags } = requestContext.all;
    
        const baseInstructions = `You are a helpful assistant. The current user ID is: ${userId}.`;
    
        if (featureFlags?.debugMode) {
          return `${baseInstructions} Debug mode is enabled - provide verbose responses.`;
        }
    
        return baseInstructions;
      },
      tools: ({ requestContext }) => {
        const tools: Record<string, any> = {
          weatherInfo,
        };
    
        // Conditionally add tools based on validated feature flags
        const { featureFlags } = requestContext.all;
        if (featureFlags?.enableSearch) {
          tools['web_search_preview'] = openai.tools.webSearchPreview();
        }
    
        return tools;
      },
    });

    Workflow example:

    import { createWorkflow } from '@&#8203;mastra/core/workflows';
    import { z } from 'zod';
    
    const workflow = createWorkflow({
      id: 'my-workflow',
      inputSchema: z.object({ data: z.string() }),
      requestContextSchema: z.object({
        tenantId: z.string(),
      }),
    });
    
    const step = createStep({
      id: 'my-step',
      description: 'My step description',
      inputSchema: z.object({ data: z.string() }),
      outputSchema: z.object({ result: z.string() }),
      requestContextSchema: z.object({
        userId: z.string(),
      }),
      execute: async ({ inputData, requestContext }) => {
        const userId = requestContext?.get('userId');
        return {
          result: 'some result here',
        };
      },
    });
    
    workflow.then(step).commit();

    When requestContextSchema is defined, validation runs automatically and throws an error if required context values are missing or invalid.

Patch Changes
  • dependencies updates: (#​10184)

  • Update provider registry and model documentation with latest models and providers (1cf5d2e)

  • Fixed skill loading error caused by Zod version conflicts between v3 and v4. Replaced Zod schemas with plain TypeScript validation functions in skill metadata validation. (#​12485)

  • Fix model router routing providers that use non-default AI SDK packages (e.g. @ai-sdk/anthropic, @ai-sdk/openai) to their correct SDK instead of falling back to openai-compatible. Add cerebras, togetherai, and deepinfra as native SDK providers. (#​12450)

  • Make suspendedToolRunId nullable to fix the null issue in tool input validation (#​12303)

  • Fixed agent.network() to properly pass requestContext to workflow runs. Workflow execution now includes user metadata (userId, resourceId) for observability and analytics. (Fixes #​12330) (#​12379)

  • Fix ModelRouterLanguageModel to propagate supportedUrls from underlying model providers (#​12167)

    Previously, ModelRouterLanguageModel (used when specifying models as strings like "mistral/mistral-large-latest" or "openai/gpt-4o") had supportedUrls hardcoded as an empty object. This caused Mastra to download all file URLs and convert them to bytes/base64, even when the model provider supports URLs natively.

    This fix:

    • Changes supportedUrls to a lazy PromiseLike that resolves the underlying model's supported URL patterns
    • Updates llm-execution-step.ts to properly await supportedUrls when preparing messages

    Impact:

    • Mistral: PDF URLs are now passed directly (fixes #​12152)
    • OpenAI: Image URLs (and PDF URLs in response models) are now passed directly
    • Anthropic: Image URLs are now passed directly
    • Google: Files from Google endpoints are now passed directly

    Note: Users who were relying on Mastra to download files from URLs that model providers cannot directly access (internal URLs, auth-protected URLs) may need to adjust their approach by either using base64-encoded content or ensuring URLs are publicly accessible to the model provider.

  • Extended readOnly memory option to also apply to working memory. When readOnly: true, working memory data is provided as context but the updateWorkingMemory tool is not available. (#​12471)

    Example:

    // Working memory is loaded but agent cannot update it
    const response = await agent.generate('What do you know about me?', {
      memory: {
        thread: 'conversation-123',
        resource: 'user-alice-456',
        options: { readOnly: true },
      },
    });
  • fix(core): skip non-serializable values in RequestContext.toJSON (#​12344)

  • Fixed TypeScript error when calling bail() in workflow steps. bail() now accepts any value, so workflows can exit early with a custom result. Fixes #​12424. (#​12429)

  • Fixed type error when using createTool with Agent when exactOptionalPropertyTypes is enabled in TypeScript config. The ProviderDefinedTool structural type now correctly marks inputSchema as optional and allows execute to be undefined, matching the ToolAction interface. Fixes #​12281 (#​12325)

  • Fixed tracingOptions.tags not being preserved when merging defaultOptions with call-site options. Tags set in agent's defaultOptions.tracingOptions are now correctly passed to all observability exporters (Langfuse, Langsmith, Braintrust, Datadog, etc.). Fixes #​12209. (#​12220)

  • Added activeTools parameter support to model loop stream. The activeTools parameter can now be passed through the ModelLoopStreamArgs to control which tools are available during LLM execution. (#​12082)

  • Fixed agent network crashing with 'Invalid task input' error when routing agent returns malformed JSON for tool/workflow prompts. The error is now fed back to the routing agent, allowing it to retry with valid JSON on the next iteration. (#​12486)

  • Fixed type error when passing MastraVoice implementations (like OpenAIVoice) directly to Agent's voice config. Previously, the voice property only accepted CompositeVoice, requiring users to wrap their voice provider. Now you can pass any MastraVoice implementation directly. (#​12329)

    Before (required wrapper):

    const agent = new Agent({
      voice: new CompositeVoice({ output: new OpenAIVoice() }),
    });

    After (direct usage):

    const agent = new Agent({
      voice: new OpenAIVoice(),
    });

    Fixes #​12293

  • Fixed output processors not being applied to messages saved during network execution. When using agent.network(), configured output processors (like TraceIdInjector for feedback attribution) are now correctly applied to all messages before they are saved to storage. (#​12346)

  • Removed deprecated Google text-embedding-004 embedding model from the model router. Google shut down this model on January 14, 2026. Use google/gemini-embedding-001 instead. (#​12433)

  • Fixed tool input validation failing when LLMs send null for optional fields (#​12362). Zod's .optional() only accepts undefined, not null, causing validation errors with Gemini and other LLMs. Validation now retries with null values stripped when the initial attempt fails, so .optional() fields accept null while .nullable() fields continue to work correctly. (#​12396)

  • Tracing fixes: (#​12370)

    • Spans now inherit entityType/entityId from the closest non-internal parent (#​12250)
    • Processor spans correctly track separate input and output data
    • Model chunk spans are now emitted for all streaming chunks
    • Internal framework spans no longer appear in exported traces
  • Fixed generated provider types so IDs starting with digits no longer break TypeScript builds (#​12418)

  • Fixed network mode not applying user-configured input/output processors (like token limiters) to the routing agent. This caused unbounded context growth during network iterations. (#​12074)

    User-configured processors are now correctly passed to the routing agent, while memory-derived processors (which could interfere with routing logic) are excluded.

    Fixes #​12016

  • Fixed repeated build failures caused by stale global cache (~/.cache/mastra/) containing invalid TypeScript in provider-types.generated.d.ts. Provider names starting with digits (e.g. 302ai) are now properly quoted, and the global cache sync validates .d.ts files before copying to prevent corrupted files from overwriting correct ones. (#​12425)

  • Update @​isaacs/ttlcache to v2 and fix import for v2 compatibility (changed from default to named export) (#​10184)

  • Fixed custom data parts from writer.custom() breaking subsequent messages with Gemini. Messages containing only data-* parts no longer produce empty content arrays that cause Gemini to fail with 'must include at least one parts field'. (#​12373)

  • Improve autoresume prompt sent to LLM to ensure gemini resumes well. (#​12320)
    Gemini sometimes doesn't use the previous messages to create inputData for the tool to resume, the prompt was updated to make sure it gets the inputData from the suspended tool call.

  • Fixed sub-agents in Agent Networks seeing completion-check feedback from prior iterations, so delegated agents stay focused on their task. Fixes #​12224 (#​12338)

  • Fix TypeScript types for custom API route handlers to include requestContext in Hono context Variables. Previously, only mastra was typed, causing TypeScript errors when accessing c.get('requestContext') even though the runtime correctly provided this context. (#​12419)

  • Let callers cancel a running agent network call and handle abort callbacks. (#​12351)

    Example
    Before:

    const stream = await agent.network(task);

    After:

    const controller = new AbortController();
    const stream = await agent.network(task, {
      abortSignal: controller.signal,
      onAbort: ({ primitiveType, primitiveId }) => {
        logger.info(`Aborted ${primitiveType}:${primitiveId}`);
      },
    });
    
    controller.abort();

    Related issue: #12282

v1.0.4

Compare Source

v1.0.0

Compare Source

Major Changes
  • Moving scorers under the eval domain, api method consistency, prebuilt evals, scorers require ids. (#​9589)

  • Scorers for Agents will now use MastraDBMessage instead of UIMessage (#​9702)

    • Scorer input/output types now use MastraDBMessage[] with nested content object structure
    • Added getTextContentFromMastraDBMessage() helper function to extract text content from MastraDBMessage objects
    • Added createTestMessage() helper function for creating MastraDBMessage objects in tests with optional tool invocations support
    • Updated extractToolCalls() to access tool invocations from nested content structure
    • Updated getUserMessageFromRunInput() and getAssistantMessageFromRunOutput() to use new message structure
    • Removed createUIMessage()
  • Every Mastra primitive (agent, MCPServer, workflow, tool, processor, scorer, and vector) now has a get, list, and add method associated with it. Each primitive also now requires an id to be set. (#​9675)

    Primitives that are added to other primitives are also automatically added to the Mastra instance

  • Update handlers to use listWorkflowRuns instead of getWorkflowRuns. Fix type names from StoragelistThreadsByResourceIdInput/Output to StorageListThreadsByResourceIdInput/Output. (#​9507)

  • Refactor workflow and tool types to remove Zod-specific constraints (#​11814)

    Removed Zod-specific type constraints across all workflow implementations and tool types, replacing them with generic types. This ensures type consistency across default, evented, and inngest workflows while preparing for Zod v4 migration.

    Workflow Changes:

    • Removed z.ZodObject<any> and z.ZodType<any> constraints from all workflow generic types
    • Updated method signatures to use TInput and TState directly instead of z.infer<TInput> and z.infer<TState>
    • Aligned conditional types across all workflow implementations using TInput extends unknown pattern
    • Fixed TSteps generic to properly use TEngineType instead of any

    Tool Changes:

    • Removed Zod schema constraints from ToolExecutionContext and related interfaces
    • Simplified type parameters from TSuspendSchema extends ZodLikeSchema to TSuspend and TResume
    • Updated tool execution context types to use generic types

    Type Utilities:

    • Refactored type helpers to work with generic schemas instead of Zod-specific types
    • Updated type extraction utilities for better compatibility

    This change maintains backward compatibility while improving type consistency and preparing for Zod v4 support across all affected packages.

  • Remove getMessagesPaginated() and add perPage: false support (#​9670)

    Removes deprecated getMessagesPaginated() method. The listMessages() API and score handlers now support perPage: false to fetch all records without pagination limits.

    Storage changes:

    • StoragePagination.perPage type changed from number to number | false
    • All storage implementations support perPage: false:
      • Memory: listMessages()
      • Scores: listScoresBySpan(), listScoresByRunId(), listScoresByExecutionId()
    • HTTP query parser accepts "false" string (e.g., ?perPage=false)

    Memory changes:

    • memory.query() parameter type changed from StorageGetMessagesArg to StorageListMessagesInput
    • Uses flat parameters (page, perPage, include, filter, vectorSearchString) instead of selectBy object

    Stricter validation:

    • listMessages() requires non-empty, non-whitespace threadId (throws error instead of returning empty results)

    Migration:

    // Storage/Memory: Replace getMessagesPaginated with listMessages
    - storage.getMessagesPaginated({ threadId, selectBy: { pagination: { page: 0, perPage: 20 } } })
    + storage.listMessages({ threadId, page: 0, perPage: 20 })
    + storage.listMessages({ threadId, page: 0, perPage: false })  // Fetch all
    
    // Memory: Replace selectBy with flat parameters
    - memory.query({ threadId, selectBy: { last: 20, include: [...] } })
    + memory.query({ threadId, perPage: 20, include: [...] })
    
    // Client SDK
    - thread.getMessagesPaginated({ selectBy: { pagination: { page: 0 } } })
    + thread.listMessages({ page: 0, perPage: 20 })
  • Removed storage.getMessages() (#​9695)

    The getMessages() method has been removed from all storage implementations. Use listMessages() instead, which provides pagination support.

    Migration:

    // Before
    const messages = await storage.getMessages({ threadId: 'thread-1' });
    
    // After
    const result = await storage.listMessages({
      threadId: 'thread-1',
      page: 0,
      perPage: 50,
    });
    const messages = result.messages; // Access messages array
    console.log(result.total); // Total count
    console.log(result.hasMore); // Whether more pages exist

    Message ordering default

    listMessages() defaults to ASC (oldest first) ordering by createdAt, matching the previous getMessages() behavior.

    To use DESC ordering (newest first):

    const result = await storage.listMessages({
      threadId: 'thread-1',
      orderBy: { field: 'createdAt', direction: 'DESC' },
    });

    Renamed client.getThreadMessages()client.listThreadMessages()

    Migration:

    // Before
    const response = await client.getThreadMessages(threadId, { agentId });
    
    // After
    const response = await client.listThreadMessages(threadId, { agentId });

    The response format remains the same.

    Removed StorageGetMessagesArg type

    Use StorageListMessagesInput instead:

    // Before
    import type { StorageGetMessagesArg } from '@&#8203;mastra/core';
    
    // After
    import type { StorageListMessagesInput } from '@&#8203;mastra/core';
    • Removes modelSettings.abortSignal in favour of top-level abortSignal only. Also removes the deprecated output field - use structuredOutput.schema instead. (9e1911d)
    • The deprecated generateVNext() and streamVNext() methods have been removed since they're now the stable generate() and stream() methods.
    • The deprecated output option has been removed entirely, in favour of structuredOutput.

    Method renames to clarify the API surface:

    • getDefaultGenerateOptions → getDefaultGenerateOptionsLegacy
    • getDefaultStreamOptions → getDefaultStreamOptionsLegacy
    • getDefaultVNextStreamOptions → getDefaultStreamOptions
  • Bump minimum required Node.js version to 22.13.0 (#​9706)

  • Replace getThreadsByResourceIdPaginated with listThreadsByResourceId across memory handlers. Update client SDK to use listThreads() with offset/limit parameters instead of deprecated getMemoryThreads(). Consolidate /api/memory/threads routes to single paginated endpoint. (#​9508)

  • Add new list methods to storage API: listMessages, listMessagesById, listThreadsByResourceId, and listWorkflowRuns. Most methods are currently wrappers around existing methods. Full implementations will be added when migrating away from legacy methods. (#​9489)

  • Update tool execution signature (#​9587)

    Consolidated the 3 different execution contexts to one

    // before depending on the context the tool was executed in
    tool.execute({ context: data });
    tool.execute({ context: { inputData: data } });
    tool.execute(data);
    
    // now, for all contexts
    tool.execute(data, context);

    Before:

    inputSchema: z.object({ something: z.string() }),
    execute: async ({ context, tracingContext, runId, ... }) => {
      return doSomething(context.string);
    }

    After:

    inputSchema: z.object({ something: z.string() }),
    execute: async (inputData, context) => {
      const { agent, mcp, workflow, ...sharedContext } = context
    
      // context that only an agent would get like toolCallId, messages, suspend, resume, etc
      if (agent) {
        doSomething(inputData.something, agent)
      // context that only a workflow would get like runId, state, suspend, resume, etc
      } else if (workflow) {
        doSomething(inputData.something, workflow)
      // context that only a workflow would get like "extra", "elicitation"
      } else if (mcp) {
        doSomething(inputData.something, mcp)
      } else {
        // Running a tool in no execution context
        return doSomething(inputData.something);
      }
    }
  • The @mastra/core package no longer allows top-level imports except for Mastra and type Config. You must use subpath imports for all other imports. (#​9544)

    For example:

      import { Mastra, type Config } from "@&#8203;mastra/core";
    - import { Agent } from "@&#8203;mastra/core";
    - import { createTool } from "@&#8203;mastra/core";
    - import { createStep } from "@&#8203;mastra/core";
    
    + import { Agent } from "@&#8203;mastra/core/agent";
    + import { createTool } from "@&#8203;mastra/core/tools";
    + import { createStep } from "@&#8203;mastra/core/workflows";
  • This simplifies the Memory API by removing the confusing rememberMessages method and renaming query to recall for better clarity. (#​9701)

    The rememberMessages method name implied it might persist data when it was actually just retrieving messages, same as query. Having two methods that did essentially the same thing was unnecessary.

    Before:

    // Two methods that did the same thing
    memory.rememberMessages({ threadId, resourceId, config, vectorMessageSearch });
    memory.query({ threadId, resourceId, perPage, vectorSearchString });

    After:

    // Single unified method with clear purpose
    memory.recall({ threadId, resourceId, perPage, vectorMessageSearch, threadConfig });

    All usages have been updated across the codebase including tests. The agent now calls recall directly with the appropriate parameters.

  • Rename RuntimeContext to RequestContext (#​9511)

  • Implement listMessages API for replacing previous methods (#​9531)

  • Rename defaultVNextStreamOptions to defaultOptions. Add "Legacy" suffix to v1 option properties and methods (defaultGenerateOptionsdefaultGenerateOptionsLegacy, defaultStreamOptionsdefaultStreamOptionsLegacy). (#​9535)

  • Breaking Change: Convert OUTPUT generic from OutputSchema constraint to plain generic (#​11741)

    This change removes the direct dependency on Zod typings in the public API by converting all OUTPUT extends OutputSchema generic constraints to plain OUTPUT generics throughout the codebase. This is preparation for moving to a standard schema approach.

    • All generic type parameters previously constrained to OutputSchema (e.g., <OUTPUT extends OutputSchema = undefined>) are now plain generics with defaults (e.g., <OUTPUT = undefined>)
    • Affects all public APIs including Agent, MastraModelOutput, AgentExecutionOptions, and stream/generate methods
    • InferSchemaOutput<OUTPUT> replaced with OUTPUT throughout
    • PartialSchemaOutput<OUTPUT> replaced with Partial<OUTPUT>
    • Schema fields now use NonNullable<OutputSchema<OUTPUT>> instead of OUTPUT directly
    • Added FullOutput<OUTPUT> type representing complete output with all fields
    • Added AgentExecutionOptionsBase<OUTPUT> type
    • getFullOutput() method now returns Promise<FullOutput<OUTPUT>>
    • Agent class now generic: Agent<TAgentId, TTools, TOutput>
    • agent.generate() and agent.stream() methods have updated signatures
    • MastraModelOutput<OUTPUT> no longer requires OutputSchema constraint
    • Network route and streaming APIs updated to use plain OUTPUT generic

    Before:

    const output = await agent.generate<z.ZodType>([...], {
      structuredOutput: { schema: mySchema }
    });
    
    **After:**
    const output = await agent.generate<z.infer<typeof mySchema>>([...], {
      structuredOutput: { schema: mySchema }
    });
    // Or rely on type inference:
    const output = await agent.generate([...], {
      structuredOutput: { schema: mySchema }
    });
  • Remove getThreadsByResourceId and getThreadsByResourceIdPaginated methods from storage interfaces in favor of listThreadsByResourceId. The new method uses offset/limit pagination and a nested orderBy object structure ({ field, direction }). (#​9536)

  • Remove getMessagesById method from storage interfaces in favor of listMessagesById. The new method only returns V2-format messages and removes the format parameter, simplifying the API surface. Users should migrate from getMessagesById({ messageIds, format }) to listMessagesById({ messageIds }). (#​9534)

  • Experimental auth -> auth (#​9660)

  • Renamed a bunch of observability/tracing-related things to drop the AI prefix. (#​9744)

  • Removed MastraMessageV3 intermediary format, now we go from MastraDBMessage->aiv5 formats and back directly (#​9094)

  • Breaking Change: Remove legacy v1 watch events and consolidate on v2 implementation. (#​9252)

    This change simplifies the workflow watching API by removing the legacy v1 event system and promoting v2 as the standard (renamed to just watch).

    What's Changed

    • Removed legacy v1 watch event handlers and types
    • Renamed watch-v2 to watch throughout the codebase
    • Removed .watch() method from client-js SDK (Workflow and AgentBuilder classes)
    • Removed /watch HTTP endpoints from server and deployer
    • Removed WorkflowWatchResult and v1 WatchEvent types
  • Remove various deprecated APIs from agent class. (#​9257)

    • agent.llmagent.getLLM()
    • agent.toolsagent.getTools()
    • agent.instructionsagent.getInstructions()
    • agent.speak()agent.voice.speak()
    • agent.getSpeakers()agent.voice.getSpeakers()
    • agent.listenagent.voice.listen()
    • agent.fetchMemory(await agent.getMemory()).query()
    • agent.toStep → Add agent directly to the step, workflows handle the transformation
  • Pagination APIs now use page/perPage instead of offset/limit (#​9592)

    All storage and memory pagination APIs have been updated to use page (0-indexed) and perPage instead of offset and limit, aligning with standard REST API patterns.

    Affected APIs:

    • Memory.listThreadsByResourceId()
    • Memory.listMessages()
    • Storage.listWorkflowRuns()

    Migration:

    // Before
    await memory.listThreadsByResourceId({
      resourceId: 'user-123',
      offset: 20,
      limit: 10,
    });
    
    // After
    await memory.listThreadsByResourceId({
      resourceId: 'user-123',
      page: 2, // page = Math.floor(offset / limit)
      perPage: 10,
    });
    
    // Before
    await memory.listMessages({
      threadId: 'thread-456',
      offset: 20,
      limit: 10,
    });
    
    // After
    await memory.listMessages({
      threadId: 'thread-456',
      page: 2,
      perPage: 10,
    });
    
    // Before
    await storage.listWorkflowRuns({
      workflowName: 'my-workflow',
      offset: 20,
      limit: 10,
    });
    
    // After
    await storage.listWorkflowRuns({
      workflowName: 'my-workflow',
      page: 2,
      perPage: 10,
    });

    Additional improvements:

    • Added validation for negative page values in all storage implementations
    • Improved perPage validation to handle edge cases (negative values, 0, false)
    • Added reusable query parser utilities for consistent validation in handlers
  • import { Mastra } from '@&#8203;mastra/core';
    import { Observability } from '@&#8203;mastra/observability'; // Explicit import
    
    const mastra = new Mastra({
      ...other_config,
      observability: new Observability({
        default: { enabled: true },
      }), // Instance
    });

    Instead of:

    import { Mastra } from '@&#8203;mastra/core';
    import '@&#8203;mastra/observability/init'; // Explicit import
    
    const mastra = new Mastra({
      ...other_config,
      observability: {
        default: { enabled: true },
      },
    });

    Also renamed a bunch of:

    • Tracing things to Observability things.
    • AI- things to just things.
  • Changing getAgents -> listAgents, getTools -> listTools, getWorkflows -> listWorkflows (#​9495)

  • Removed old tracing code based on OpenTelemetry (#​9237)

  • Remove deprecated vector prompts and cohere provider from code (#​9596)

  • Renamed MastraStorage to MastraCompositeStore for better clarity. The old MastraStorage name remains available as a deprecated alias for backward compatibility, but will be removed in a future version. (#​12093)

    Migration:

    Update your imports and usage:

    // Before
    import { MastraStorage } from '@&#8203;mastra/core/storage';
    
    const storage = new MastraStorage({
      id: 'composite',
      domains: { ... }
    });
    
    // After
    import { MastraCompositeStore } from '@&#8203;mastra/core/storage';
    
    const storage = new MastraCompositeStore({
      id: 'composite',
      domains: { ... }
    });

    The new name better reflects that this is a composite storage implementation that routes different domains (workflows, traces, messages) to different underlying stores, avoiding confusion with the general "Mastra Storage" concept.

  • Mark as stable (83d5942)

  • Changed .branch() result schema to make all branch output fields optional. (#​10693)

    Breaking change: Branch outputs are now optional since only one branch executes at runtime. Update your workflow schemas to handle optional branch results.

    Before:

    const workflow = createWorkflow({...})
      .branch([
        [condition1, stepA],  // outputSchema: { result: z.string() }
        [condition2, stepB],  // outputSchema: { data: z.number() }
      ])
      .map({
        finalResult: { step: stepA, path: 'result' }  // Expected non-optional
      });

    After:

    const workflow = createWorkflow({...})
      .branch([
        [condition1, stepA],
        [condition2, stepB],
      ])
      .map({
        finalResult: {
          step: stepA,
          path: 'result'  // Now optional - provide fallback
        }
      });

    Why: Branch conditionals execute only one path, so non-executed branches don't produce outputs. The type system now correctly reflects this runtime behavior.

    Related issue: #​10642

  • Enforcing id required on Processor primitive (#​9591)

  • Breaking Changes: (#​9045)

    • Moved generateTitle from threads.generateTitle to top-level memory option
    • Changed default value from true to false
    • Using threads.generateTitle now throws an error

    Migration:
    Replace threads: { generateTitle: true } with generateTitle: true at the top level of memory options.

    Playground:
    The playground UI now displays thread IDs instead of "Chat from" when titles aren't generated.

  • Renamed MastraMessageV2 to MastraDBMessage (#​9255)
    Made the return format of all methods that return db messages consistent. It's always { messages: MastraDBMessage[] } now, and messages can be converted after that using @mastra/ai-sdk/ui's toAISdkV4/5Messages() function

  • moved ai-tracing code into @​mastra/observability (#​9661)

  • Remove legacy evals from Mastra (#​9491)

  • Use tool's outputSchema to validate results and return an error object if schema does not match output results. (#​9664)

    const getUserTool = createTool({
      id: 'get-user',
      outputSchema: z.object({
        id: z.string(),
        name: z.string(),
        email: z.string().email(),
      }),
      execute: async inputData => {
        return { id: '123', name: 'John' };
      },
    });

    When validation fails, the tool returns a ValidationError:

    // Before v1 - invalid output would silently pass through
    await getUserTool.execute({});
    // { id: "123", name: "John" } - missing email
    
    // After v1 - validation error is returned
    await getUserTool.execute({});
    // {
    //   error: true,
    //   message: "Tool output validation failed for get-user. The tool returned invalid output:\n- email: Required\n\nReturned output: {...}",
    //   validationErrors: { ... }
    // }
  • Removes deprecated input-processor type and processors. (#​9200)

Minor Changes
  • Add context parameter to idGenerator to enable deterministic ID generation based on context. (#​10964)

    The idGenerator function now receives optional context about what type of ID is being generated and from which Mastra primitive. This allows generating IDs that can be shared with external databases.

    const mastra = new Mastra({
      idGenerator: context => {
        // context.idType: 'thread' | 'message' | 'run' | 'step' | 'generic'
        // context.source: 'agent' | 'workflow' | 'memory'
        // context.entityId: the agent/workflow id
        // context.threadId, context.resourceId, context.role, context.stepType
    
        if (context?.idType === 'message' && context?.threadId) {
          return `msg-${context.threadId}-${Date.now()}`;
        }
        if (context?.idType === 'run' && context?.source === 'agent') {
          return `run-${context.entityId}-${Date.now()}`;
        }
        return crypto.randomUUID();
      },
    });

    Existing idGenerator functions without parameters continue to work since the context is optional.

    Fixes #​8131

  • Added flush() method to ObservabilityExporter, ObservabilityBridge, and ObservabilityInstance interfaces (#​12003)

  • Add hideInput and hideOutput options to TracingOptions for protecting sensitive data in traces. (#​11969)

    When set to true, these options hide input/output data from all spans in a trace, including child spans. This is useful for protecting sensitive information from being logged to observability platforms.

    const agent = mastra.getAgent('myAgent');
    await agent.generate('Process this sensitive data', {
      tracingOptions: {
        hideInput: true, // Input will be hidden from all spans
        hideOutput: true, // Output will be hidden from all spans
      },
    });

    The options can be used independently (hide only input or only output) or together. The settings are propagated to all child spans via TraceState, ensuring consistent behavior across the entire trace.

    Fixes #​10888

  • Add onError hook to server configuration for custom error handling. (#​11403)

    You can now provide a custom error handler through the Mastra server config to catch errors, format responses, or send them to external services like Sentry:

    import { Mastra } from '@&#8203;mastra/core/mastra';
    
    const mastra = new Mastra({
      server: {
        onError: (err, c) => {
          // Send to Sentry
          Sentry.captureException(err);
    
          // Return custom formatted response
          return c.json(
            {
              error: err.message,
              timestamp: new Date().toISOString(),
            },
            500,
          );
        },
      },
    });

    If no onError is provided, the default error handler is used.

    Fixes #​9610

  • Add stored agents support (#​10953)

    Agents can now be stored in the database and loaded at runtime. This lets you persist agent configurations and dynamically create executable Agent instances from storage.

    import { Mastra } from '@&#8203;mastra/core';
    import { LibSQLStore } from '@&#8203;mastra/libsql';
    
    const mastra = new Mastra({
      storage: new LibSQLStore({ url: ':memory:' }),
      tools: { myTool },
      scorers: { myScorer },
    });
    
    // Create agent in storage via API or directly
    await mastra.getStorage().createAgent({
      agent: {
        id: 'my-agent',
        name: 'My Agent',
        instructions: 'You are helpful',
        model: { provider: 'openai', name: 'gpt-4' },
        tools: { myTool: {} },
        scorers: { myScorer: { sampling: { type: 'ratio', rate: 0.5 } } },
      },
    });
    
    // Load and use the agent
    const agent = await mastra.getStoredAgentById('my-agent');
    const response = await agent.generate('Hello!');
    
    // List all stored agents with pagination
    const { agents, total, hasMore } = await mastra.listStoredAgents({
      page: 0,
      perPage: 10,
    });

    Also adds a memory registry to Mastra so stored agents can reference memory instances by key.

  • Added human-in-the-loop (HITL) tool approval support for generate() method. (#​12056)

    Why: This provides parity between stream() and generate() for tool approval flows, allowing non-streaming use cases to leverage requireToolApproval without needing to switch to streaming.

    Previously, tool approval with requireToolApproval only worked with stream(). Now you can use the same approval flow with generate() for non-streaming use cases.

    Using tool approval with generate()

    const output = await agent.generate('Find user John', {
      requireToolApproval: true,
    });
    
    // Check if a tool is waiting for approval
    if (output.finishReason === 'suspended') {
      console.log('Tool requires approval:', output.suspendPayload.toolName);
    
      // Approve the tool call
      const result = await agent.approveToolCallGenerate({
        runId: output.runId,
        toolCallId: output.suspendPayload.toolCallId,
      });
    
      console.log(result.text);
    }

    Declining a tool call

    if (output.finishReason === 'suspended') {
      const result = await agent.declineToolCallGenerate({
        runId: output.runId,
        toolCallId: output.suspendPayload.toolCallId,
      });
    }

    New methods added:

    • agent.approveToolCallGenerate({ runId, toolCallId }) - Approves a pending tool call and returns the complete result
    • agent.declineToolCallGenerate({ runId, toolCallId }) - Declines a pending tool call and returns the complete result

    Server routes added:

    • POST /api/agents/:agentId/approve-tool-call-generate
    • POST /api/agents/:agentId/decline-tool-call-generate

    The playground UI now also supports tool approval when using generate mode.

  • Memory system now uses processors. Memory processors (MessageHistory, SemanticRecall, WorkingMemory) are now exported from @mastra/memory/processors and automatically added to the agent pipeline based on your memory config. Core processors (ToolCallFilter, TokenLimiter) remain in @mastra/core/processors. (#​9254)

  • Add reserved keys in RequestContext for secure resourceId/threadId setting from middleware (#​10657)

    This allows middleware to securely set resourceId and threadId via reserved keys in RequestContext (MASTRA_RESOURCE_ID_KEY and MASTRA_THREAD_ID_KEY), which take precedence over client-provided values for security.

  • Removed the deprecated AISDKV5OutputStream class from the public API. (#​11845)

    What changed: The AISDKV5OutputStream class is no longer exported from @mastra/core. This class was previously used with the format: 'aisdk' option, which has already been removed from .stream() and .generate() methods.

    Who is affected: Only users who were directly importing AISDKV5OutputStream from @mastra/core. If you were using the standard .stream() or .generate() methods without the format option, no changes are needed.

    Migration: If you were importing this class directly, switch to using MastraModelOutput which provides the same streaming functionality:

    // Before
    import { AISDKV5OutputStream } from '@&#8203;mastra/core';
    
    // After
    import { MastraModelOutput } from '@&#8203;mastra/core';
  • Changed JSON columns from TEXT to JSONB in mastra_threads and mastra_workflow_snapshot tables. (#​11853)

    Why this change?

    These were the last remaining columns storing JSON as TEXT. This change aligns them with other tables that already use JSONB, enabling native JSON operators and improved performance. See #​8978 for details.

    Columns Changed:

    • mastra_threads.metadata - Thread metadata
    • mastra_workflow_snapshot.snapshot - Workflow run state

    PostgreSQL

    Migration Required - PostgreSQL enforces column types, so existing tables must be migrated. Note: Migration will fail if existing column values contain invalid JSON.

    ALTER TABLE mastra_threads
    ALTER COLUMN metadata TYPE jsonb
    USING metadata::jsonb;
    
    ALTER TABLE mastra_workflow_snapshot
    ALTER COLUMN snapshot TYPE jsonb
    USING snapshot::jsonb;

    LibSQL

    No Migration Required - LibSQL now uses native SQLite JSONB format (added in SQLite 3.45) for ~3x performance improvement on JSON operations. The changes are fully backwards compatible:

    • Existing TEXT JSON data continues to work
    • New data is stored in binary JSONB format
    • Both formats can coexist in the same table
    • All JSON functions (json_extract, etc.) work on both formats

    New installations automatically use JSONB. Existing applications continue to work without any changes.

  • Memory scope defaults changed from 'thread' to 'resource' (#​8983)

    Both workingMemory.scope and semanticRecall.scope now default to 'resource' instead of 'thread'. This means:

    • Working memory persists across all conversations for the same user/resource
    • Semantic recall searches across all threads for the same user/resource

    Migration: To maintain the previous thread-scoped behavior, explicitly set scope: 'thread':

    memory: new Memory({
      storage,
      workingMemory: {
        enabled: true,
        scope: 'thread', // Explicitly set for thread-scoped behavior
      },
      semanticRecall: {
        scope: 'thread', // Explicitly set for thread-scoped behavior
      },
    }),

    Also fixed issues where playground semantic recall search could show missing or incorrect results in certain cases.

  • Add support for AI SDK v6 (LanguageModelV3) (#​11191)

    Agents can now use LanguageModelV3 models from AI SDK v6 beta providers like @ai-sdk/openai@^3.0.0-beta.

    New features:

    • Usage normalization: V3's nested usage format is normalized to Mastra's flat format with reasoningTokens, cachedInputTokens, and raw data preserved in a raw field

    Backward compatible: All existing V1 and V2 models continue to work unchanged.

  • Respect structured outputs for v2 models so tool schemas aren’t stripped (#​11038)

  • Deprecate default: { enabled: true } observability configuration (#​11674)

    The shorthand default: { enabled: true } configuration is now deprecated and will be removed in a future version. Users should migrate to explicit configuration with DefaultExporter, CloudExporter, and SensitiveDataFilter.

    Before (deprecated):

    import { Observability } from '@&#8203;mastra/observability';
    
    const mastra = new Mastra({
      observability: new Observability({
        default: { enabled: true },
      }),
    });

    After (recommended):

    import { Observability, DefaultExporter, CloudExporter, SensitiveDataFilter } from '@&#8203;mastra/observability';
    
    const mastra = new Mastra({
      observability: new Observability({
        configs: {
          default: {
            serviceName: 'mastra',
            exporters: [new DefaultExporter(), new CloudExporter()],
            spanOutputProcessors: [new SensitiveDataFilter()],
          },
        },
      }),
    });

    The explicit configuration makes it clear exactly what exporters and processors are being used, improving code readability and maintainability.

    A deprecation warning will be logged when using the old configuration pattern.

  • Exported isProcessorWorkflow function from @​mastra/core/processors. Added getConfiguredProcessorWorkflows() method to agents and listProcessors() method to the Mastra class for programmatic access to processor information. (#​12059)

  • Adds a new suspendData parameter to workflow step execute functions that provides access to the data originally passed to suspend() when the step was suspended. This enables steps to access context about why they were suspended when they are later resumed. (#​10734)

    New Features:

    • suspendData parameter automatically populated in step execute function when resuming
    • Type-safe access to suspend data matching the step's suspendSchema
    • Backward compatible - existing workflows continue to work unchanged

    Example:

    const step = createStep({
      suspendSchema: z.object({ reason: z.string() }),
      resumeSchema: z.object({ approved: z.boolean() }),
    

Configuration

📅 Schedule: Branch creation - "every weekday" (UTC), Automerge - At any time (no schedule defined).

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate renovate bot force-pushed the renovate/mastra-core-1.x branch 2 times, most recently from c0ab850 to e95b6ef Compare January 27, 2026 14:41
@renovate renovate bot force-pushed the renovate/mastra-core-1.x branch from e95b6ef to b080275 Compare January 30, 2026 11:03
@renovate renovate bot changed the title chore(deps): update dependency @mastra/core to v1 chore(deps): update dependency @mastra/core to v1 - autoclosed Feb 2, 2026
@renovate renovate bot closed this Feb 2, 2026
@renovate renovate bot deleted the renovate/mastra-core-1.x branch February 2, 2026 10:37
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.

0 participants