-
Add embedded documentation support for Mastra packages (#11472)
Mastra packages now include embedded documentation in the published npm package under dist/docs/. This enables coding agents and AI assistants to understand and use the framework by reading documentation directly from node_modules.
Each package includes:
- SKILL.md - Entry point explaining the package's purpose and capabilities
- SOURCE_MAP.json - Machine-readable index mapping exports to types and implementation files
- Topic folders - Conceptual documentation organized by feature area
Documentation is driven by the packages frontmatter field in MDX files, which maps docs to their corresponding packages. CI validation ensures all docs include this field.
-
Add support for retries and scorers parameters across all createStep overloads.
(#11495)
The createStep function now includes support for the retries and scorers fields across all step creation patterns, enabling step-level retry configuration and AI evaluation support for regular steps, agent-based steps, and tool-based steps.
import { init } from '@mastra/inngest';
import { z } from 'zod';
const { createStep } = init(inngest);
// 1. Regular step with retries
const regularStep = createStep({
id: 'api-call',
inputSchema: z.object({ url: z.string() }),
outputSchema: z.object({ data: z.any() }),
retries: 3, // ← Will retry up to 3 times on failure
execute: async ({ inputData }) => {
const response = await fetch(inputData.url);
return { data: await response.json() };
},
});
// 2. Agent step with retries and scorers
const agentStep = createStep(myAgent, {
retries: 3,
scorers: [{ id: 'accuracy-scorer', scorer: myAccuracyScorer }],
});
// 3. Tool step with retries and scorers
const toolStep = createStep(myTool, {
retries: 2,
scorers: [{ id: 'quality-scorer', scorer: myQualityScorer }],
});
This change ensures API consistency across all createStep overloads. All step types now support retry and evaluation configurations.
This is a non-breaking change - steps without these parameters continue to work exactly as before.
Fixes #9351
-
Remove streamVNext, resumeStreamVNext, and observeStreamVNext methods, call stream, resumeStream and observeStream directly (#11499)
+ const run = await workflow.createRun({ runId: '123' });
- const stream = await run.streamVNext({ inputData: { ... } });
+ const stream = await run.stream({ inputData: { ... } });
-
Fix workflow tool not executing when requireApproval is true and tool call is approved (#11538)
-
Breaking Change: memory.readOnly has been moved to memory.options.readOnly (#11523)
The readOnly option now lives inside memory.options alongside other memory configuration like lastMessages and semanticRecall.
Before:
agent.stream('Hello', {
memory: {
thread: threadId,
resource: resourceId,
readOnly: true,
},
});
After:
agent.stream('Hello', {
memory: {
thread: threadId,
resource: resourceId,
options: {
readOnly: true,
},
},
});
Migration: Run the codemod to update your code automatically:
npx @mastra/codemod@beta v1/memory-readonly-to-options .
This also fixes issue #11519 where readOnly: true was being ignored and messages were saved to memory anyway.
-
Fix agent runs with multiple steps only showing last text chunk in observability tools (#11672)
When an agent model executes multiple steps and generates multiple text chunks, the onFinish payload was only receiving the text from the last step instead of all accumulated text. This caused observability tools like Braintrust to only display the final text chunk. The fix now correctly concatenates all text chunks from all steps.
-
Fix tool input validation destroying non-plain objects (#11541)
The convertUndefinedToNull function in tool input validation was treating all objects as plain objects and recursively processing them. For objects like Date, Map, URL, and class instances, this resulted in empty objects {} because they have no enumerable own properties.
This fix changes the approach to only recurse into plain objects (objects with Object.prototype or null prototype). All other objects (Date, Map, Set, URL, RegExp, Error, custom class instances, etc.) are now preserved as-is.
Fixes #11502
-
Fixed client-side tool invocations not being stored in memory. Previously, tool invocations with state 'call' were filtered out before persistence, which incorrectly removed client-side tools. Now only streaming intermediate states ('partial-call') are filtered. (#11630)
Fixed a crash when updating working memory with an empty or null update; existing data is now preserved.
-
Fixed memory readOnly option not being respected when agents share a RequestContext. Previously, when output processors were resolved, the readOnly check happened too early - before the agent could set its own MastraMemory context. This caused child agents to inherit their parent's readOnly setting when sharing a RequestContext. (#11653)
The readOnly check is now only done at execution time in each processor's processOutputResult method, allowing proper isolation.
-
Fix network validation not seeing previous iteration results in multi-step tasks (#11691)
The validation LLM was unable to determine task completion for multi-step tasks because it couldn't see what primitives had already executed. Now includes a compact list of completed primitives in the validation prompt.
-
Fix provider-executed tools (like openai.tools.webSearch()) not working correctly with AI SDK v6 models. The agent's generate() method was ending prematurely with finishReason: 'tool-calls' instead of completing with a text response after tool execution. (#11622)
The issue was that V6 provider tools have type: 'provider' while V5 uses type: 'provider-defined'. The tool preparation code now detects the model version and uses the correct type.
-
Added startExclusive and endExclusive options to dateRange filter for message queries. (#11479)
What changed: The filter.dateRange parameter in listMessages() and Memory.recall() now supports startExclusive and endExclusive boolean options. When set to true, messages with timestamps exactly matching the boundary are excluded from results.
Why this matters: Enables cursor-based pagination for chat applications. When new messages arrive during a session, offset-based pagination can skip or duplicate messages. Using endExclusive: true with the oldest message's timestamp as a cursor ensures consistent pagination without gaps or duplicates.
Example:
// Get first page
const page1 = await memory.recall({
threadId: 'thread-123',
perPage: 10,
orderBy: { field: 'createdAt', direction: 'DESC' },
});
// Get next page using cursor-based pagination
const oldestMessage = page1.messages[page1.messages.length - 1];
const page2 = await memory.recall({
threadId: 'thread-123',
perPage: 10,
orderBy: { field: 'createdAt', direction: 'DESC' },
filter: {
dateRange: {
end: oldestMessage.createdAt,
endExclusive: true, // Excludes the cursor message
},
},
});
-
fix(core): support LanguageModelV3 in MastraModelGateway.resolveLanguageModel (#11489)
-
Fixed agent network not returning text response when routing agent handles requests without delegation. (#11497)
What changed:
- Agent networks now correctly stream text responses when the routing agent decides to handle a request itself instead of delegating to sub-agents, workflows, or tools
- Added fallback in transformers to ensure text is always returned even if core events are missing
Why this matters:
Previously, when using toAISdkV5Stream or networkRoute() outside of the Mastra Studio UI, no text content was returned when the routing agent handled requests directly. This fix ensures consistent behavior across all API routes.
Fixes #11219
-
Add initial state input to workflow form in studio (#11560)
-
Added missing stream types to @mastra/core/stream for better TypeScript support (#11513)
New types available:
- Chunk types:
ToolCallChunk, ToolResultChunk, SourceChunk, FileChunk, ReasoningChunk
- Payload types:
ToolCallPayload, ToolResultPayload, TextDeltaPayload, ReasoningDeltaPayload, FilePayload, SourcePayload
- JSON utilities:
JSONValue, JSONObject, JSONArray and readonly variants
These types are now properly exported, enabling full TypeScript IntelliSense when working with streaming data.
-
Refactor the MessageList class from ~4000 LOC monolith to ~850 LOC with focused, single-responsibility modules. This improves maintainability, testability, and makes the codebase easier to understand. (#11658)
- Extract message format adapters (AIV4Adapter, AIV5Adapter) for SDK conversions
- Extract TypeDetector for centralized message format identification
- Extract MessageStateManager for tracking message sources and persistence
- Extract MessageMerger for streaming message merge logic
- Extract StepContentExtractor for step content extraction
- Extract CacheKeyGenerator for message deduplication
- Consolidate provider compatibility utilities (Gemini, Anthropic, OpenAI)
message-list/
├── message-list.ts # Main class (~850 LOC, down from ~4000)
├── adapters/ # SDK format conversions
│ ├── AIV4Adapter.ts # MastraDBMessage <-> AI SDK V4
│ └── AIV5Adapter.ts # MastraDBMessage <-> AI SDK V5
├── cache/
│ └── CacheKeyGenerator.ts # Deduplication keys
├── conversion/
│ ├── input-converter.ts # Any format -> MastraDBMessage
│ ├── output-converter.ts # MastraDBMessage -> SDK formats
│ ├── step-content.ts # Step content extraction
│ └── to-prompt.ts # LLM prompt formatting
├── detection/
│ └── TypeDetector.ts # Format identification
├── merge/
│ └── MessageMerger.ts # Streaming merge logic
├── state/
│ └── MessageStateManager.ts # Source & persistence tracking
└── utils/
└── provider-compat.ts # Provider-specific fixes
-
Resolve suspendPayload when tripwire is set off in agentic loop to prevent unresolved promises hanging. (#11621)
-
Fix OpenAI reasoning model + memory failing on second generate with "missing item" error (#11492)
When using OpenAI reasoning models with memory enabled, the second agent.generate() call would fail with: "Item 'rs_...' of type 'reasoning' was provided without its required following item."
The issue was that text-start events contain providerMetadata with the text's itemId (e.g., msg_xxx), but this metadata was not being captured. When memory replayed the conversation, the reasoning part had its rs_ ID but the text part was missing its msg_ ID, causing OpenAI to reject the request.
The fix adds handlers for text-start (to capture text providerMetadata) and text-end (to clear it and prevent leaking into subsequent parts).
Fixes #11481
-
Fix reasoning content being lost when text-start chunk arrives before reasoning-end (#11494)
Some model providers (e.g., ZAI/glm-4.6) return streaming chunks where text-start arrives before reasoning-end. Previously, this would clear the accumulated reasoning deltas, resulting in empty reasoning content in the final message. Now text-start is properly excluded from triggering the reasoning state reset, allowing reasoning-end to correctly save the reasoning content.
-
Add resumeGenerate method for resuming agent via generate (#11503)
Add runId and suspendPayload to fullOutput of agent stream
Default suspendedToolRunId to empty string to prevent null issue
-
Adds thread cloning to create independent copies of conversations that can diverge. (#11517)
// Clone a thread
const { thread, clonedMessages } = await memory.cloneThread({
sourceThreadId: 'thread-123',
title: 'My Clone',
options: {
messageLimit: 10, // optional: only copy last N messages
},
});
// Check if a thread is a clone
if (memory.isClone(thread)) {
const source = await memory.getSourceThread(thread.id);
}
// List all clones of a thread
const clones = await memory.listClones('thread-123');
Includes:
- Storage implementations for InMemory, PostgreSQL, LibSQL, Upstash
- API endpoint:
POST /api/memory/threads/:threadId/clone
- Embeddings created for cloned messages (semantic recall)
- Clone button in playground UI Memory tab
-
Fix runEvals() to automatically save scores to storage, making them visible in Studio observability. (#11516)
Previously, runEvals() would calculate scores but not persist them to storage, requiring users to manually implement score saving via the onItemComplete callback. Scores now automatically save when the target (Agent/Workflow) has an associated Mastra instance with storage configured.
What changed:
- Scores are now automatically saved to storage after each evaluation run
- Fixed compatibility with both Agent (
getMastraInstance()) and Workflow (.mastra getter)
- Saved scores include complete context:
groundTruth (in additionalContext), requestContext, traceId, and spanId
- Scores are marked with
source: 'TEST' to distinguish them from live scoring
Migration:
No action required. The onItemComplete workaround for saving scores can be removed if desired, but will continue to work for custom logic.
Example:
const result = await runEvals({
target: mastra.getWorkflow("myWorkflow"),
data: [{ input: {...}, groundTruth: {...} }],
scorers: [myScorer],
});
// Scores are now automatically saved and visible in Studio!
-
Fix autoresume not working fine in useChat (#11486)