Skip to content

fix(cli): bound memory growth in high-volume components#24862

Closed
spencer426 wants to merge 3 commits intomainfrom
mem-vleak
Closed

fix(cli): bound memory growth in high-volume components#24862
spencer426 wants to merge 3 commits intomainfrom
mem-vleak

Conversation

@spencer426
Copy link
Copy Markdown
Contributor

Summary

This PR addresses potential memory leaks in the CLI UI by bounding the maximum number of items/lines retained in high-volume components (AnsiOutput, SubagentProgressDisplay, and text-buffer reducers). It also adds a missing test:ci script to packages/test-utils/package.json to satisfy CI checks.

Details

  • AnsiOutput: Limited the maximum number of ANSI lines rendered to 1000 when truncation is disabled.
  • SubagentProgressDisplay: Bounded the recent activity history rendered to the last 100 items.
  • text-buffer.ts: Capped the maximum text buffer lines at 10,000, adjusting the cursor and expanded paste regions accordingly.
  • packages/test-utils: Added test:ci script (echo 'no tests') to pass npm run test:ci across workspaces.

Related Issues

How to Validate

  1. Run npm run preflight to verify all linters, type checks, and tests pass.
  2. Run high-volume CLI interactions to verify bounded memory.

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

@spencer426 spencer426 requested a review from a team as a code owner April 7, 2026 23:32
@gemini-cli
Copy link
Copy Markdown
Contributor

gemini-cli Bot commented Apr 7, 2026

Hi @spencer426, thank you so much for your contribution to Gemini CLI! We really appreciate the time and effort you've put into this.

We're making some updates to our contribution process to improve how we track and review changes. Please take a moment to review our recent discussion post: Improving Our Contribution Process & Introducing New Guidelines.

Key Update: Starting January 26, 2026, the Gemini CLI project will require all pull requests to be associated with an existing issue. Any pull requests not linked to an issue by that date will be automatically closed.

Thank you for your understanding and for being a part of our community!

@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 introduces memory usage optimizations across several CLI UI components by enforcing hard limits on the amount of data retained in memory. By capping the number of lines and activity items processed, the application becomes more resilient during high-volume operations. Additionally, a minor configuration update was applied to the test utilities package to resolve CI execution issues.

Highlights

  • Memory Management: Implemented strict bounds on data retention for high-volume UI components to prevent memory leaks.
  • Component Updates: Capped ANSI output lines at 1000, recent activity items at 100, and text buffer lines at 10,000.
  • CI Configuration: Added a placeholder test:ci script to packages/test-utils to ensure compatibility with CI pipeline requirements.
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.

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.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 7, 2026

Size Change: +1.36 kB (0%)

Total Size: 34 MB

