Repository navigation
Update dependency @mastra/core to v1 - #12
Open
renovate[bot] wants to merge 1 commit into
Open
renovate[bot] wants to merge 1 commit into
renovate[bot] wants to merge 1 commit into
Conversation
renovate
Bot
force-pushed
the
renovate/mastra-core-1.x
branch
2 times, most recently
from
April 18, 2026 08:52
6de8b46 to
b37e28c
Compare
renovate
Bot
force-pushed
the
renovate/mastra-core-1.x
branch
4 times, most recently
from
May 2, 2026 01:49
89500b2 to
c6c04dd
Compare
renovate
Bot
force-pushed
the
renovate/mastra-core-1.x
branch
2 times, most recently
from
May 12, 2026 09:49
0098570 to
19b0b67
Compare
renovate
Bot
force-pushed
the
renovate/mastra-core-1.x
branch
6 times, most recently
from
May 23, 2026 00:33
8819cf7 to
ce69b78
Compare
renovate
Bot
force-pushed
the
renovate/mastra-core-1.x
branch
7 times, most recently
from
June 2, 2026 05:51
1c3a41f to
f14940c
Compare
renovate
Bot
force-pushed
the
renovate/mastra-core-1.x
branch
5 times, most recently
from
June 12, 2026 23:30
0d1eb5f to
00dbc1f
Compare
renovate
Bot
force-pushed
the
renovate/mastra-core-1.x
branch
2 times, most recently
from
June 20, 2026 08:38
d53662f to
734056f
Compare
renovate
Bot
force-pushed
the
renovate/mastra-core-1.x
branch
2 times, most recently
from
June 27, 2026 06:35
f8bb9dd to
9002d5d
Compare
renovate
Bot
force-pushed
the
renovate/mastra-core-1.x
branch
4 times, most recently
from
August 2, 2026 04:48
f08ce8a to
dd69ca4
Compare
renovate
Bot
force-pushed
the
renovate/mastra-core-1.x
branch
4 times, most recently
from
August 14, 2026 22:57
debf13c to
3bd730d
Compare
renovate
Bot
force-pushed
the
renovate/mastra-core-1.x
branch
3 times, most recently
from
August 22, 2026 05:16
2dd4033 to
f93fd4f
Compare
renovate
Bot
force-pushed
the
renovate/mastra-core-1.x
branch
4 times, most recently
from
August 30, 2026 02:50
1fdaecc to
f5f5c3a
Compare
renovate
Bot
force-pushed
the
renovate/mastra-core-1.x
branch
5 times, most recently
from
September 6, 2026 05:51
c26bc51 to
47c8779
Compare
renovate
Bot
force-pushed
the
renovate/mastra-core-1.x
branch
5 times, most recently
from
September 13, 2026 18:00
e39294d to
751c543
Compare
renovate
Bot
force-pushed
the
renovate/mastra-core-1.x
branch
2 times, most recently
from
September 18, 2026 21:56
62106e0 to
d1db72a
Compare
renovate
Bot
force-pushed
the
renovate/mastra-core-1.x
branch
2 times, most recently
from
September 25, 2026 18:28
568e8dc to
141978e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note
This PR body was truncated due to platform limits.
This PR contains the following updates:
^0.8.0→^0.8.0 || ^1.0.00.8.3→1.82.0Release Notes
mastra-ai/mastra (@mastra/core)
v1.82.0Compare Source
Minor Changes
An agent's
scopesfunction can now narrow or replace a run's scopes. It receives the run's incoming scopes (from the call,defaultOptionsand request context) and returns the run's final set. A staticscopeslist is still added to the incoming scopes. The function must keep anyresource:orthread:scope it was given; dropping one throws. (#28353)Added
requestId,serviceName, andexperimentIdfilters to advanced trace queries. Use them at trace scope or insidespans.some/spans.nonewitheq,ne,in,notIn,exists, andnotExists. (#29194)Added
onDecisiontoClassifierProcessorfor writing guardrail decisions to your own audit log (#28985). It runs after each classification, including failed classifier calls, and receives the outcome (allowed,filteredoraborted), the answers or the error, the call duration, and the ids of the message, thread, resource or run. (#29070)abortinonResultnow accepts tripwire metadata, so the caller can tell which policy blocked the request:A failed classifier call under
errorStrategy: 'strict'now sets tripwire metadata{ processorId, classifierId, code: 'CLASSIFIER_FAILED' }. On agents that aren't durable, stopping a streaming reply now cancels a classifier call that is still running.Fixed evented durable agents stopping when the API process shuts down. Workers now keep running the agent, finish cleanup, and free the thread, so the next message starts right away. (#28951)
Each run keeps its own ownership. Nested durable agents and runs that share a
RequestContextno longer pick up each other's ownership. Canceling a suspended run still notifies listeners, and a late cancel can no longer stop a newer run of the same agent.This needs storage that supports run fencing. Other shared setups keep their current behavior and log a warning once per agent: API shutdown can still stop remote workers. Single-process setups are unchanged.
To enable worker continuity across API loss, use a shared fencing-capable adapter, such as
PostgresStorefrom@mastra/pg, and a shared pubsub transport:Added
getStepResultAsyncto workflow step, condition, sleep, and.map()contexts, and deprecatedgetStepResult. (#26999)getStepResultAsyncreturns the same value asgetStepResult, wrapped in a promise. Switching now prepares workflows for upcoming storage changes that load step results on demand instead of keeping every result in memory.getStepResultkeeps working and will be removed in a future major release.If your tests build step execute params by hand, add a
getStepResultAsyncfunction to them.Changes to an experimental, upcoming memory feature. (#28353)
experimental_subconsciousand the Memoryknowledgeoption are removed: setting either now throws, saying to attach Knowledge to the agent instead (knowledge: falseis still accepted).SubconsciousandKnowledgeSemanticIndexCoordinatorstay exported from@mastra/memoryso older code still loads, and throw when constructed.@mastra/core/knowledgeexportsKnowledgewith its option and result types and the Knowledge storage domain; the tool, pin and semantic-index helpers are internal.Agent
scopesare plain"<type>:<value>"strings that say who the run is; what the run may read or write comes from whoever consumes them, such as Knowledge's scope declarations.A message can carry its own scopes:
session.sendMessage({ content, scopes }). A run's scopes are fixed when it starts, so a message whose scopes differ from the running run's waits for it to end, then starts its own run in the same conversation.sendSignalreports this as{ action: 'blocked', reason: 'scope-mismatch', runId }.queueMessage,queueSignal,sendNotificationSignalandsteertakescopesthe same way: a queued or steering message, or a notification, whose scopes differ from the running run's starts its own run with its scopes after that run instead of joining it. A deferred notification keeps the scopes it was sent with. Scopes are set in process only: remote callers (the agent controller HTTP routes and@mastra/client-js) can't send them. To derive scopes from a request, give the agent ascopesfunction that reads trusted request context. When the thread's claimed owner runs in another process, a message with scopes isn't queued:sendMessagerejects with an error saying scopes don't cross processes. Messages without scopes still join that owner's run.Attach Knowledge with
new Agent({ knowledge, scopes }). Knowledge needsscopes, the scope types its runs may hold and who gets which role on them:new Knowledge({ storage, scopes: { ...knowledgeMemoryScopes() } })declares Memory'sthread:/resource:scopes and anorg:scope;{}declares none. A run holding a scope that isn't declared fails. Tools are off by default:tools: truegives the read and write tools,tools: { read: true }the read tools only, andpinnedContext: true(or{ maxPins, maxTokens }) delivers pinned pages every turn and addsknowledge_pin.knowledge.readScopeFor(principals)returns what holders of those scopes can read, for host code reading the store directly.knowledge.explainAccess(runScopes, { scope } | { page })says why a run can or can't reach a scope or page; it's host-only, so never expose it through an agent tool or an unauthenticated route.knowledge.onChange(listener)reports each committed write made throughknowledge.getStorage()(tools included) by id, for live UIs; re-read each page under the viewer's read scope before showing it. A scope declaration can carry adisplayLabelfor UIs, andknowledge.scopeDeclaration(key)returns a declaration's display fields.Knowledge is a tree of pages. The tools are five:
knowledge_search,knowledge_read(a page with its notes, or a scope's pages),knowledge_page(op: 'write' | 'move' | 'merge'),knowledge_note(op: 'add' | 'restamp' | 'remove') andknowledge_pin(op: 'pin' | 'unpin'). Notes are at most about 250 tokens and page descriptions about 100; long text goes in a page body.Search is keyword search by default. Give Knowledge a
vectorstore and anembedderandknowledge_searchblends keyword and semantic results; pages and notes written through the tools are embedded as they're written. Every semantic hit is re-checked against what the run can read before it's ranked, so hidden content never fills result slots. Content written straight to the store isn't embedded.Added
knowledge_list, a Knowledge read tool that lists the page tree under a starting point, so an agent can see how a scope or section is organized before searching. Each entry has the refknowledge_readaccepts, its name and child count; passdescriptions: trueto add page descriptions. It goes up to 3 levels deep, takes alimitand acursorfor wide levels, and says when deeper levels were cut off. A result also stops at the same token cap as aknowledge_readbody;moresays how many entries were left out and gives thecursorthat continues exactly where it stopped, at any depth. Scopes the run can't read are left out.tools: { read: true }includes it. (#29207)Knowledge storage adapters gained
countChildPages({ parentIds, read }), which counts the readable child pages under each parent in one query.Agents can pin a listing of the Knowledge page tree with
knowledge_pin. Passkind: 'list'and an optionaldepth(1 to 3, default 1) to pin the pages under a page instead of the page itself: (#29263){ "op": "pin", "kind": "list", "depth": 2, "page": "docs:acme#guides", "audience": ["org:acme"] }A page can hold a page pin and a listing pin at once; each is pinned and unpinned on its own, and pinning a listing again sets its new depth. Stored listings follow the same rules as config listings: only pages the run can read, one pin slot, shared
pinnedContext.maxTokens. A stored listing that repeats a config listing is shown once.Knowledge storage adapters store a pin
kind(pageorlist) and a list pin'sdepth. Adapters report this with thesupportsPinKindscapability. With a storage adapter from before this release, page pins keep working and pinning a listing is refused before anything is stored, with a message to upgrade the adapter.Pin a listing of the Knowledge page tree into an agent's context. A scope's
pinnedContextnow also takes{ list, depth? }entries, so every run holding the scope sees the pages under a scope or page (ref, name and child count each) without callingknowledge_list: (#29263)A listing shows only pages the run can read, updates when a listed page is added, renamed or removed, takes one pin slot and shares
pinnedContext.maxTokenswith pinned pages. A listing cut short ends with aknowledge_listpointer to the rest.knowledge_readreads long pages by line. A body that fits in one read (1,000 tokens) comes back whole asbody. A longer one comes back as numberedlines(12│text) with therangethey cover and the page'slineCount, ending at the last whole line that fits, andmoresays where to read on withfrom: { line }. A line too long for one read, such as a wide table row, is cut by column (from: { line, col }). An agent can also ask for the body'soutline(headings with their line numbers and sizes), read onesection, ormatchone or more terms like grep: only the lines that contain them, withcontextlines before and after (default 2). (#29207)Every Knowledge read result now lists anything it cut short in
more, with how to get the rest: the body, notes, child pages (continue withknowledge_list), notes that mention the page (newmentionedInCursor), and search results at their limit.A whole-page read keeps a page's notes, and the notes on other pages that mention it, only while each list stays under 1,000 tokens. A longer list is left out and counted in
more; read it on its own withnotes: trueormentionedIn: true. A read withcursorormentionedInCursornow returns only those notes, without repeating the body.Every part of a body a read returns comes with its
range.knowledge_pagetakes arangewithexpectedVersionand replaces only those lines withbody; an emptybodyremoves them. A range may be the one a read returned or a smaller one inside it, but not wider than one read from its first line shows, so text the writer never saw can't be overwritten. A body write without a range is refused when the stored body is longer than one read returns, and so is a body that still carries the read's line numbers.The per-result cap is configurable:
tools: { read: { maxTokens: 3000 }, write: true }sets how many estimated tokens oneknowledge_readbody window or match result, and oneknowledge_listresult, returns (default 1,000, at least 100). The write guard for line ranges follows the same cap.Added vector and hybrid workspace search for vector stores that generate embeddings themselves, so no
embedderis needed. (#27022)With MongoDB Automated Embedding, MongoDB embeds each file when it is indexed and the search string when you query:
The index is named
<workspace id>_search_selfembedunless you setsearchIndexName. Workspaces that pass anembedderkeep working unchanged.Added opt-in Editor ownership and versioned persistence for static model fallback lists. (#27287)
Added time to first token and time to first chunk measures to trace and span aggregates, so dashboards can chart streaming latency next to duration. (#29055)
The new measures are
timeToFirstToken.avg,.min,.max,.p50,.p90,.p95,.p99, and.count, plus the same set fortimeToFirstChunk. Traces and spans without a recorded value are skipped instead of counted as 0, and.countreports how many had one. Stores opt in through theaggregate-time-to-first-measuresfeature.Suspended background tool calls can now be answered after a reload. (#28998)
Open request saved on the tool call. While a deferred background tool is suspended, the assistant message that holds the call keeps the open request in
metadata.suspendedTools[toolCallId](withsuspendPayload,taskId, andsuspendedAt). The entry is removed when the task resumes or is cancelled, so a chat that reloads the thread shows only requests that are still open.Resumed tools see what they asked for. A resumed background tool now receives the payload it suspended with in
context.agent.suspendPayload, the same as a foreground tool:Resume only the suspension you read.
BackgroundTaskManager.resume()takes an optionalsuspendedAt, and refuses the resume when the task has resumed or suspended again since:The agent reports a deferred task's result after its stream ends. When a deferred background task finishes and no
untilIdlestream is open for its thread, the agent now gets abackground-tasknotification signal on the thread. An idle thread wakes and the agent reports the outcome; a run already active on the thread gets the signal on its next model request. Before, the result was only written to memory and waited for the next user message. This coversstream()calls withoutuntilIdleand tasks resumed after they suspended, for example an MCP task answered from the chat. Streams usinguntilIdleare unchanged: they still run their own follow-up turn, and no signal is sent. Cancelling or aborting anuntilIdlestream still means no reply for the tasks it was waiting for.Opt in to ending woken runs after their turn. A run that a signal starts on an idle thread still stays open until the background tasks it dispatched finish. Set the temporary
wokenRunsEndAfterTurnoption to end it after its own turn instead; each task then wakes the thread when it finishes. A signal's ownifIdle.streamOptions.untilIdletakes precedence either way.In the next major release, ending after the turn becomes the only behavior and
wokenRunsEndAfterTurnis removed. PassuntilIdle: trueinifIdle.streamOptionsto keep a woken run open; channels and agent controller sessions already do.Turn off the completion signal. Set
notifyAgentOnFinish: falsein thebackgroundTasksconfig when your app sends its own completion signal, for example fromonTaskComplete. WithwokenRunsEndAfterTurnalso on, your app must report the tasks of woken runs itself.Added
knowledge.upsertPage(),knowledge.removePage(),knowledge.addNote(),knowledge.updateNote()andknowledge.removeNote()so apps can sync outside content, such as docs, into Knowledge from server code. A page is identified by its scope and key path, so running a sync again updates the same pages and skips unchanged ones. Notes on synced pages are kept, pages are embedded when Knowledge has a vector store, and change listeners are told. (#29159)Fixed a recovered thread run losing its thread lease when the execution it replaced finished. Previously, when a run was recovered in another process, the original execution's exit released the shared lease while the recovered execution was still running, so the thread looked free and a new run could start on it. Each execution now holds the lease under its own generation, so only the live execution can renew or release it. (#28947)
Also fixed run IDs that contain the internal handover marker, or look like an encoded lease owner, being misread as a different run. Any caller-provided run ID now round-trips unchanged.
Upgrading shared deployments
Drain active thread runs and restart API and worker processes together. Older processes do not recognize the new lease owner format: they cannot see a run as live, even while it is renewing its lease. Follow-ups that an older process forwards to a run on this release are still delivered.
Patch Changes
dependencies updates: (#29242)
@ai-sdk/provider-utils-v6@npm:@ai-sdk/provider-utils@4.0.63↗︎ (fromnpm:@ai-sdk/provider-utils@4.0.62, independencies)@ai-sdk/provider-utils-v7@npm:@ai-sdk/provider-utils@5.0.72↗︎ (fromnpm:@ai-sdk/provider-utils@5.0.69, independencies)@ai-sdk/provider-v7@npm:@ai-sdk/provider@4.0.35↗︎ (fromnpm:@ai-sdk/provider@4.0.34, independencies)Moved the agent stream layer (stream topics, event publishing, replay cache setup and abort requests) out of
DurableAgentinto a shared internal module. No behavior change; existing@mastra/core/agent/durableexports are unchanged. (#28416)Fixed resuming suspended agent runs across server instances when a process retains an earlier run segment. (#29065)
Update provider registry and model documentation with latest models and providers (
4d3ebbd)Fixed thread aborts for durable agents on multi-server deployments. Aborting a thread on a server that didn't start the run now stops the run on whichever server executes it, as aborting by run id already did. A run parked on a suspension, such as a tool waiting for approval, is released too. (#28345)
The server finds the run through the thread lease, so this needs a pubsub with a shared lease backend such as Redis or Valkey.
Return values
{ aborted: true }once the abort request is published, and an error if the lease backend can't be reached or doesn't answer in time.abortThreadStreamstill returnsfalsefor a run on another server, because it can't wait for the lookup.expectedRunIdthat doesn't match the running run leaves it alone and returnsfalse.Queued input
With
clearPendingSignals: true, the server running the run clears its queued input before stopping it, as long as that run still holds the thread. Input queued for a run that has since taken over the thread is kept.Updated
chatand the Chat SDK adapters to ^4.44.0, so all Mastra channel packages resolve the samechatversion. (#29285)Fixed "Tenant model credential policy could not be resolved: the request has no authenticated user" errors that recurred on every message in a session with a pending tool approval. When a new user message declines a pending approval, the decline now runs with the sender's request context instead of the reattached run's empty one. (#29231)
Fixed durable agents (
durable: true) ending themodel_generationtrace span withoutresponseIdandresponseModel. Durable runs now record the provider's response id and model on the span, the same as non-durable agents, so a trace can be linked back to the provider's own response log. Fixes #29053. (#29180)Improved
validateStepRequestContexttyping. It now accepts any object withidandrequestContextSchema, not only a fullStep. (#29080)Fixed skills not loading on Windows when the workspace
basePathuses a short folder name such asC:\Users\ABCDEF~1\..., whichos.tmpdir()can return. Optional skill folders that don't exist, likereferences, no longer fail withPermission denied. Fixes #28957. (#29078)Fixed a signal sent to a thread while an
agent.generate()turn was running starting a second run that could not see that turn.generate()now registers its run with the thread likestream()does, sosendSignal,sendMessageand notifications with the defaultifActive: 'deliver'join the running turn. (#28953)As with
stream(), a thread-boundgenerate()now waits for a run already going on the same thread, so concurrentgenerate()calls on one thread run one after another instead of in parallel. A tool that callsgenerate()orstream()on its own thread waits for the run it is part of, so it deadlocks; a tool should usesendSignalto send a message to its own thread.Fixed metadata filters in the in-memory store returning records whose nested metadata had different keys from the filter when one side contained an
undefinedvalue. (#29101)Improved how Knowledge reads fenced code and headings in page bodies, to match CommonMark: (#29260)
block can show```js ```` lines without breaking a page's sections, outline or embedding chunks. A fence indented four or more spaces is indented code and doesn't open or close a block.#that's part of its title:## Use C#is the section "Use C#".knowledge_pageaccepts back for an edit.Embedding a long page now runs up to 4 chunk embeddings at once (one at a time when the embedding model doesn't support parallel calls), and one semantic search looks at no more than 1,000 distinct pages and notes.
Knowledge notes returned by
knowledge_note,knowledge_readandknowledge_searchnow carrymentions, mapping each[[ref]]in the note to the page id it resolves to, so a chat can link them. Refs are resolved the way the note's write resolved them; a name that no longer resolves is left out. (#28942){ "id": "n1", "text": "Escalate per [[Escalation Process]].", "mentions": { "Escalation Process": "page-esc" } }knowledge_listnow asks the store for only the child scopes the agent can read, and counts them in one call per level, instead of reading every child scope and dropping the unreadable ones. Listing under a scope with many private child scopes no longer loads all of them. (#29260)Knowledge pages too long for the embedder are now searchable by meaning. A long page is embedded as overlapping chunks of about 2,000 tokens, cut between paragraphs and before headings, and semantic search still returns the page once, ranked by its best chunk. Before, the embedder refused these pages and they could only be found by keyword. Rewriting a page replaces its chunks, and deleting it removes them all. Pages embedded before this change keep working; a page that was too long is chunked the next time it changes. (#29218)
A page is embedded as at most 100 chunks (about 200,000 tokens of body); past that, the rest of the body is found by keyword only. A page's name and description lead every chunk, up to 500 tokens. One semantic search looks at up to 1,000 distinct pages and notes (up to 10,000 vectors). A rewrite drops the page's old chunks by id after the new ones are stored; on a vector store that can't delete by id, old chunks stay but only ever point back to the same page.
Knowledge page and note writes now retry a failed embed or vector write a few times over about 10 seconds before giving up. That gives a cold scale-to-zero database (Neon, Lakebase) time to wake, so its pages aren't silently left out of semantic search. If every try fails, the write still succeeds and the warning names the page, for example
Knowledge couldn't embed page "docs:acme#guides/cli" after retrying; it is findable by keyword search only until it is rewritten. Errors the embedding provider marks as not retryable, such as an input over its limit, are not retried. (#29288)Added read-only page-store readers to
@mastra/core/knowledgefor browsing UIs:listKnowledgeTreeChildren(one level of the page tree, child scopes first),readKnowledgePage(a page with its readable ancestors, notes, pin, referenced pages and notes that mention it),readKnowledgePageGraph(a bounded page graph with parent and mention edges, limits inDEFAULT_KNOWLEDGE_PAGE_GRAPH_LIMITS; mentions come from every note on a walked page until a graph-widemaxNotesbudget runs out, which setstruncated),searchKnowledgeExplorerPagesandreadKnowledgePageActivity. Each takes a read scope ('all'for an owner view) and passes it to every store call, so pages and notes outside it never appear, mention edges included. They never write. AddedreadKnowledgeScopeGrants, which lists the declared grants onto the scope pages in a graph (principal scope, its page id when it has one, target and role), keeping only principals the read scope can read, andknowledge.scopeGrants(scope), which gives each principal the declared rules name on one scope with its strongest role. (#28362)Knowledge tool results now name each page by the ref
knowledge_readaccepts. Search, read and page results carryref: the scope address for a scope page, the full key path for a nested page (docs:acme#guides/observability/logging), or the page id when a page on the path has no key. The oldkeyfield held only the last path segment, so agents that copied it got "not found" for nested pages; it is gone. A read that misses now lists up to three close matches by ref, and an ambiguous page name lists its candidates by ref. (#29207)Fixed pinned Knowledge listings stopping at 100 pages per level. A listing now reads every child of a page while its token budget has room, and only ends with "…and more" when the budget runs out. (#29298)
GET /knowledge/:knowledgeKey/pages/:pageIdalso opens a page by the reference tools name it with: a scope address (site:mastra) orscope#key/path(site:mastra#topics/observability). It reads only what the caller can read, and an unreadable or unknown reference still returns 404. (#29215)Sped up
readKnowledgePageGraph, which loads the Studio Knowledge graph, on large page trees. It now reads the tree a level at a time: one child-page count for the whole level, a listing only for pages that have children, one note count across the walked pages, and one lookup for linked pages outside the walk. A 300-page tree went from 1,221 sequential storage calls to 37, which matters most on remote databases such as Turso. The returned graph is unchanged. (#29288)Added an index of the
[[links]]in Knowledge page bodies, so a host can count and list which pages link where without loading every page.countPageLinksandlistPageLinkson the Knowledge store return in and out links for a set of pages, gated by the reader's scopes like every other read. Note[[mentions]]show up there asnotelinks. (#29260)How links resolve
upsertPageorremovePage.scope#key/keypath, scope address or page name: the page's own scope first, then the whole store.knowledge.reindexLinks()runs.Pages written before this release, or straight to the store, aren't indexed until they are written again. Run
knowledge.reindexLinks()once to index them all; it needs no vector store.Added
readKnowledgePageLinkCountsandreadKnowledgePageLinksto@mastra/core/knowledge: exact link counts for any set of pages and one page's links with the pages at their other ends, read from the store's link index and filtered by the caller's read scope. Both returnnullwhen the store has no link index. (#29260)Knowledge page paths ignore leading, trailing and doubled slashes, so
docs:acme#models/gateways/names the same page asdocs:acme#models/gateways. This holds forupsertPage,removePage, the note methods,[[links]]in notes, page refs given to the Knowledge tools, andpinnedContextrefs. PreviouslyupsertPagerefused such paths and reads reported the page as not found. (#29207)pinnedContext.maxTokensno longer has a 2,000-token ceiling. It budgets pinned pages only, so set it below your model's context limit to leave room for instructions, conversation history and model output, for examplepinnedContext: { maxTokens: 8000 }. The default is still 500. (#29257)Fill pinned Knowledge listings level by level within their token budget. A listing with
depth2 or 3 now shows every child that fits first, then grandchildren, and so on, instead of stopping at a fixed 50 entries in sibling order. Each entry still prints under its parent, and a listing that is cut still ends with…and more; knowledge_list from: "<ref>". (#29263)The
knowledge_readtool now tells the model that a read from a line runs until the page ends or the read's token cap is reached, and thatlimitcaps notes, children and mentioning notes, not lines. Models had been passinglimitexpecting a set number of lines back. (#29258)Removed
knowledge.reindex(). It re-embedded every page and note in the store, which is slow and costly on a large store. Pages and notes are embedded when they're written through the Knowledge tools,upsertPageor the note methods. If an embed fails, the write still succeeds and the content stays findable by keyword search; it's embedded again the next time it changes.upsertPageskips writes whose content is unchanged, so sending identical content again doesn't retry a failed embed; the Knowledge tools andupdateNoteembed on every write. (#29256)If you called
reindex()after loading content, delete the call and load the content throughupsertPageinstead, which embeds each new or changed page as it's written. There is no bulk re-embed for content that's already stored:Fixed read-only Knowledge runs filling storage with empty scopes. A run that only searches, lists or reads Knowledge, and pinned-context delivery each turn, no longer creates the run's
resource:,thread:or other held scopes. A held scope is created, with its template pages and itsbelongsToplacement, the first time a run writes Knowledge (knowledge_page,knowledge_note,knowledge_pin). (#29288)Before, a Slack agent that only answered questions with
knowledge_searchcreated an emptyresource:slack:<userId>andthread:scope for every user and conversation, which filled the Studio tree.A scope with no pages yet reads as not found until the first write creates it.
knowledge_searchnow finds pages that match any word of the query, not only pages that match every word. Pages with more of the query's words rank higher, so a long question still finds the page that holds most of its words. A page whose name contains the whole query still comes first. When several pages match the same number of words, a word in the name counts more than one in the description, and one in the description more than one in the body. A search uses the first 16 distinct words of the query; repeated words count once. (#29207)knowledge_searchhandles queries agents commonly write. Quoted words ("api" "responses") are searched as plain words; before, the quote marks were matched literally and nothing was found. A search that finds nothing now says so and suggests one to three key words orknowledge_list. Passing a page ref such asdocs:acme#modelsasscopenow explains thatscopetakes scope addresses, instead of reporting the page as not found. (#29207)A first
knowledge_readof a long page that stops early now also returns theoutlineof the headings it did not reach (up to about 500 tokens of them, with their line numbers and sizes), so an agent can read the section it needs next instead of paging toward it. Search results at their limit no longer tell the agent to search again; the default Knowledge instructions now say to followmoreonly when what the agent needs is not already in what it has read, and to answer once it has it.The first
knowledge_readof a page too long to return whole now shows the whole page at a glance: every heading with its line number and the first lines under it, with⋯ lines a–bwhere lines are left out, within the read cap. Read the part you need withsectionorfrom. A page with more headings than fit falls back to its top lines and the headings below them.Knowledge tree child counts now say when they were capped. If a scope has more child scopes than the 1,000 the tree looks through for each level, the page gets
childCountCapped: trueand itschildCountis a lower bound. The Knowledge explorer then shows the count as "1000+" in the tree and in the graph's "pages under" line, instead of a number that's too low. (#29216)Each page in a Knowledge tree level (
GET /api/knowledge/:knowledgeKey/tree,listKnowledgeTreeChildren) now carrieschildCount: how many readable pages, and for a scope how many readable child scopes, sit directly under it. A browsing UI can show the count and offer to open only pages that have children. The count is left out when the storage adapter can't count children yet. (#29216)Storage adapters gain
countChildPages({ parentIds, read }), which counts a batch of parents' child pages in one query (in-memory, LibSQL, PostgreSQL, MySQL, MongoDB).Stored spans now use the service name from the Observability config when the span metadata does not set one. (#29201)
Fixed
getStepResultandgetStepResultAsyncreturningnullfor the loop body inside.dowhile()and.dountil()conditions on the evented workflow engine. Loop conditions now see the body's latest result on every iteration, matching the default engine. (#26999)Fixed approving a tool loaded by
ToolSearchProcessorafter a server restart. The approved call failed withTool "<name>" not foundwhen memory no longer returned the search result. It now runs with the tools it had when the model made the call. Fixes #29196. (#29251)Fixed
run.cancel()leaving suspended nested workflow runs suspended after the parent was canceled. Nested runs, including foreach iterations, are now canceled with their parent on both the default and evented engines. Callingcancel()again on a run that is already canceled also cancels nested runs it left suspended. If storage fails while canceling nested runs,cancel()rejects and can be retried. (#24780) (#29170)Fixed
Property '#private' is missingwhen passing a voice provider such asnew OpenAIRealtimeVoice()to an agent'svoiceoption or toCompositeVoice(#29016). (#29074)These options now accept the
IMastraVoiceinterface, exported from@mastra/core/voice.agent.voiceandagent.getVoice()returnIMastraVoice. If you annotate a variable with a voice type, useIMastraVoiceinstead ofMastraVoice.Fixed signal-started runs to use the
ifIdle.streamOptions.runIdyou provide. This applies whensendSignal,sendMessage,queueSignalorqueueMessagewakes an idle thread or queues a run behind the active one. The id appears inaccepted.runId,output.runIdand thread events. If the id already names a known run,acceptedrejects. Calls without an id still get a random one. (#29152)Fixed progress from tools wrapped with
createStepfrom@mastra/core/workflows/eventednever reaching the workflow stream. The tool now receives the stepwriter, the runactor(including theactorstep option) andsuspendPayload, the same as withcreateStepfrom@mastra/core/workflows. Fixes #29164. (#29165)Fixed controller tool status for validation failures and explicit error results, including delegated tool results. Successful results containing business error fields retain their successful status. (#28812)
Fixed directly resumed nested workflows in the evented engine so they continue suspended parent runs. (#25979)
Fixed type declarations for bundled AI SDK packages. When
@mastra/corebundled two versions of the same AI SDK package, one version's types overwrote the other's, so types such asLanguageModelV3used by the bundled OpenAI, Google and Anthropic providers resolved toany. Each version now keeps its own copy, so these types are checked again. Fixes #28732. (#29171)Fixed retained workflow handles and abort listeners after durable agent runs settle (#29009) (#29065)
Fixed dataset experiments that target a workflow so their traces carry the experiment ID. Workflow runs and every auto-resume now include
experimentIdin tracing metadata, so Studio's experiment page shows total cost, tokens and run duration for workflow experiments, the same as it does for agent experiments. (#29245)[
v1.81.0](https://redirect.Configuration
📅 Schedule: (in timezone America/Los_Angeles)
* * * * 1-5)🚦 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 these updates again.
This PR was generated by Mend Renovate. View the repository job log.