Skip to content

fix: agent output render issue - #505

Merged
lixun910 merged 3 commits into
mainfrom
504-fix-subagent-tool-rendering-issue-in-new-ai-sdk-v5
Mar 31, 2026
Merged

lixun910 merged 3 commits into
mainfrom
504-fix-subagent-tool-rendering-issue-in-new-ai-sdk-v5

Conversation

@lixun910

@lixun910 lixun910 commented Mar 30, 2026 •

Copy link
Copy Markdown
Collaborator

after removing openassistant dependency and migrating tools to AI SDK standard tool, the tools of subagent can not be rendered in the main agent.

Summary by CodeRabbit

  • New Features
    • Real-time progress tracking for agent tool execution during operations
    • Live syncing of per-tool-call progress during streaming with automatic clear on completion
    • Interactive tool-call details popup showing execution status, output, and errors
    • Structured agent output now includes final output plus detailed, per-tool-call history

@lixun910 lixun910 linked an issue Mar 30, 2026 that may be closed by this pull request
@coderabbitai

coderabbitai Bot commented Mar 30, 2026 •

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 62553de3-de07-494b-b0f2-950ac1ae267a

📥 Commits

Reviewing files that changed from the base of the PR and between a4d62fe and f40550d.

📒 Files selected for processing (1)
  • packages/ai-core/src/agents/AgentUtils.ts

📝 Walkthrough

Walkthrough

Adds live agent sub-tool-call progress tracking: stream processing now returns structured AgentStreamOutput (finalOutput + agentToolCalls), store gains agentProgress with update/clear actions, and UI components render live per-tool-call progress and details during agent execution.

Changes

Cohort / File(s) Summary
Agent stream & agent output
packages/ai-core/src/agents/AgentUtils.ts, examples/ai-agent/src/agents/WeatherAgent.ts
processAgentStream now returns AgentStreamOutput { finalOutput, agentToolCalls } instead of plain string; streaming logic tracks per-tool-call lifecycle and returns structured result; WeatherAgent updated to consume the new shape.
State management (agent progress)
packages/ai-core/src/AiSlice.ts, packages/ai-core/src/types.ts
Added agentProgress: Record<string, AgentToolCall[]> to state/transport types; new actions updateAgentProgress(parentToolCallId, toolCalls) and clearAgentProgress(parentToolCallId) implemented.
UI rendering & components
packages/ai-core/src/components/ToolPartRenderer.tsx
Introduced ToolCallDetailPopup, AgentProgressSection and updated AgentProgressRenderer to prefer live s.ai.agentProgress[toolCallId]; UI now shows live per-tool-call entries, final calls, or fallback results.
Public type exports
packages/ai-core/src/index.ts, packages/ai/src/index.ts
Exported AgentStreamOutput from core and re-exported from main package to expose the new structured stream output type.

Sequence Diagram(s)

sequenceDiagram
    participant Agent as Agent Stream Processor
    participant Store as AI Store
    participant UI as ToolPartRenderer

    Agent->>Agent: Process streamed UIMessageChunks (tool events)
    Agent->>Store: updateAgentProgress(parentToolCallId, toolCalls)
    Store->>UI: state change (agentProgress updated)
    UI->>UI: Render AgentProgressRenderer (live tool calls)
    Agent->>Agent: Stream completes -> assemble finalOutput
    Agent->>Store: clearAgentProgress(parentToolCallId)
    UI->>UI: Render final AgentProgressRenderer with agentToolCalls
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • sqlrooms/sqlrooms issue 504: Targets streamed sub-agent tool-call rendering and progress state; this change implements live progress and per-tool-call visibility requested there.

Possibly related PRs

  • PR 423: Modifies agent stream handling and AgentStreamOutput types; likely strongly related to these changes.

Suggested reviewers

  • ilyabo
  • dmitriy-kostianetskiy

Poem