Filename Size Change
./bundle/chunk-VSGPLWPS.js 0 B -3.15 MB (removed) 🏆
./bundle/chunk-WUVN64PT.js 0 B -14.8 MB (removed) 🏆
./bundle/core-43NWIXQF.js 0 B -45.4 kB (removed) 🏆
./bundle/devtoolsService-LFPVG2JD.js 0 B -28.4 kB (removed) 🏆
./bundle/interactiveCli-ZP55UWFI.js 0 B -1.65 MB (removed) 🏆
./bundle/oauth2-provider-LPT5X24X.js 0 B -9.16 kB (removed) 🏆
./bundle/chunk-CU6DZNKG.js 3.15 MB +3.15 MB (new file) 🆕
./bundle/chunk-WFDAL7DK.js 14.8 MB +14.8 MB (new file) 🆕
./bundle/core-477J6EUV.js 45.4 kB +45.4 kB (new file) 🆕
./bundle/devtoolsService-2M3LAJCA.js 28.4 kB +28.4 kB (new file) 🆕
./bundle/interactiveCli-O3EONGI5.js 1.65 MB +1.65 MB (new file) 🆕
./bundle/oauth2-provider-I6JNP3RV.js 9.16 kB +9.16 kB (new file) 🆕
ℹ️ View Unchanged
Filename Size
./bundle/bundled/third_party/index.js 8 MB
./bundle/chunk-34MYV7JD.js 2.45 kB
./bundle/chunk-5AUYMPVF.js 858 B
./bundle/chunk-5PS3AYFU.js 1.18 kB
./bundle/chunk-664ZODQF.js 124 kB
./bundle/chunk-DAHVX5MI.js 206 kB
./bundle/chunk-IUUIT4SU.js 56.5 kB
./bundle/chunk-OGWWODAT.js 1.96 MB
./bundle/chunk-RJTRUG2J.js 39.8 kB
./bundle/devtools-36NN55EP.js 696 kB
./bundle/dist-T73EYRDX.js 356 B
./bundle/events-XB7DADIJ.js 418 B
./bundle/gemini.js 553 kB
./bundle/getMachineId-bsd-TXG52NKR.js 1.55 kB
./bundle/getMachineId-darwin-7OE4DDZ6.js 1.55 kB
./bundle/getMachineId-linux-SHIFKOOX.js 1.34 kB
./bundle/getMachineId-unsupported-5U5DOEYY.js 1.06 kB
./bundle/getMachineId-win-6KLLGOI4.js 1.72 kB
./bundle/memoryDiscovery-JNNGTYL3.js 980 B
./bundle/multipart-parser-KPBZEGQU.js 11.7 kB
./bundle/node_modules/@google/gemini-cli-devtools/dist/client/main.js 222 kB
./bundle/node_modules/@google/gemini-cli-devtools/dist/src/_client-assets.js 229 kB
./bundle/node_modules/@google/gemini-cli-devtools/dist/src/index.js 13.4 kB
./bundle/node_modules/@google/gemini-cli-devtools/dist/src/types.js 132 B
./bundle/sandbox-macos-permissive-open.sb 890 B
./bundle/sandbox-macos-permissive-proxied.sb 1.31 kB
./bundle/sandbox-macos-restrictive-open.sb 3.36 kB
./bundle/sandbox-macos-restrictive-proxied.sb 3.56 kB
./bundle/sandbox-macos-strict-open.sb 4.82 kB
./bundle/sandbox-macos-strict-proxied.sb 5.02 kB
./bundle/src-QVCVGIUX.js 47 kB
./bundle/tree-sitter-7U6MW5PS.js 274 kB
./bundle/tree-sitter-bash-34ZGLXVX.js 1.84 MB

compressed-size-action

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 line limits and truncation logic across several UI components and the text buffer to prevent memory issues and improve performance. Specifically, it caps ANSI output to 1000 lines, subagent progress activity to 100 items, and the text buffer to 10,000 lines. Feedback was provided regarding a logical inconsistency in the AnsiOutput truncation where disabling truncation could unexpectedly result in fewer lines than when enabled. Additionally, the textBufferReducer needs to be updated to correctly shift or clear the selectionAnchor when the buffer is truncated to avoid UI state corruption.

Comment on lines +38 to 45
const MAXIMUM_ANSI_LINES_RENDERED = 1000;
const lastLines = Array.isArray(data)
? disableTruncation
? data
? data.slice(-MAXIMUM_ANSI_LINES_RENDERED)
: numLinesRetained === 0
? []
: data.slice(-numLinesRetained)
: [];
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.

high

The current implementation of MAXIMUM_ANSI_LINES_RENDERED introduces a logical inconsistency. When disableTruncation is true, the output is capped at 1000 lines. However, when disableTruncation is false, the number of lines is determined by numLinesRetained, which can exceed 1000 if maxLines is set to a higher value. This means that 'disabling' truncation could actually result in fewer lines being displayed than when it is enabled. The safety cap should be applied consistently to both cases to ensure the memory bound is respected without inverting the behavior of the disableTruncation prop.

