Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughReplaces AI SDK v5 streaming/execution with AI SDK v6 ToolLoopAgent/streamSubAgent, renames addToolResult → addToolOutput, adds approval-related tool states and approval APIs, updates transports/types/components/hooks/examples/docs, and fixes toModelOutput parameter shapes. Changes
Sequence Diagram(s)sequenceDiagram
participant App as Application
participant Agent as ToolLoopAgent
participant UIStream as Agent UI Stream
participant Tools as Tools/Models
participant Approval as Approval Handler
participant Renderer as UI Renderer
App->>Agent: create(instructions, tools, temperature, stopWhen)
Agent->>Tools: invoke toolCall
Tools-->>Agent: return output / error
Agent->>UIStream: emit UI chunks (text-delta, tool-*-events)
UIStream-->>Renderer: stream chunks → update UI/tool state
alt approval requested
Agent->>UIStream: emit approval-requested (id)
UIStream-->>Renderer: show approval UI
Approval->>Agent: submit approval response (id, approved)
Agent->>UIStream: emit approval-responded
end
alt approval denied
Agent->>UIStream: emit output-denied
else success
Agent->>UIStream: emit final text-delta
end
Agent->>Agent: check stopWhen(stepCountIs(maxSteps))
Agent-->>App: return final accumulated text
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
# Conflicts: # examples/app-builder/package.json # packages/ai-core/src/AiSlice.ts # pnpm-lock.yaml
There was a problem hiding this comment.
Pull request overview
Upgrades the monorepo to AI SDK v6 ([email protected]) and migrates the chat/tool streaming architecture from streamText + client-orchestrated tool calls to ToolLoopAgent + agent-loop streaming, with additional schema/type updates to support v6 approval states.
Changes:
- Bumps
ai/@ai-sdk/reactdependencies to v6/v3 across packages, examples, and lockfile. - Reworks transports and agent utilities to use
ToolLoopAgent+createAgentUIStreamResponse/createAgentUIStream, and updates public exports/renames (processAgentStream→streamSubAgent,addToolResult→addToolOutput). - Extends UI message schemas/types and tool rendering to recognize v6 approval-related tool states (
approval-requested,approval-responded,output-denied), and updates docs/example routes accordingly.
Reviewed changes
Copilot reviewed 32 out of 33 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Locks upgraded AI SDK + related dependency graph changes. |
| packages/vega/src/VegaChartTool.tsx | Updates toModelOutput signature to v6 tool output wrapper shape. |
| packages/vega/package.json | Pins ai dependency to 6.0.132. |
| packages/mosaic/package.json | Reorders deps (no functional change). |
| packages/ai/src/tools/query/queryTool.ts | Updates toModelOutput signature to v6 wrapper shape. |
| packages/ai/src/index.ts | Updates public exports/types to new agent/tool APIs and renames. |
| packages/ai/package.json | Pins ai dependency to 6.0.132. |
| packages/ai-rag/src/createRagTool.tsx | Updates tool output mapping signature; removes unused output destructuring. |
| packages/ai-rag/package.json | Pins ai dependency to 6.0.132. |
| packages/ai-core/src/utils.ts | Adds shouldEndAnalysis; updates tool-call completion logic for approval states. |
| packages/ai-core/src/types.ts | Replaces AddToolResult with AddToolOutput and adds approval response typing; expands tool renderer states. |
| packages/ai-core/src/index.ts | Re-exports new utilities/types (streamSubAgent, updateAgentToolCallData, approval types, etc.). |
| packages/ai-core/src/hooks/useSessionChat.ts | Migrates to v6 useChat API surface (addToolOutput, addToolApprovalResponse) and new auto-send helper. |
| packages/ai-core/src/hooks/useAssistantMessageParts.ts | Adjusts message parts typing to align with updated schemas. |
| packages/ai-core/src/components/tools/ToolResult.tsx | Extends accepted tool states for approval flow. |
| packages/ai-core/src/components/ToolPartRenderer.tsx | Adds approval-state rendering path and passes approvalId into tool renderers. |
| packages/ai-core/src/components/ToolCallInfo.tsx | Extends state union to include approval states; simplifies props usage. |
| packages/ai-core/src/chatTransport.ts | Replaces streamText pipeline with ToolLoopAgent + createAgentUIStreamResponse; updates remote request body shape. |
| packages/ai-core/src/agents/AgentUtils.ts | Replaces processAgentStream with streamSubAgent using v6 agent UI stream. |
| packages/ai-core/src/AiSlice.ts | Removes client-side tool-call plumbing; adds per-session output/approval function registration; switches deep copy to structuredClone. |
| packages/ai-core/package.json | Upgrades ai and @ai-sdk/react dependency versions. |
| packages/ai-core/README.md | Updates docs to reference streamSubAgent instead of processAgentStream. |
| packages/ai-config/src/schema/UIMessageSchema.ts | Updates Zod schemas for AI SDK v6 UIMessage/tool states (incl. approval states). |
| packages/ai-config/src/index.ts | Exports DynamicToolUIPart type alongside existing UI message part types. |
| examples/app-builder/package.json | Pins ai dependency to 6.0.132. |
| examples/ai/package.json | Pins ai dependency to 6.0.132. |
| examples/ai-rag/package.json | Pins ai dependency to 6.0.132. |
| examples/ai-nextjs/src/app/api/chat/route.ts | Migrates Next.js route to ToolLoopAgent + createAgentUIStreamResponse and new request params. |
| examples/ai-nextjs/package.json | Pins ai dependency to 6.0.132. |
| examples/ai-core/package.json | Pins ai dependency to 6.0.132. |
| examples/ai-agent/src/agents/WeatherAgent.ts | Migrates sub-agent tool to ToolLoopAgent + streamSubAgent. |
| examples/ai-agent/package.json | Pins ai dependency to 6.0.132. |
| docs/upgrade-guide.md | Adds 0.29.0 migration guide covering ToolLoopAgent, addToolOutput, approval states, and remote transport changes. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/ai-core/src/AiSlice.ts (1)
600-610:⚠️ Potential issue | 🟡 MinorMissing cleanup for
sessionAddToolApprovalResponsesindeleteSession.The
deleteSessionmethod cleans upsessionAddToolOutputsbut doesn't clean up the newsessionAddToolApprovalResponsesMap, which could cause a memory leak for approval response handlers.🛠️ Proposed fix
sessionChatStops.delete(sessionId); sessionChatSendMessages.delete(sessionId); sessionAddToolOutputs.delete(sessionId); + sessionAddToolApprovalResponses.delete(sessionId); const now = Date.now();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/ai-core/src/AiSlice.ts` around lines 600 - 610, deleteSession currently clears several per-session maps but misses removing entries from sessionAddToolApprovalResponses, causing potential memory leaks; update the deleteSession method to also call sessionAddToolApprovalResponses.delete(sessionId) (or clear the map entry) alongside sessionAddToolOutputs.delete(sessionId) and the other sessionXXX.delete(sessionId) calls so all per-session handlers are cleaned up when deleteSession runs.
🧹 Nitpick comments (4)
packages/ai-core/src/components/ToolPartRenderer.tsx (1)
183-188: Remove unusedisCompletedprop.As noted in
ToolCallInfo.tsx, theisCompletedprop is no longer used. Consider removing it from this call site.♻️ Proposed fix
<ToolCallInfo toolName={toolName} input={input} - isCompleted={isCompleted} state={state} />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/ai-core/src/components/ToolPartRenderer.tsx` around lines 183 - 188, Remove the now-unused isCompleted prop from the ToolCallInfo invocation in ToolPartRenderer: update the JSX that renders <ToolCallInfo ... /> by deleting the isCompleted={isCompleted} prop so the component is called with only toolName, input, and state; verify ToolCallInfo props type (in ToolCallInfo.tsx) matches and run type checks to ensure no other references to isCompleted remain in ToolPartRenderer.examples/ai-nextjs/src/app/api/chat/route.ts (1)
11-18: Consider validating request payload.The route destructures the request body without validation. While the defaults handle missing values, invalid types (e.g., non-number
maxSteps) could cause unexpected behavior. Consider adding Zod validation for the incoming payload.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/ai-nextjs/src/app/api/chat/route.ts` around lines 11 - 18, Add a Zod schema and validate the parsed body immediately after the await req.json() call in the API route handler in route.ts: define a schema for messages, modelProvider, model, instructions, maxSteps (number), temperature (number) with appropriate defaults/required flags, parse/parseAsync the incoming payload, and if validation fails return a 400 response with the error details; then use the validated/parsed values (instead of raw destructuring) so downstream logic (e.g., any function expecting numeric maxSteps or temperature) receives correct types.packages/ai-core/src/components/ToolCallInfo.tsx (1)
13-25: Remove unusedisCompletedprop from type definition.The
isCompletedprop is declared inToolCallInfoProps(line 16) but is not destructured or used in the component (lines 40-43). The completion state is now derived directly from thestateprop (line 51). WhileToolPartRenderer.tsxstill passesisCompleted={isCompleted}(line 186), this prop is ignored.♻️ Proposed fix to remove unused prop
type ToolCallInfoProps = { toolName: string; input: unknown; - isCompleted: boolean; state: | 'input-streaming' | 'input-available' | 'output-available' | 'output-error' | 'approval-requested' | 'approval-responded' | 'output-denied'; };Also update the JSDoc at lines 36-37 to remove the
@param props.isCompletedline, and remove the prop fromToolPartRenderer.tsxline 186.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/ai-core/src/components/ToolCallInfo.tsx` around lines 13 - 25, ToolCallInfoProps declares an unused isCompleted prop — remove the isCompleted field from the ToolCallInfoProps type declaration and update the ToolCallInfo component JSDoc to delete the `@param` props.isCompleted entry; then remove the isCompleted={isCompleted} prop passed from ToolPartRenderer (so ToolPartRenderer no longer forwards that unused prop). Ensure you only change the type, JSDoc, and the prop usage (references: ToolCallInfoProps, isCompleted, ToolPartRenderer) so the component derives completion from the existing state prop.packages/ai-core/src/agents/AgentUtils.ts (1)
73-114: Consider returning tool call state alongside final text.The
streamSubAgentfunction maintainstoolEditStateinternally but only returnsfinalText. If callers need visibility into tool call progress (e.g., for logging or debugging), consider returning both:return { text: finalText, toolCalls: toolEditState };This is optional since the current API may be intentionally minimal for the sub-agent use case.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/ai-core/src/agents/AgentUtils.ts` around lines 73 - 114, streamSubAgent currently accumulates tool call state in toolEditState but only returns finalText; change streamSubAgent to return both the text and the tool call state (e.g., { text: string, toolCalls: Map<string,AgentToolCall> }) by updating the function return type and final return value, preserve building toolEditState inside the function, and update all callers of streamSubAgent (and any type annotations) to handle the new object shape so callers can access toolCalls for logging/debugging.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/ai-core/src/hooks/useSessionChat.ts`:
- Around line 107-108: Delete the unused ref declaration and assignment for
addToolOutputRef: remove the const addToolOutputRef =
useRef<AddToolOutput>(null!) declaration and any subsequent assignment to
addToolOutputRef, since addToolOutput is already registered with the store (the
addToolOutput function is used directly) and the ref is redundant; ensure any
imports (e.g., useRef) that become unused are cleaned up.
In `@packages/ai-core/src/utils.ts`:
- Around line 308-349: The tail tool-detection in shouldEndAnalysis currently
only checks part.type and can miss tool parts that are waiting for approval;
update the tailHasTool predicate inside shouldEndAnalysis to also treat a tool
part as pending when its state === 'approval-requested' (e.g., check part.state
=== 'approval-requested' alongside type checks), mirroring the pattern used in
fixIncompleteToolCalls; ensure this integrates with the existing use of
lastAssistantMessageIsCompleteWithToolCalls and keep behavior consistent with
lastAssistantMessageIsCompleteWithApprovalResponses for approval flows.
---
Outside diff comments:
In `@packages/ai-core/src/AiSlice.ts`:
- Around line 600-610: deleteSession currently clears several per-session maps
but misses removing entries from sessionAddToolApprovalResponses, causing
potential memory leaks; update the deleteSession method to also call
sessionAddToolApprovalResponses.delete(sessionId) (or clear the map entry)
alongside sessionAddToolOutputs.delete(sessionId) and the other
sessionXXX.delete(sessionId) calls so all per-session handlers are cleaned up
when deleteSession runs.
---
Nitpick comments:
In `@examples/ai-nextjs/src/app/api/chat/route.ts`:
- Around line 11-18: Add a Zod schema and validate the parsed body immediately
after the await req.json() call in the API route handler in route.ts: define a
schema for messages, modelProvider, model, instructions, maxSteps (number),
temperature (number) with appropriate defaults/required flags, parse/parseAsync
the incoming payload, and if validation fails return a 400 response with the
error details; then use the validated/parsed values (instead of raw
destructuring) so downstream logic (e.g., any function expecting numeric
maxSteps or temperature) receives correct types.
In `@packages/ai-core/src/agents/AgentUtils.ts`:
- Around line 73-114: streamSubAgent currently accumulates tool call state in
toolEditState but only returns finalText; change streamSubAgent to return both
the text and the tool call state (e.g., { text: string, toolCalls:
Map<string,AgentToolCall> }) by updating the function return type and final
return value, preserve building toolEditState inside the function, and update
all callers of streamSubAgent (and any type annotations) to handle the new
object shape so callers can access toolCalls for logging/debugging.
In `@packages/ai-core/src/components/ToolCallInfo.tsx`:
- Around line 13-25: ToolCallInfoProps declares an unused isCompleted prop —
remove the isCompleted field from the ToolCallInfoProps type declaration and
update the ToolCallInfo component JSDoc to delete the `@param` props.isCompleted
entry; then remove the isCompleted={isCompleted} prop passed from
ToolPartRenderer (so ToolPartRenderer no longer forwards that unused prop).
Ensure you only change the type, JSDoc, and the prop usage (references:
ToolCallInfoProps, isCompleted, ToolPartRenderer) so the component derives
completion from the existing state prop.
In `@packages/ai-core/src/components/ToolPartRenderer.tsx`:
- Around line 183-188: Remove the now-unused isCompleted prop from the
ToolCallInfo invocation in ToolPartRenderer: update the JSX that renders
<ToolCallInfo ... /> by deleting the isCompleted={isCompleted} prop so the
component is called with only toolName, input, and state; verify ToolCallInfo
props type (in ToolCallInfo.tsx) matches and run type checks to ensure no other
references to isCompleted remain in ToolPartRenderer.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1d32e015-3c6e-4248-a664-355e233d5a64
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (32)
docs/upgrade-guide.mdexamples/ai-agent/package.jsonexamples/ai-agent/src/agents/WeatherAgent.tsexamples/ai-core/package.jsonexamples/ai-nextjs/package.jsonexamples/ai-nextjs/src/app/api/chat/route.tsexamples/ai-rag/package.jsonexamples/ai/package.jsonexamples/app-builder/package.jsonpackages/ai-config/src/index.tspackages/ai-config/src/schema/UIMessageSchema.tspackages/ai-core/README.mdpackages/ai-core/package.jsonpackages/ai-core/src/AiSlice.tspackages/ai-core/src/agents/AgentUtils.tspackages/ai-core/src/chatTransport.tspackages/ai-core/src/components/ToolCallInfo.tsxpackages/ai-core/src/components/ToolPartRenderer.tsxpackages/ai-core/src/components/tools/ToolResult.tsxpackages/ai-core/src/hooks/useAssistantMessageParts.tspackages/ai-core/src/hooks/useSessionChat.tspackages/ai-core/src/index.tspackages/ai-core/src/types.tspackages/ai-core/src/utils.tspackages/ai-rag/package.jsonpackages/ai-rag/src/createRagTool.tsxpackages/ai/package.jsonpackages/ai/src/index.tspackages/ai/src/tools/query/queryTool.tspackages/mosaic/package.jsonpackages/vega/package.jsonpackages/vega/src/VegaChartTool.tsx
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
packages/ai-core/src/AiSlice.ts (3)
214-214:⚠️ Potential issue | 🟠 Major
getMaxStepsFromSettings()currently masks the settings value.Because
maxStepsis defaulted to50at Line 214, the first branch here always wins when the caller does not pass an override. That makesaiSettings.config.modelParameters.maxStepsunreachable, so the new agent loop ignores the user-configured limit.🔧 Minimal fix
- maxSteps = 50, + maxSteps, ... getMaxStepsFromSettings: () => { const store = get(); // First try the maxSteps parameter if provided - if (maxSteps && Number.isFinite(maxSteps) && maxSteps > 0) { + if ( + maxSteps !== undefined && + Number.isFinite(maxSteps) && + maxSteps > 0 + ) { return maxSteps; } // Fall back to settings if (hasAiSettingsConfig(store)) { const settingsMaxSteps = store.aiSettings.config.modelParameters.maxSteps; if (Number.isFinite(settingsMaxSteps) && settingsMaxSteps > 0) { return settingsMaxSteps; } } return 50; },Also applies to: 739-754
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/ai-core/src/AiSlice.ts` at line 214, The function getMaxStepsFromSettings currently sets its parameter maxSteps = 50 which causes the first branch to always win and ignores aiSettings.config.modelParameters.maxSteps; change the parameter so it does not default to 50 (e.g. accept undefined/null) and implement a proper fallback: if an explicit maxSteps argument is provided use it, otherwise read aiSettings.config.modelParameters.maxSteps (and if that is missing use 50 as the final default). Update getMaxStepsFromSettings and any other occurrences (same pattern around the block handling lines ~739-754) to follow this precedence: explicit arg -> aiSettings.config.modelParameters.maxSteps -> 50.
651-674:⚠️ Potential issue | 🟡 MinorDon’t report a successful UI-message save when nothing was written.
If the session lookup misses, this still returns
trueeven though nouiMessageswere persisted. The transport callers inpackages/ai-core/src/chatTransport.tsat Line 236 and Line 290 currently ignore the boolean, so this failure mode stays silent and makes the new contract unreliable.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/ai-core/src/AiSlice.ts` around lines 651 - 674, setSessionUiMessages currently returns true even when no session is found and nothing is written; update the implementation of setSessionUiMessages to detect whether a matching session was located and only return true when uiMessages were actually persisted. Specifically, locate the session lookup in the produce callback (the draft.ai.config.sessions.find call inside setSessionUiMessages) and add a boolean flag (e.g., "updated") that is set to true when session.uiMessages is assigned; call set(...) only when necessary or run it but return the flag value (false if no session matched). Ensure the function still catches errors and returns false on exceptions but does not report success when no session was modified.
603-613:⚠️ Potential issue | 🟡 MinorClean up
sessionAddToolApprovalResponseson session deletion.The new approval-response map is the only per-session callback store not cleared in
deleteSession(). That keeps a stale closure alive after the session is gone and makes teardown inconsistent with the other session-scoped callbacks.🧹 Minimal fix
sessionAbortControllers.delete(sessionId); sessionChatStops.delete(sessionId); sessionChatSendMessages.delete(sessionId); sessionAddToolOutputs.delete(sessionId); + sessionAddToolApprovalResponses.delete(sessionId); const now = Date.now();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/ai-core/src/AiSlice.ts` around lines 603 - 613, deleteSession currently fails to remove the per-session approval-response map, leaving sessionAddToolApprovalResponses entries and closures alive; update the deleteSession implementation to mirror the other per-session cleanup (sessionChatStops, sessionChatSendMessages, etc.) by calling sessionAddToolApprovalResponses.delete(sessionId) when a session is deleted so the approval-response callbacks are released.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@packages/ai-core/src/AiSlice.ts`:
- Line 214: The function getMaxStepsFromSettings currently sets its parameter
maxSteps = 50 which causes the first branch to always win and ignores
aiSettings.config.modelParameters.maxSteps; change the parameter so it does not
default to 50 (e.g. accept undefined/null) and implement a proper fallback: if
an explicit maxSteps argument is provided use it, otherwise read
aiSettings.config.modelParameters.maxSteps (and if that is missing use 50 as the
final default). Update getMaxStepsFromSettings and any other occurrences (same
pattern around the block handling lines ~739-754) to follow this precedence:
explicit arg -> aiSettings.config.modelParameters.maxSteps -> 50.
- Around line 651-674: setSessionUiMessages currently returns true even when no
session is found and nothing is written; update the implementation of
setSessionUiMessages to detect whether a matching session was located and only
return true when uiMessages were actually persisted. Specifically, locate the
session lookup in the produce callback (the draft.ai.config.sessions.find call
inside setSessionUiMessages) and add a boolean flag (e.g., "updated") that is
set to true when session.uiMessages is assigned; call set(...) only when
necessary or run it but return the flag value (false if no session matched).
Ensure the function still catches errors and returns false on exceptions but
does not report success when no session was modified.
- Around line 603-613: deleteSession currently fails to remove the per-session
approval-response map, leaving sessionAddToolApprovalResponses entries and
closures alive; update the deleteSession implementation to mirror the other
per-session cleanup (sessionChatStops, sessionChatSendMessages, etc.) by calling
sessionAddToolApprovalResponses.delete(sessionId) when a session is deleted so
the approval-response callbacks are released.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 59d83cef-596e-42f9-ac76-56b19433be53
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (5)
docs/upgrade-guide.mdpackages/ai-core/README.mdpackages/ai-core/src/AiSlice.tspackages/ai-core/src/types.tspackages/pivot/src/pivotExecution.ts
✅ Files skipped from review due to trivial changes (2)
- packages/ai-core/README.md
- packages/pivot/src/pivotExecution.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- docs/upgrade-guide.md
…packages fix: enhance security in POST request handling and improve state management in components
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/ai-core/src/AiSlice.ts (1)
603-613:⚠️ Potential issue | 🟡 MinorClear reverse tool-call mappings during session teardown.
This cleanup removes the session-keyed callbacks, but any
toolCallToSessionIdentries that still point atsessionIdsurvive. Late tool-stream events can still resolve the deleted session, and the map will grow monotonically over time.🧹 Proposed fix
sessionChatSendMessages.delete(sessionId); sessionAddToolOutputs.delete(sessionId); sessionAddToolApprovalResponses.delete(sessionId); + for (const [toolCallId, mappedSessionId] of toolCallToSessionId) { + if (mappedSessionId === sessionId) { + toolCallToSessionId.delete(toolCallId); + } + } const now = Date.now();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/ai-core/src/AiSlice.ts` around lines 603 - 613, The deleteSession function currently removes per-session state but leaves entries in toolCallToSessionId that still map toolCallIds to the deleted sessionId; update deleteSession to scan the toolCallToSessionId map (or use an index) and delete any entries whose value === sessionId so late tool-stream events cannot resolve the removed session and the map does not leak; locate the deleteSession implementation and remove matching keys from toolCallToSessionId for the given sessionId.
🧹 Nitpick comments (1)
packages/ai-core/src/components/ToolPartRenderer.tsx (1)
132-145: Use the sameToolComponentpredicate for branching and rendering.The new approval path activates on any truthy
ToolComponent, but the JSX below still renders only plain function components. A memoized orforwardRefrenderer will therefore enter this branch and return an empty wrapper, which hides the approval UI. Please align those checks, or type the registry as a React component map and render directly.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/ai-core/src/components/ToolPartRenderer.tsx` around lines 132 - 145, The conditional that chooses to return the approval/UI wrapper uses a looser predicate (checking ToolComponent truthiness) than the JSX renderer (which only renders when ToolComponent is a plain function), causing memoized/forwardRef components to be skipped; update the branch to use the same predicate as rendering (or vice-versa) so both use an identical check for renderability — e.g., ensure the if condition mirrors the JSX guard that checks ToolComponent && typeof ToolComponent === 'function' (or change both to React.isValidElementType/explicit registry typing) when evaluating isApprovalState and hasExecute in ToolPartRenderer (the conditional around ToolComponent, isApprovalState, hasExecute and the JSX that renders <ToolComponent/>).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/ai-core/src/agents/AgentUtils.ts`:
- Around line 8-14: The AgentToolCall.type currently restricts state to
'pending' | 'success' | 'error', which omits the new approval-related states;
update the AgentToolCall definition (symbol: AgentToolCall and its state field)
to include 'approval-requested', 'approval-responded', and 'output-denied' in
the state union so tool renderers and approval flows type-check correctly and
handle these new statuses.
- Around line 98-108: The loop in AgentUtils.ts that consumes the streaming
response only appends 'text-delta' chunks and silently ignores
'tool-output-error' and 'tool-input-error' chunks; update the for-await loop
that processes "stream" (and uses throwIfAborted and finalText) to detect error
chunk types (e.g., 'tool-output-error', 'tool-input-error') and immediately
propagate them by throwing an Error (or rethrowing contained error payload)
including the tool error details so the caller sees sub-agent failures instead
of returning partial finalText; ensure the catch block continues to call
throwIfAborted before rethrowing.
In `@packages/ai-core/src/components/ToolCallInfo.tsx`:
- Around line 49-55: The JSX in ToolCallInfo currently treats
'approval-requested' like other in-progress states and renders Loader2
(spinner); change the conditional so 'approval-requested' is excluded from the
spinner branch and instead render a distinct, non-animating indicator for that
state (e.g., use a PauseIcon or a static CircleArrowRightIcon variant) so it
reads as “paused awaiting user action.” Update the conditional logic around the
state check in the ToolCallInfo component (the spots referencing state, Loader2,
and CircleArrowRightIcon) to add an explicit branch for 'approval-requested'
rendering the chosen static icon/class names and remove 'approval-requested'
from the spinner condition.
In `@packages/ai-core/src/hooks/useSessionChat.ts`:
- Around line 120-135: The current seeding of useChat (id, transport, messages:
initialMessages) is losing pending approval requests because
fixIncompleteToolCalls rewrites trailing "approval-requested" parts to
"output-error"; update the flow so approval-requested parts are preserved across
reloads—either by changing fixIncompleteToolCalls (in
packages/ai-core/src/utils.ts) to treat trailing parts with type
"approval-requested" as complete and leave them untouched, or by skipping
fixIncompleteToolCalls for messages that contain an "approval-requested" part
before passing initialMessages into useChat; ensure this touches the helper
fixIncompleteToolCalls and the call site that prepares initialMessages (the code
that seeds useChat) and keep lastAssistantMessageIsCompleteWithApprovalResponses
and isAbortedRef logic unchanged.
---
Outside diff comments:
In `@packages/ai-core/src/AiSlice.ts`:
- Around line 603-613: The deleteSession function currently removes per-session
state but leaves entries in toolCallToSessionId that still map toolCallIds to
the deleted sessionId; update deleteSession to scan the toolCallToSessionId map
(or use an index) and delete any entries whose value === sessionId so late
tool-stream events cannot resolve the removed session and the map does not leak;
locate the deleteSession implementation and remove matching keys from
toolCallToSessionId for the given sessionId.
---
Nitpick comments:
In `@packages/ai-core/src/components/ToolPartRenderer.tsx`:
- Around line 132-145: The conditional that chooses to return the approval/UI
wrapper uses a looser predicate (checking ToolComponent truthiness) than the JSX
renderer (which only renders when ToolComponent is a plain function), causing
memoized/forwardRef components to be skipped; update the branch to use the same
predicate as rendering (or vice-versa) so both use an identical check for
renderability — e.g., ensure the if condition mirrors the JSX guard that checks
ToolComponent && typeof ToolComponent === 'function' (or change both to
React.isValidElementType/explicit registry typing) when evaluating
isApprovalState and hasExecute in ToolPartRenderer (the conditional around
ToolComponent, isApprovalState, hasExecute and the JSX that renders
<ToolComponent/>).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: de031c0d-494d-4273-b23d-36251fd1acbb
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (17)
examples/ai-agent/package.jsonexamples/ai-core/package.jsonexamples/ai-nextjs/package.jsonexamples/ai-nextjs/src/app/api/chat/route.tsexamples/ai-rag/package.jsonexamples/ai/package.jsonpackages/ai-core/package.jsonpackages/ai-core/src/AiSlice.tspackages/ai-core/src/agents/AgentUtils.tspackages/ai-core/src/components/ToolCallInfo.tsxpackages/ai-core/src/components/ToolPartRenderer.tsxpackages/ai-core/src/components/tools/ToolResult.tsxpackages/ai-core/src/hooks/useSessionChat.tspackages/ai-core/src/utils.tspackages/ai-rag/package.jsonpackages/ai/package.jsonpackages/vega/package.json
✅ Files skipped from review due to trivial changes (8)
- packages/vega/package.json
- examples/ai/package.json
- packages/ai-rag/package.json
- examples/ai-rag/package.json
- examples/ai-core/package.json
- examples/ai-nextjs/package.json
- packages/ai-core/package.json
- examples/ai-agent/package.json
🚧 Files skipped from review as they are similar to previous changes (4)
- packages/ai/package.json
- packages/ai-core/src/utils.ts
- packages/ai-core/src/components/tools/ToolResult.tsx
- examples/ai-nextjs/src/app/api/chat/route.ts
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/ai-core/src/agents/AgentUtils.ts`:
- Around line 94-104: Move the awaited createAgentUIStream call into the try
block and call throwIfAborted() immediately before awaiting it so pre-stream
aborts are normalized; specifically, inside the function where you currently
await createAgentUIStream(...) (creating the const stream), remove that await
from before the try, then inside the try call throwIfAborted() and then await
createAgentUIStream(...) to assign stream, so any abort during stream creation
is caught and converted to the ToolAbortError(TOOL_CALL_CANCELLED) handling
path.
In `@packages/ai-core/src/components/ToolPartRenderer.tsx`:
- Around line 132-137: The current ToolPartRenderer returns null when
isApprovalState is true but ToolComponent is missing, which silently hides
approval requests; change this to render a minimal fallback UI (e.g., an inline
ApprovalFallback) that visibly displays the toolName and the approval prompt and
wires into the existing approval handlers so the user can approve/deny; update
the branch that checks isApprovalState && !ToolComponent to return this fallback
element (or call a new renderApprovalFallback function/component) instead of
null and ensure it uses the same props/callback signatures as the real
ToolComponent so the agent loop can continue.
In `@packages/ai-core/src/hooks/useSessionChat.ts`:
- Around line 113-135: The sendAutomaticallyWhen callback only checks
lastAssistantMessageIsCompleteWithApprovalResponses so sessions stall when a
tool returns client-provided output via addToolOutput (no execute); import
lastAssistantMessageIsCompleteWithToolCalls and change sendAutomaticallyWhen to
return true when either
lastAssistantMessageIsCompleteWithApprovalResponses(options) OR
lastAssistantMessageIsCompleteWithToolCalls(options) is true (keeping the
existing isAbortedRef.current guard), so both approval-driven and
tool-output-driven auto-resubmits continue the chat.
In `@packages/ai-core/src/utils.ts`:
- Around line 398-402: The cleanup logic around toolPart state incorrectly
converts valid input-waiting states to 'output-error' when finishing normally;
update fixIncompleteToolCalls (and its callers such as the normal-finish path in
chatTransport.ts) to accept a flag like preserveInputStates or a mode ('abort'
vs 'finish') and, when in the normal finish mode, skip converting states that
start with 'input' (e.g. 'input-streaming', 'input-available') so tools awaiting
client addToolOutput remain unchanged; reference the isCompleted check and
ensure callers using useChat/createAgentUIStreamResponse pass the appropriate
mode so only abort/error flows perform aggressive cancellation.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8d95a8c5-5ec5-421d-9f88-707e7ad2a4ea
📒 Files selected for processing (8)
docs/upgrade-guide.mdpackages/ai-config/src/schema/UIMessageSchema.tspackages/ai-core/src/AiSlice.tspackages/ai-core/src/agents/AgentUtils.tspackages/ai-core/src/chatTransport.tspackages/ai-core/src/components/ToolPartRenderer.tsxpackages/ai-core/src/hooks/useSessionChat.tspackages/ai-core/src/utils.ts
✅ Files skipped from review due to trivial changes (1)
- docs/upgrade-guide.md
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/ai-core/src/AiSlice.ts
|
Add token usage and summarization current session to a new session. Adjust the UI for nested agents/tools Untitled.mp4 |

Summary
Upgrades AI SDK from v5 to v6 (
[email protected]) across all packages and examples.The core change is replacing
streamTextwithToolLoopAgent— tool execution now happens server-side inside an agent loop instead of being orchestrated client-side viaonChatToolCall. This removes ~120 lines of dead plumbing (waitForToolOutput,pendingToolCallResolvers,onChatToolCall) and simplifies the transport layer significantly.Sub-agent composition follows the same pattern:
processAgentStreamis replaced bystreamSubAgent, which wraps aToolLoopAgent+createAgentUIStream.The other major addition is native support for the v6
needsApprovalapproval flow. Tools withneedsApproval: truenow pause the agent loop, stream anapproval-requestedstate to the client, and resume when the user confirms or denies. This replaces our custom approval wiring.Note: The needsApproval mechanism in the Vercel AI SDK works by pausing the chat-level loop and emitting an approval-requested state in the UI message parts. But createAgentUIStream / ToolLoopAgent runs its own internal loop, and it doesn't know how to pause and wait for user approval.
Renames:
addToolResult→addToolOutput,processAgentStream→streamSubAgent.Breaking changes
addToolResult→addToolOutputwith new signature:{tool, toolCallId, output}processAgentStream→streamSubAgent(agent, prompt, abortSignal)approval-requested,approval-responded,output-deniedToolLoopAgent+createAgentUIStreamResponseSee
docs/upgrade-guide.mdsection 0.29.0 for full migration instructions with code examples.Summary by CodeRabbit
Documentation
New Features
Refactor