🐰 I nibble streams and count each call,
Tool hops recorded, I catch them all,
Final output curled in a neat little row,
Live progress sparkles as onward we go—
A rabbit’s cheer for updates that glow! ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'fix: agent output render issue' is vague and uses generic terminology ('issue') that doesn't convey specific information about what was fixed or how the changeset addresses the problem. Consider using a more descriptive title that clarifies the specific rendering problem being addressed, such as 'fix: display subagent tool calls in agent progress rendering' or similar.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 504-fix-subagent-tool-rendering-issue-in-new-ai-sdk-v5

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
packages/ai-core/src/agents/AgentUtils.ts (1)

151-161: Minor: Abort signal not checked during await agentResult.text.

If the abort signal fires while awaiting agentResult.text at line 156, the abort won't be immediately respected since the promise doesn't accept an abort signal. The current behavior is acceptable for most use cases, but for strict cancellation semantics, consider wrapping with Promise.race against an abort promise.

♻️ Optional: Race against abort signal
   throwIfAborted();
-  const finalOutput = await agentResult.text;
+  const finalOutput = await Promise.race([
+    agentResult.text,
+    new Promise<never>((_, reject) => {
+      if (abortSignal?.aborted) {
+        reject(new ToolAbortError(TOOL_CALL_CANCELLED));
+      }
+      abortSignal?.addEventListener('abort', () => {
+        reject(new ToolAbortError(TOOL_CALL_CANCELLED));
+      }, { once: true });
+    }),
+  ]);
🤖 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 151 - 161, The await
on agentResult.text can block cancellation; wrap waiting for agentResult.text in
a Promise.race with an abort promise so the abort signal is respected: create an
abortPromise that rejects (or resolves to a sentinel) when throwIfAborted()
would trigger (or listen to the same AbortSignal used elsewhere), then replace
`const finalOutput = await agentResult.text;` with `const finalOutput = await
Promise.race([agentResult.text, abortPromise]);` and handle the abort case by
calling throwIfAborted() (or rethrowing) so cancellation flows through; keep the
existing finally cleanup
(store.getState().ai.clearAgentProgress(parentToolCallId)) and preserve
returning `agentToolCalls: Array.from(toolCallMap.values())`.
packages/ai-core/src/components/ToolPartRenderer.tsx (1)

188-216: Consider consolidating redundant store reads.

liveProgress is read from the store at line 188 in AgentProgressSection, and then read again at line 101 inside AgentProgressRenderer. Since AgentProgressRenderer already reads live progress internally, the check at line 188 could be simplified by always rendering AgentProgressRenderer and letting it handle the live vs. fallback logic entirely.