Suggested change
const MAXIMUM_ANSI_LINES_RENDERED = 1000;
const lastLines = Array.isArray(data)
? disableTruncation
? data
? data.slice(-MAXIMUM_ANSI_LINES_RENDERED)
: numLinesRetained === 0
? []
: data.slice(-numLinesRetained)
: [];
const MAXIMUM_ANSI_LINES_RENDERED = 1000;
const lastLines = Array.isArray(data)
? disableTruncation
? data.slice(-MAXIMUM_ANSI_LINES_RENDERED)
: numLinesRetained === 0
? []
: data.slice(-Math.min(numLinesRetained, MAXIMUM_ANSI_LINES_RENDERED))
: [];
References
  1. When implementing truncation logic, a simpler approach is preferred over a more complex one if the potential inaccuracy is trivial compared to the overall buffer size.
  2. Maintain consistency with existing UI behavior across components.

Comment on lines +2785 to +2804
if (newState.lines.length > MAX_TEXT_BUFFER_LINES) {
const excess = newState.lines.length - MAX_TEXT_BUFFER_LINES;
const newLines = newState.lines.slice(excess);
const newCursorRow = Math.max(0, newState.cursorRow - excess);
let newExpandedPaste = newState.expandedPaste;
if (newExpandedPaste) {
const newStartLine = newExpandedPaste.startLine - excess;
if (newStartLine < 0) {
newExpandedPaste = null;
} else {
newExpandedPaste = { ...newExpandedPaste, startLine: newStartLine };
}
}
newState = {
...newState,
lines: newLines,
cursorRow: newCursorRow,
expandedPaste: newExpandedPaste,
};
}
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.

high

The truncation logic correctly adjusts the cursorRow and expandedPaste regions, but it fails to update the selectionAnchor. If a selection exists when the buffer is truncated, the anchor row will point to the wrong line or become negative, leading to inconsistent state and UI bugs.

  if (newState.lines.length > MAX_TEXT_BUFFER_LINES) {
    const excess = newState.lines.length - MAX_TEXT_BUFFER_LINES;
    const newLines = newState.lines.slice(excess);
    const newCursorRow = Math.max(0, newState.cursorRow - excess);
    let newExpandedPaste = newState.expandedPaste;
    if (newExpandedPaste) {
      const newStartLine = newExpandedPaste.startLine - excess;
      if (newStartLine < 0) {
        newExpandedPaste = null;
      } else {
        newExpandedPaste = { ...newExpandedPaste, startLine: newStartLine };
      }
    }
    let newSelectionAnchor = newState.selectionAnchor;
    if (newSelectionAnchor) {
      const [anchorRow, anchorCol] = newSelectionAnchor;
      const shiftedRow = anchorRow - excess;
      newSelectionAnchor = shiftedRow < 0 ? null : [shiftedRow, anchorCol];
    }
    newState = {
      ...newState,
      lines: newLines,
      cursorRow: newCursorRow,
      expandedPaste: newExpandedPaste,
      selectionAnchor: newSelectionAnchor,
    };
  }
References
  1. When implementing truncation logic, a simpler approach is preferred over a more complex one if the potential inaccuracy is trivial compared to the overall buffer size.

@gemini-cli gemini-cli Bot added the priority/p1 Important and should be addressed in the near term. label Apr 7, 2026
@gemini-cli
Copy link
Copy Markdown
Contributor

gemini-cli Bot commented Apr 22, 2026

Hi there! Thank you for your interest in contributing to Gemini CLI.

To ensure we maintain high code quality and focus on our prioritized roadmap, we have updated our contribution policy (see Discussion #17383).

We only guarantee review and consideration of pull requests for issues that are explicitly labeled as 'help wanted'. All other community pull requests are subject to closure after 14 days if they do not align with our current focus areas. For this reason, we strongly recommend that contributors only submit pull requests against issues explicitly labeled as 'help-wanted'.

This pull request is being closed as it has been open for 14 days without a 'help wanted' designation. We encourage you to find and contribute to existing 'help wanted' issues in our backlog! Thank you for your understanding and for being part of our community!

@gemini-cli gemini-cli Bot closed this Apr 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority/p1 Important and should be addressed in the near term.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant