Repository navigation
fix(core): bound multi-part tool outputs and evict older multimodal inlineData - #30180
Conversation
Summary of ChangesHello, 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 addresses a critical V8 heap out-of-memory crash occurring during long-running agentic sessions. By implementing stricter byte-level bounds on tool outputs, improving multimodal data accounting, and introducing proactive compression triggers based on memory usage rather than just token counts, the changes significantly improve the stability and memory footprint of the application. Highlights
Using Gemini Code AssistThe 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
Customization To customize the 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 Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. 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
|
|
📊 PR Size: size/L
|
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request addresses heap out-of-memory issues and memory leaks by introducing history byte size thresholds to trigger collapsing of older function responses, optimizing the truncation of multi-part tool outputs, and adding comprehensive regression tests. Feedback on the changes highlights a critical bug in tool-executor.ts where multi-part tool outputs exceeding the byte budget are discarded instead of being written to the disk fallback, leading to data loss. Additionally, the history byte size calculation in chatCompressionService.ts should be updated to include nested text parts to prevent underestimating the history size.
There was a problem hiding this comment.
Code Review
This pull request introduces memory optimization mechanisms to prevent heap out-of-memory issues, including strict budget-based truncation of tool outputs and collapsing older function responses when history byte size thresholds are exceeded. The review feedback suggests a performance optimization in collapseOlderFunctionResponses to break out of the loop early once the exempt byte budget is exhausted, avoiding expensive size calculations on remaining older turns.
Note: Security Review did not run due to the size of the PR.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request addresses heap out-of-memory issues (GH-29830) by strictly bounding joined tool response outputs and collapsing older multimodal inlineData payloads when history byte size limits are exceeded. Key changes include introducing history byte size thresholds, updating history size calculations to account for nested parts, and refining the tool execution and masking services to enforce these limits. The feedback suggests enhancing robustness by defensively checking that nested parts are valid objects before accessing their properties in both the compression and masking services.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request addresses heap out-of-memory issues (GH-29830) by introducing stricter history byte size thresholds and collapsing older function responses when under memory pressure. It updates history byte size calculations to account for nested multimodal data, bounds joined response outputs, and adds comprehensive regression tests. The review feedback suggests using object destructuring to dynamically exclude the parts property from functionResponse in chatCompressionService.ts rather than explicitly copying individual properties, which is fragile and prone to silent data loss.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request addresses heap out-of-memory issues (GH-29830) by introducing stricter budgeting and truncation of history and tool outputs. Key changes include updating calculateHistoryByteSize to account for nested functionResponse.parts and inlineData, implementing a new history byte size threshold (COLLAPSE_HISTORY_BYTE_THRESHOLD) to trigger collapsing of older responses, and refactoring ToolExecutor to strictly budget and truncate multi-part array outputs. Additionally, comprehensive regression tests have been added to verify these memory-bounding behaviors. No review comments were provided, so I have no feedback to address.
Summary
Fixes a V8 Old Space heap out-of-memory crash (
FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory) during long-running agentic sessions that repeatedly read multiple files (e.g.,read_many_files) or inspect multimodal tool outputs (such as screenshots).Details
packages/core/src/scheduler/tool-executor.ts):MAX_STORED_TOOL_OUTPUT_BYTES(64 KB) budget across array elements inToolExecutor.truncateOutputIfNeededand appliestruncateFunctionResponseParton the convertedfunctionResponseparts increateSuccessResultandcreateCancelledResult. Previously, each element was checked against64 KBindependently beforeconvertToFunctionResponsejoined them into a single multi-megabyte string.inlineDataAccounting & Eviction (packages/core/src/context/chatCompressionService.ts,packages/core/src/context/toolOutputMaskingService.ts):calculateHistoryByteSizeandToolOutputMaskingServiceto count nestedfunctionResponse.partsinlineDatabytes (used by Gemini 3 multimodal tool responses).functionResponse.partswhen masking older tool responses inToolOutputMaskingServiceso token savings are positive instead of negative (previously, retaining the3,000-tokeninlineDatainsidemaskedPartcausedsavings <= 0and aborted masking).collapseOlderFunctionResponsesto strip nestedfunctionResponse.partsand siblingpart.inlineDatapayloads outsideRECENT_TURNS_PROTECTED(3), and bounds older uncollapsed exempt retrieval tool outputs (read_file,read_many_files,grep, etc.) to a64 KBbudget (MAX_EXEMPT_RETRIEVAL_HISTORY_BYTES).packages/core/src/agents/local-executor.ts,packages/core/src/core/client.ts,packages/core/src/core/geminiChat.ts):collapseOlderFunctionResponsesinLocalAgentExecutor.tryCompressChatandGeminiClient.tryMaskToolOutputswhen history exceedsCOLLAPSE_HISTORY_BYTE_THRESHOLD(256 KB), even when estimated tokens are well below the500,000-token compression threshold.HistoryTurn.idvalues when updating chat history viasetHistoryinGeminiClient.tryMaskToolOutputs, and extractsBINARY_INJECTION_KEYbefore recording the initial tool response turn inGeminiChat.sendMessageStream.Related Issues
Fixes #29830
How to Validate
npm test -w @google/gemini-cli-core -- src/agents/memory-leak-regression.test.ts src/context/chatCompressionService.test.ts src/context/toolOutputMaskingService.test.ts src/scheduler/tool-executor.test.tsnpm run lint && npm run typecheckPre-Merge Checklist