♻️ Potential simplification
 const AgentProgressSection: React.FC<{
   toolCallId: string;
   output: unknown;
   input: unknown;
   state:
     | 'input-streaming'
     | 'input-available'
     | 'output-available'
     | 'output-error';
   errorText?: string;
 }> = ({toolCallId, output, input, state, errorText}) => {
-  const liveProgress = useStoreWithAi((s) => s.ai.agentProgress[toolCallId]);
-
   const agentOutput = output as {
     agentToolCalls?: Array<{
       toolCallId: string;
       toolName: string;
       output?: unknown;
       errorText?: string;
       state: 'pending' | 'success' | 'error';
     }>;
     finalOutput?: string;
   };

-  const hasFinishedAgentProgress =
-    agentOutput?.agentToolCalls && agentOutput.agentToolCalls.length > 0;
-
   const reasoning =
     input instanceof Object && 'reasoning' in input
       ? (input.reasoning as string)
       : undefined;

-  if (liveProgress && liveProgress.length > 0) {
+  // AgentProgressRenderer handles live progress internally
+  if (agentOutput?.agentToolCalls && agentOutput.agentToolCalls.length > 0) {
     return (
       <AgentProgressRenderer
         toolCallId={toolCallId}
-        agentToolCalls={[]}
+        agentToolCalls={agentOutput.agentToolCalls}
+        finalOutput={agentOutput.finalOutput}
         reasoning={reasoning}
       />
     );
   }

-  if (hasFinishedAgentProgress) {
-    return (
-      <AgentProgressRenderer
-        toolCallId={toolCallId}
-        agentToolCalls={agentOutput.agentToolCalls!}
-        finalOutput={agentOutput.finalOutput}
-        reasoning={reasoning}
-      />
-    );
-  }
-
   return (
     <ToolResult
       toolCallId={toolCallId}
🤖 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 188 - 216,
Currently ToolPartRenderer reads liveProgress via useStoreWithAi (liveProgress)
and conditionally returns an AgentProgressRenderer when liveProgress exists;
since AgentProgressRenderer already reads live progress itself, remove the
redundant store read and the early-return branch: always render
<AgentProgressRenderer toolCallId={toolCallId}
agentToolCalls={agentOutput?.agentToolCalls ?? []} reasoning={reasoning} /> (or
equivalent) so AgentProgressRenderer controls live vs. fallback display; delete
the const liveProgress = ... and the if (liveProgress && liveProgress.length >
0) return ... block and ensure agentOutput and reasoning are still computed and
passed through.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/ai-core/src/agents/AgentUtils.ts`:
- Around line 151-161: The await on agentResult.text can block cancellation;
wrap waiting for agentResult.text in a Promise.race with an abort promise so the
abort signal is respected: create an abortPromise that rejects (or resolves to a
sentinel) when throwIfAborted() would trigger (or listen to the same AbortSignal
used elsewhere), then replace `const finalOutput = await agentResult.text;` with
`const finalOutput = await Promise.race([agentResult.text, abortPromise]);` and
handle the abort case by calling throwIfAborted() (or rethrowing) so
cancellation flows through; keep the existing finally cleanup
(store.getState().ai.clearAgentProgress(parentToolCallId)) and preserve
returning `agentToolCalls: Array.from(toolCallMap.values())`.

In `@packages/ai-core/src/components/ToolPartRenderer.tsx`:
- Around line 188-216: Currently ToolPartRenderer reads liveProgress via
useStoreWithAi (liveProgress) and conditionally returns an AgentProgressRenderer
when liveProgress exists; since AgentProgressRenderer already reads live
progress itself, remove the redundant store read and the early-return branch:
always render <AgentProgressRenderer toolCallId={toolCallId}
agentToolCalls={agentOutput?.agentToolCalls ?? []} reasoning={reasoning} /> (or
equivalent) so AgentProgressRenderer controls live vs. fallback display; delete
the const liveProgress = ... and the if (liveProgress && liveProgress.length >
0) return ... block and ensure agentOutput and reasoning are still computed and
passed through.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: eae43361-c52d-493b-b2b2-61ff959eb897

📥 Commits

Reviewing files that changed from the base of the PR and between f11641a and 7954af5.

📒 Files selected for processing (7)
  • examples/ai-agent/src/agents/WeatherAgent.ts
  • packages/ai-core/src/AiSlice.ts
  • packages/ai-core/src/agents/AgentUtils.ts
  • packages/ai-core/src/components/ToolPartRenderer.tsx
  • packages/ai-core/src/index.ts
  • packages/ai-core/src/types.ts
  • packages/ai/src/index.ts

@lixun910
lixun910 requested a review from ilyabo March 30, 2026 19:28
@lixun910

Copy link
Copy Markdown
Collaborator Author
Screenshot 2026-03-30 at 12 29 33 PM

@lixun910
lixun910 merged commit 1bb4642 into main Mar 31, 2026
5 checks passed
@lixun910
lixun910 deleted the 504-fix-subagent-tool-rendering-issue-in-new-ai-sdk-v5 branch March 31, 2026 00:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: subagent tool rendering issue in new AI SDK v5

2 participants