Skip to content

fix(cli): resolve subagent grouping and UI state persistence#22252

Merged
abhipatel12 merged 4 commits intomainfrom
abhi/subagent-ux
Mar 18, 2026
Merged

fix(cli): resolve subagent grouping and UI state persistence#22252
abhipatel12 merged 4 commits intomainfrom
abhi/subagent-ux

Conversation

@abhipatel12
Copy link
Copy Markdown
Contributor

Summary

This PR significantly improves the UX, stability, and type safety of subagent rendering in the CLI. It ensures that subagent progress UI persists gracefully after completion, connects related tool calls visually through batching to prevent tearing, and resolves several strict typing and rendering bugs.

Details

1. Persistent Rich UI for Subagents (@google/gemini-cli-core)

  • local-invocation.ts: Modified LocalSubagentInvocation to return a structured SubagentProgress object in its returnDisplay rather than a flat Markdown string. This ensures the rich subagent UI (expandable progress, thought bubbles, etc.) persists properly even after the subagent finishes execution.
  • types.ts: Expanded the SubagentProgress interface to include result and terminateReason.
  • index.ts: Exported safeJsonToMarkdown for use by the CLI package.

2. History Batching & Rendering Stability (packages/cli/src/ui/hooks/useGeminiStream)

  • useGeminiStream.ts: Refactored the stream polling logic to batch contiguous completed tools into a single history item. This prevents Ink rendering bugs (tearing/stretching) and connects borders beautifully between sequential tool responses.

