Repository navigation
Conversation
…ol#2874) @ag-ui/mastra pinned a nested zod@^3.25.76 solely through its @ai-sdk/ui-utils dependency, forcing a second Zod 3 copy into apps on Zod 4 (the ERESOLVE/duplicate-zod problem in ag-ui-protocol#2874). The only use of @ai-sdk/ui-utils was a single parsePartialJson import. Replace it with parsePartialJson from the current AI SDK ('ai'), which is zod-4-compatible (peers zod ^3.25.76 || ^4.1.8). That function is async, so the chunk pipeline it feeds (parseStreamingWorkingMemoryUpdate -> processChunk -> releaseDeferredReplay -> handleChunk -> flush) is made async and awaited at its call sites. Awaiting the chunk pipeline leaves a remote producer suspended in 'await onChunk(...)' when a run is cancelled, so the abort listener's synchronous subscriber.complete() tore the run down before the producer re-observed the aborted signal and stopped upstream (ag-ui-protocol#2288). Defer that completion by one macrotask so the producer yields and stops first. - drop @ai-sdk/ui-utils, add ai - widen zod to ^3.25.76 || ^4.0.0 (keeps the existing 3.x floor, adds Zod 4; matches @mastra/core and @mastra/client-js) typecheck + 522 tests + build pass locally, including the ag-ui-protocol#2288 remote-cancellation suite.
sander-su
force-pushed
the
fix/mastra-zod4
branch
from
October 9, 2026 12:52
a6f1ca3 to
93a5b8d
Compare
This was referenced Oct 18, 2026
Resolve conflicts with main's AG-UI 1.0 adapter (@ag-ui/mastra 2.0.1). - Re-apply the async parsePartialJson chain to main's mastra.ts, including the new `return handleChunk(...)` inside processChunk and the external flush() call sites. - Pin ai to ^5.0.207, the first 5.x release that pins @ai-sdk/provider-utils 3.0.28, outside GHSA-866g-f22w-33x8 / CVE-2026-8769 (ag-ui-protocol#3096). Resolves to ai 5.0.278 / provider-utils 3.0.41. - Rework the ag-ui-protocol#2288 cancellation ordering for main's new abort listener: the cancelled RUN_FINISHED is still emitted synchronously, and only subscriber.complete() is deferred one macrotask. Deferring the whole block (the previous approach) let the drained stream end the run first and lost the cancelled outcome; deferring nothing settled the run before the remote producer saw its aborted signal. typecheck clean, 35 files / 687 tests pass; cancellation, state-snapshot and working-memory suites pass 10/10 repeated runs.
sander-su
added a commit
to sander-su/ag-ui
that referenced
this pull request
Oct 27, 2026
…-protocol#3096, ag-ui-protocol#2874) @ag-ui/mastra used one function from @ai-sdk/ui-utils, parsePartialJson, to read working memory out of streaming tool-call arguments. ui-utils is the AI SDK 4 package: its last release (1.2.11) pins @ai-sdk/provider-utils@2.2.8, affected by GHSA-866g-f22w-33x8 / CVE-2026-8769 with no patched 2.x, and peers zod ^3.23.8, which forces a nested Zod 3 copy into apps on Zod 4. Fix it the way Mastra fixed the same dependency in @mastra/client-js (mastra-ai/mastra#27297), using the same layout: src/utils/ai-sdk-v4/{fix-json,parse-partial-json,types,index}.ts, taken from Mastra's copy at c56d026c5194b1977e5321f429565eedbccf2c40 (Apache-2.0, attribution kept). The code is identical to Mastra's apart from prettier formatting; types.ts and index.ts keep only what this package uses (JSONValue, parsePartialJson). parsePartialJson parses with plain JSON.parse instead of provider-utils' safeParseJSON, so no AI SDK package is needed. The function stays synchronous, so parseStreamingWorkingMemoryUpdate and the chunk pipeline are unchanged; nothing in streaming or cancellation moves. This replaces the async `ai` approach of ag-ui-protocol#3007. - remove @ai-sdk/ui-utils from dependencies - widen zod to ^3.25.76 || ^4.0.0 - add src/utils/ai-sdk-v4/ai-sdk-v4.test.ts (Mastra's parse-state cases plus the working-memory envelope shapes) typecheck clean; 47 files / 891 tests pass; build and test:exports pass. A clean consumer install with @mastra/core 1.79.0 and @mastra/client-js 1.56.0 resolves no @ai-sdk/ui-utils or provider-utils 2.x.
sander-su
added a commit
to sander-su/ag-ui
that referenced
this pull request
Oct 27, 2026
…-protocol#3096, ag-ui-protocol#2874) @ag-ui/mastra used one function from @ai-sdk/ui-utils, parsePartialJson, to read working memory out of streaming tool-call arguments. ui-utils is the AI SDK 4 package: its last release (1.2.11) pins @ai-sdk/provider-utils@2.2.8, affected by GHSA-866g-f22w-33x8 / CVE-2026-8769 with no patched 2.x, and peers zod ^3.23.8, which forces a nested Zod 3 copy into apps on Zod 4. Fix it the way Mastra fixed the same dependency in @mastra/client-js (mastra-ai/mastra#27297), using the same layout: src/utils/ai-sdk-v4/{fix-json,parse-partial-json,types,index}.ts, taken from Mastra's copy at c56d026c5194b1977e5321f429565eedbccf2c40 (Apache-2.0, attribution kept). The code is identical to Mastra's apart from prettier formatting; types.ts and index.ts keep only what this package uses (JSONValue, parsePartialJson). parsePartialJson parses with plain JSON.parse instead of provider-utils' safeParseJSON, so no AI SDK package is needed. The function stays synchronous, so parseStreamingWorkingMemoryUpdate and the chunk pipeline are unchanged; nothing in streaming or cancellation moves. This replaces the async `ai` approach of ag-ui-protocol#3007. - remove @ai-sdk/ui-utils from dependencies - widen zod to ^3.25.76 || ^4.0.0 - add src/utils/ai-sdk-v4/ai-sdk-v4.test.ts (Mastra's parse-state cases plus the working-memory envelope shapes) typecheck clean; 47 files / 891 tests pass; build and test:exports pass. A clean consumer install with @mastra/core 1.79.0 and @mastra/client-js 1.56.0 resolves no @ai-sdk/ui-utils or provider-utils 2.x.
Contributor
Author
|
Superseded by #3224. Following the preference for a synchronous parser on #3096, and the way Mastra fixed the same dependency in mastra-ai/mastra#27297, #3224 vendors Mastra's sync Closing this one so the review can continue on #3224. |
1 task done
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.
Closes #2874 (follow-up to the discussion there).
Summary
@ag-ui/mastrapulled in a nestedzod@3copy only through@ai-sdk/ui-utils, which is an AI SDK v4-era package that peerszod ^3.23.8(every published version, including the latest 1.2.11). The adapter used exactly one symbol from it:parsePartialJson. Apps on Zod 4 therefore got a duplicate Zod 3 plus theERESOLVE overriding peer dependencywarning described in the issue.This PR:
@ai-sdk/ui-utilsparsePartialJsonfrom the current AI SDK (ai, which peerszod ^3.25.76 || ^4.1.8)zoddependency from^3.25.76to^3.25.76 || ^4.0.0(same 3.x floor, matching@mastra/core/@mastra/client-js)The sync → async change
The current AI SDK only ships an async
parsePartialJson(Promise<{ value, state }>, same shape otherwise). Its single caller,parseStreamingWorkingMemoryUpdate, runs inside the chunk pipeline, so that chain is now async and awaited at its call sites:parseStreamingWorkingMemoryUpdate→processChunk→releaseDeferredReplay(its.some()short-circuit becomes an awaitedfor…ofwith the same early return) →handleChunk→ theflushreturned bycreateChunkProcessor.No public API change. None of these functions are exported.
createChunkProcessorisprivate, the emitted.d.tscontains none of their signatures, andMastraAgent's public methods already returned Observables/Promises. Both drivers already awaited the callback (@mastra/client-js'sprocessDataStreamdoesawait onChunk(chunk); the local path is afor awaitloop), so chunk ordering is unchanged.Cancellation (#2288): why the abort listener now defers
subscriber.complete()Making the pipeline async surfaced an ordering issue in the #2288 remote-cancellation guarantee.
cancellation-remote-producer.test.ts > still cancels the run when the caller's controller abortsfailed: the producer had delivered 1 chunk and had not stopped (stoppedEarly === false), yet the run had already settled.Cause: when the run is aborted mid-stream the producer is suspended in
await onChunk(...). The abort listener calledsubscriber.complete()synchronously, so the run settled before the producer resumed and re-checked its aborted signal. With the previously sync pipeline the producer happened to win that race.Fix: the abort listener now settles the Observable on the next macrotask (
setTimeout(…, 0)). Pending microtasks, the producer's resumption included, run first, so the producer observes the abort and stops before the run settles. The reasoning is also documented inline in the listener.Trade-offs I'd like a maintainer's view on:
If you'd prefer an explicit guard (stop emitting once
abortSignal.aborted, plus an explicit producer-stopped handshake) over the timer, I'm happy to rework it that way.Testing
Run locally in
integrations/mastra/typescript:pnpm typecheck: cleanpnpm test: 31 files / 522 tests passed, including the fullcancellation-remote-producer,cancellation,state-deltaandworking-memory-syncsuitespnpm build: cleanpnpm install --frozen-lockfile --filter @ag-ui/mastraNot done: the full monorepo test matrix (only
@ag-ui/mastrais touched). The lockfile diff includes theai@5subtree and pnpm's re-resolution of zod-peering packages; happy to regenerate it with your registry setup if CI prefers.