fix(core): limit recursive reasoning turns per single user request#28164
fix(core): limit recursive reasoning turns per single user request#28164amelidev wants to merge 18 commits into
Conversation
|
📊 PR Size: size/L
|
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 introduces a safety mechanism to the core agent reasoning engine to mitigate potential infinite loop exploits. By enforcing a maximum turn limit per request, the system is better protected against malicious inputs that could otherwise exhaust local CPU resources or API quotas. The implementation includes flexible configuration options and robust unit testing to ensure reliable behavior across different usage scenarios. 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
|
There was a problem hiding this comment.
Code Review
This pull request introduces a mechanism to limit recursive turns per single prompt_id in GeminiClient. It tracks the turn count using a new promptTurnCount property, resetting it when the prompt_id changes. If the turn count exceeds the configured maximum (defaulting to 15), it yields a MaxSessionTurns event and terminates the turn. Corresponding unit tests have been added to verify both the default and custom limits, as well as the reset behavior. There are no review comments, so no additional feedback is provided.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces a mechanism to limit recursive turns per single prompt_id in GeminiClient to prevent infinite loops, defaulting to 15 turns or respecting a custom configured limit. It also adds a comprehensive test suite to verify this behavior. Feedback points out a leftover debug statement in the test file that writes to ./debug-test.log and should be removed.
# Please enter a commit message to explain why this merge is necessary, # especially if it merges an updated upstream into a topic branch. # # Lines starting with '#' will be ignored, and an empty message aborts # the commit.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces a mechanism to limit recursive turns per single prompt ID to a default of 15 (or a custom configured limit) within the GeminiClient and adds corresponding unit tests. However, tracking this limit using a single instance variable (promptTurnCount) introduces a concurrency hazard where interleaved concurrent requests can corrupt the count. The review feedback correctly suggests refactoring this to use a concurrency-safe Map (promptTurnCounts) keyed by prompt_id across the client implementation and tests.
Note: Security Review did not run due to the size of the PR.
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces a mechanism to limit recursive turns per single prompt_id in GeminiClient to prevent infinite loops. It tracks turn counts using a new promptTurnCounts map, enforcing a default limit of 15 turns or a custom limit if configured. Comprehensive unit tests have been added to verify this behavior, including limit enforcement and counter resets on new prompt IDs. I have no feedback to provide as there are no review comments to evaluate.
|
Ready to approve, all the feedback adressed |
|
Overall, this looks great. It does have a quick adjustment I'd recommend implementing before I give it an LGTM: Memory Management (Potential Leak)The private readonly promptTurnCounts = new Map<string, number>();It accumulates entries for every unique Recommendation: if (this.lastPromptId !== prompt_id) {
this.loopDetector.reset(prompt_id, partListUnionToString(request));
this.hookStateMap.delete(this.lastPromptId);
+ this.promptTurnCounts.delete(this.lastPromptId);
this.lastPromptId = prompt_id;
this.currentSequenceModel = null;
}This ensures that we only keep turn counts for active prompts, preventing unbounded map growth. |
|
While capping recursive turns per request is a strong and necessary safeguard against runaway agent loops, coupling this limit to 🟡 Architectural Concerns & Recommendations1. Overloading
|
|
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. |
Working on this suggestion |
- Introduce 'maxPromptTurns' setting to manage recursive prompt turns separately from total session depth - Yield 'GeminiEventType.MaxPromptTurns' when a single prompt's reasoning budget is exceeded - Update UI error warning message to clarify reasoning limit was reached rather than session ending - Update non-interactive CLI and agent sessions to support both prompt-level and session-level budgets - Update all associated mocks, schemas, and test assertions to align with decoupled limits
…termination - Allow callers of GeminiClient to request a custom recursive turn budget that overrides prompt-level cap - Handle MaxPromptTurns events in legacy agent sessions and ACP sessions to clear active streams gracefully - Add comprehensive test coverage in client.test.ts and legacy-agent-session.test.ts
# Conflicts: # package-lock.json
|
/gemini review |
Concerns addressed 👍 After gemini review we are ready for your review again |
There was a problem hiding this comment.
Code Review
This pull request introduces a new configuration option, maxPromptTurns (defaulting to 15), to limit the number of recursive turns (tool calls and model responses) allowed for a single prompt before halting. This change includes updates to settings schemas, CLI configurations, client logic, error handling, and corresponding tests. The review feedback correctly identifies an issue in GeminiClient where the check this.config.getMaxPromptTurns() > 0 incorrectly overrides custom limits of 0 or -1 (unlimited) with the default value of 15, and suggests a correction to properly respect these limits.
| const configMax = | ||
| this.config.getMaxPromptTurns() > 0 | ||
| ? this.config.getMaxPromptTurns() | ||
| : 15; // default to 15 recursive turns per single user request | ||
|
|
||
| const maxAllowedTurns = | ||
| this.initialPromptTurns !== undefined && | ||
| this.initialPromptTurns !== MAX_TURNS | ||
| ? this.initialPromptTurns | ||
| : configMax; | ||
|
|
||
| if (this.promptTurnCount > maxAllowedTurns) { | ||
| yield { type: GeminiEventType.MaxPromptTurns }; | ||
| return new Turn(this.getChat(), prompt_id); | ||
| } |
There was a problem hiding this comment.
The current implementation of configMax overrides custom limits of 0 or -1 (unlimited) to the default value of 15 because of the check this.config.getMaxPromptTurns() > 0. Since -1 is explicitly documented as representing an unlimited number of turns, and 0 is a valid limit, we should retrieve the configured value directly and only apply the limit check if maxAllowedTurns >= 0.
const configMax = this.config.getMaxPromptTurns();
const maxAllowedTurns =
this.initialPromptTurns !== undefined &&
this.initialPromptTurns !== MAX_TURNS
? this.initialPromptTurns
: configMax;
if (maxAllowedTurns >= 0 && this.promptTurnCount > maxAllowedTurns) {
yield { type: GeminiEventType.MaxPromptTurns };
return new Turn(this.getChat(), prompt_id);
}
Summary
This PR implements a strict recursive reasoning turn limit of 15 turns per single user request (customizable if
maxSessionTurnsis configured to a positive value) in the core agent reasoning engine. This protects user local CPU resources and model API quotas/credits from infinite loop exploits caused by malicious workspace files with indirect prompt injection.Details
promptTurnCounttoGeminiClientwhich tracks recursive turn depth scoped precisely to the uniqueprompt_idof the user's single query.0whenever a new prompt is initiated (transitioning to a newprompt_id).maxSessionTurnsis set by the user to a positive integer, that limit is honored; otherwise, it defaults to a safe and robust limit of 15 turns.MaxSessionTurnsevent and aborts recursive processing, allowing the CLI layers to gracefully terminate execution.packages/core/src/core/client.test.ts.Related Issues
Resolves the infinite loop issue described in
issue.md.How to Validate
npm test -w @google/gemini-cli-core -- src/core/client.test.tsPre-Merge Checklist