3. UI Alignment & Bug Fixes (packages/cli/src/ui/components/messages)

  • ToolGroupMessage.tsx: Updated to handle the newly batched history items. It now preserves the exact chronological ordering of interleaved agent and regular tool calls while cleanly grouping contiguous subagents into a single array before mapping. This prevents individual sticky headers (e.g., ≡ Running Agent...) from duplicating repeatedly.
  • SubagentGroupDisplay.tsx / SubagentProgressDisplay.tsx:
    • Aligned with strict UI development guidelines by removing manual string truncation (relying purely on Ink's wrap="truncate") and using exhaustive switch cases via checkExhaustive for status indicators (avoiding nested ternaries).
    • Resolved a critical @typescript-eslint/no-unsafe-assignment issue by securely narrowing SubagentActivityItem types using in operator and typeof checks for optional properties (displayName, description, content, args).
  • ToolResultDisplay.tsx: Now leverages the exported safeJsonToMarkdown utility directly from core.

Related Issues

N/A

How to Validate

  1. Rich UI Persistence: Run a subagent request (e.g., research some local files). Wait for it to complete. The subagent block should remain expandable, retaining its full activity log rather than resolving to a flat string.
  2. Visual Grouping: In a mixed workflow (agents + regular tools), ensure there is exactly one header for contiguous agent calls and no duplicated borders or headers.
  3. Validation: Run npm run preflight to confirm that all strict linting (@typescript-eslint/array-type, unsafe assignment) and typechecks pass flawlessly.

Pre-Merge Checklist

  • Updated relevant documentation and README (if needed)
  • Added/updated tests (if needed)
  • Noted breaking changes (if any)
  • Validated on required platforms/methods:
    • MacOS
      • npm run
      • npx
      • Docker
      • Podman
      • Seatbelt
    • Windows
      • npm run
      • npx
      • Docker
    • Linux
      • npm run
      • npx
      • Docker

@abhipatel12 abhipatel12 requested a review from a team as a code owner March 13, 2026 00:45
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the user experience, stability, and type safety of subagent rendering within the CLI. It ensures that the subagent's progress interface remains interactive and informative even after completion, visually groups related tool calls to prevent display issues, and addresses various underlying typing and rendering inconsistencies for a more robust and polished command-line interface.

Highlights

  • Persistent Subagent UI: Subagent progress UI now persists gracefully after completion, displaying rich details rather than a flat Markdown string. This is achieved by modifying LocalSubagentInvocation to return a structured SubagentProgress object.
  • Visual Grouping and Stability: Related tool calls are now visually batched to prevent UI tearing and improve visual coherence. The useGeminiStream hook was refactored to group contiguous completed agent tools, and ToolGroupMessage.tsx was updated to handle these batched items, ensuring correct chronological ordering and grouping of subagents.
  • Improved Type Safety and Rendering: Several strict typing and rendering bugs were resolved. SubagentProgress interface was expanded, and SubagentGroupDisplay.tsx and SubagentProgressDisplay.tsx were aligned with strict UI guidelines, including exhaustive switch cases and secure type narrowing for optional properties. The safeJsonToMarkdown utility is now exported and leveraged for consistent markdown rendering.
Changelog
  • packages/cli/src/ui/components/messages/SubagentGroupDisplay.test.tsx
    • Added new test file for the SubagentGroupDisplay component.
  • packages/cli/src/ui/components/messages/SubagentGroupDisplay.tsx
    • Introduced a new component to group and display multiple subagents, managing their collapsed and expanded states.
    • Implemented logic to render a compact summary of subagents in a collapsed view and detailed progress in an expanded view.
    • Integrated overflow actions to allow expanding/collapsing subagent groups via keyboard shortcuts.
  • packages/cli/src/ui/components/messages/SubagentProgressDisplay.test.tsx
    • Updated existing tests to pass the new terminalWidth prop to SubagentProgressDisplay.
    • Adjusted snapshot tests to reflect the addition of a header for running subagents.
  • packages/cli/src/ui/components/messages/SubagentProgressDisplay.tsx
    • Modified the component to accept a terminalWidth prop for better layout control.
    • Added a header that dynamically displays the state of the running subagent.
    • Exported formatToolArgs for external use.
    • Integrated safeJsonToMarkdown to display subagent results and termination reasons when completed.
  • packages/cli/src/ui/components/messages/ToolGroupMessage.tsx
    • Refactored the component to group contiguous agent tool calls into a single array.
    • Utilized the new SubagentGroupDisplay component to render grouped subagents.
    • Adjusted the counting logic for one-line tool calls to correctly account for agent types.
  • packages/cli/src/ui/components/messages/ToolResultDisplay.tsx
    • Updated the component to pass the terminalWidth prop to SubagentProgressDisplay when rendering subagent progress.
  • packages/cli/src/ui/components/messages/snapshots/SubagentProgressDisplay.test.tsx.snap
    • Updated snapshot files to reflect the new header and content structure in SubagentProgressDisplay output.
  • packages/cli/src/ui/hooks/useGeminiStream.ts
    • Modified the stream polling logic to batch contiguous completed agent tool calls into a single history item.
    • Ensured that the borderTop and borderBottom properties are correctly applied to the batched history item.
  • packages/core/src/agents/local-invocation.test.ts
    • Updated tests to assert that LocalSubagentInvocation now returns a structured SubagentProgress object instead of a Markdown string.
    • Verified that the SubagentProgress object correctly contains isSubagentProgress, state, result, and terminateReason properties.
  • packages/core/src/agents/local-invocation.ts
    • Changed LocalSubagentInvocation to return a structured SubagentProgress object for its returnDisplay.
    • Removed direct Markdown formatting for subagent results, delegating display logic to the UI components.
  • packages/core/src/agents/types.ts
    • Expanded the SubagentProgress interface to include optional result and terminateReason properties.
  • packages/core/src/index.ts
    • Exported the safeJsonToMarkdown utility function from the core package.
Activity
  • The pull request was created by abhipatel12. No further activity has been recorded yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces significant improvements to the subagent UI, enhancing both user experience and stability. The move to a structured SubagentProgress object for display is a great architectural change that enables a more persistent and richer UI. The batching of tool calls to prevent rendering artifacts is also a clever and effective solution. The code is well-organized and includes necessary test updates. I've identified one area for improvement regarding display consistency between collapsed and expanded views, which is crucial for maintaining consistent UI behavior as per our guidelines.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 13, 2026

Size Change: +11.2 kB (+0.04%)

Total Size: 26.2 MB

Filename Size Change
./bundle/chunk-PFX74K66.js 0 B -1.95 MB (removed) 🏆
./bundle/chunk-PKY5Z6XL.js 0 B -3.63 MB (removed) 🏆
./bundle/chunk-XRTVRU74.js 0 B -13.5 MB (removed) 🏆
./bundle/core-7FY54KKJ.js 0 B -41.3 kB (removed) 🏆
./bundle/devtoolsService-RR6Y6E7Y.js 0 B -27.7 kB (removed) 🏆
./bundle/interactiveCli-ITGRQXRT.js 0 B -1.6 MB (removed) 🏆
./bundle/oauth2-provider-ZZJBTY3C.js 0 B -9.19 kB (removed) 🏆
./bundle/chunk-B4ASJGCP.js 3.63 MB +3.63 MB (new file) 🆕
./bundle/chunk-DFBEITQE.js 13.5 MB +13.5 MB (new file) 🆕
./bundle/chunk-G4TMH6EN.js 1.95 MB +1.95 MB (new file) 🆕
./bundle/core-E7WJC2OV.js 41.4 kB +41.4 kB (new file) 🆕
./bundle/devtoolsService-DXNBTYQJ.js 27.7 kB +27.7 kB (new file) 🆕
./bundle/interactiveCli-46LGKGV4.js 1.61 MB +1.61 MB (new file) 🆕
./bundle/oauth2-provider-5YEDQWIU.js 9.19 kB +9.19 kB (new file) 🆕
ℹ️ View Unchanged
Filename Size Change
./bundle/chunk-34MYV7JD.js 2.45 kB 0 B
./bundle/chunk-37ZTTFQF.js 966 kB 0 B
./bundle/chunk-5AUYMPVF.js 858 B 0 B
./bundle/chunk-664ZODQF.js 124 kB 0 B
./bundle/chunk-DAHVX5MI.js 206 kB 0 B
./bundle/chunk-IUUIT4SU.js 56.5 kB 0 B
./bundle/chunk-RJTRUG2J.js 39.8 kB 0 B
./bundle/devtools-36NN55EP.js 696 kB 0 B
./bundle/dist-T73EYRDX.js 356 B 0 B
./bundle/gemini.js 696 kB 0 B
./bundle/getMachineId-bsd-TXG52NKR.js 1.55 kB 0 B
./bundle/getMachineId-darwin-7OE4DDZ6.js 1.55 kB 0 B
./bundle/getMachineId-linux-SHIFKOOX.js 1.34 kB 0 B
./bundle/getMachineId-unsupported-5U5DOEYY.js 1.06 kB 0 B
./bundle/getMachineId-win-6KLLGOI4.js 1.72 kB 0 B
./bundle/memoryDiscovery-T62THVNM.js 0 B -922 B (removed) 🏆
./bundle/multipart-parser-KPBZEGQU.js 11.7 kB 0 B
./bundle/node_modules/@google/gemini-cli-devtools/dist/client/main.js 221 kB 0 B
./bundle/node_modules/@google/gemini-cli-devtools/dist/src/_client-assets.js 227 kB 0 B
./bundle/node_modules/@google/gemini-cli-devtools/dist/src/index.js 11.5 kB 0 B
./bundle/node_modules/@google/gemini-cli-devtools/dist/src/types.js 132 B 0 B
./bundle/sandbox-macos-permissive-open.sb 890 B 0 B
./bundle/sandbox-macos-permissive-proxied.sb 1.31 kB 0 B
./bundle/sandbox-macos-restrictive-open.sb 3.36 kB 0 B
./bundle/sandbox-macos-restrictive-proxied.sb 3.56 kB 0 B
./bundle/sandbox-macos-strict-open.sb 4.82 kB 0 B
./bundle/sandbox-macos-strict-proxied.sb 5.02 kB 0 B
./bundle/src-QVCVGIUX.js 47 kB 0 B
./bundle/tree-sitter-7U6MW5PS.js 274 kB 0 B
./bundle/tree-sitter-bash-34ZGLXVX.js 1.84 MB 0 B
./bundle/undici-4X2YZID5.js 360 B 0 B
./bundle/memoryDiscovery-KKSSU3SI.js 922 B +922 B (new file) 🆕

compressed-size-action

Copy link
Copy Markdown
Contributor

@jacob314 jacob314 left a comment

Choose a reason for hiding this comment

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

🚨 Critical Issue: Initial State Hiding / Flickering in SubagentGroupDisplay.tsx

Currently, SubagentGroupDisplay filters the passed toolCalls to only those that have a valid SubagentProgress object:

const validAgentCalls = toolCalls.filter((tc) =>
  isSubagentProgress(tc.resultDisplay),
);

if (validAgentCalls.length === 0) {
  return null;
}

The Bug: When an agent tool call is first initiated by the model, its resultDisplay (which is mapped from liveOutput during execution) may briefly be undefined before the backend yields the first progress event.
Because of the strict filter, the component will return null and the agent will not be visible at all in the UI until it emits its first progress. If you group 2 agents and one is slightly slower to start, it will throw off the "X Agents Running" counts and pop in abruptly.

Recommendation: Remove the validAgentCalls filter. Trust the toolCalls array passed into the component (since ToolGroupMessage already groups them by Kind.Agent). If tc.resultDisplay is undefined (or not a progress object), render a generic "Starting..." state.

Copy link
Copy Markdown
Contributor

@jacob314 jacob314 left a comment

Choose a reason for hiding this comment

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

Approved with some minor nits.

@abhipatel12
Copy link
Copy Markdown
Contributor Author

Thanks for the review!

@abhipatel12 abhipatel12 enabled auto-merge March 18, 2026 03:05
@abhipatel12 abhipatel12 added this pull request to the merge queue Mar 18, 2026
Merged via the queue into main with commit be7c7bb Mar 18, 2026
27 checks passed
@abhipatel12 abhipatel12 deleted the abhi/subagent-ux branch March 18, 2026 03:25
theerud pushed a commit to theerud/gemini-cli that referenced this pull request Mar 18, 2026
ProthamD pushed a commit to ProthamD/gemini-cli that referenced this pull request Mar 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status/need-issue Pull requests that need to have an associated issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants