From efaaed9f7381f7d9d1d027193b870cf0596adb4e Mon Sep 17 00:00:00 2001 From: MuriloFP Date: Thu, 3 Jul 2025 15:26:24 -0300 Subject: [PATCH 1/5] feat: add Issue Fixer Orchestrator mode --- .../1_Workflow.xml | 402 +++++++++++++----- .../2_best_practices.xml | 31 +- .roomodes | 3 +- 3 files changed, 315 insertions(+), 121 deletions(-) diff --git a/.roo/rules-issue-fixer-orchestrator/1_Workflow.xml b/.roo/rules-issue-fixer-orchestrator/1_Workflow.xml index 2ad4bf65fc7..3e6619993ec 100644 --- a/.roo/rules-issue-fixer-orchestrator/1_Workflow.xml +++ b/.roo/rules-issue-fixer-orchestrator/1_Workflow.xml @@ -27,7 +27,7 @@ Delegate: Analyze Requirements & Explore Codebase - Launch a subtask in `code` mode to perform a detailed analysis of the issue and the codebase. The subtask will be responsible for identifying affected files and creating an implementation plan. + Launch a subtask in `architect` mode to perform a detailed analysis of the issue and the codebase. The subtask will be responsible for identifying affected files and creating an implementation plan. The context file `.roo/temp/issue-fixer-orchestrator/[TASK_ID]/issue_context.json` will be the input for this subtask. The subtask should write its findings (the implementation plan) to a new file: `.roo/temp/issue-fixer-orchestrator/[TASK_ID]/implementation_plan.md`. @@ -36,19 +36,50 @@ **Task: Analyze Issue and Create Implementation Plan** - You are an expert software architect. Your task is to analyze the provided GitHub issue and the current codebase to create a detailed implementation plan. + You are an expert software architect. Your task is to analyze the provided GitHub issue and the current codebase to create a detailed implementation plan with a focus on understanding component interactions and dependencies. 1. **Read Issue Context**: The full issue details and comments are in `.roo/temp/issue-fixer-orchestrator/[TASK_ID]/issue_context.json`. Read this file to understand all requirements, acceptance criteria, and technical discussions. - 2. **Explore Codebase**: Use `codebase_search`, `read_file`, and other tools to explore the codebase. Identify all files that will need to be modified or created to address the issue. Analyze existing patterns and conventions. - - 3. **Create Implementation Plan**: Based on your analysis, create a comprehensive implementation plan. The plan should be detailed enough for another developer to execute. It must include: - - A summary of the issue and the proposed solution. - - A list of all files to be created or modified. - - A step-by-step guide for the code changes required in each file. - - A plan for writing or updating tests. - - 4. **Save the Plan**: Write the complete implementation plan to `.roo/temp/issue-fixer-orchestrator/[TASK_ID]/implementation_plan.md`. + 2. **Perform Architectural Analysis**: + - **Map Component Interactions**: Trace the complete data flow from entry points to outputs + - **Identify Paired Operations**: For any operation (e.g., export), find its counterpart (e.g., import) + - **Find Similar Patterns**: Search for existing implementations of similar features + - **Analyze Dependencies**: Identify all consumers of the functionality being modified + - **Assess Impact**: Determine how changes will affect other parts of the system + + 3. **Explore Codebase Systematically**: + - Use `codebase_search` FIRST to find all related functionality + - Search for paired operations (if modifying export, search for import) + - Find all files that consume or depend on the affected functionality + - Identify configuration files, tests, and documentation that need updates + - Study similar features to understand established patterns + + 4. **Create Comprehensive Implementation Plan**: The plan must include: + - **Issue Summary**: Clear description of the problem and proposed solution + - **Architectural Context**: + - Data flow diagram showing component interactions + - List of paired operations that must be updated together + - Dependencies and consumers of the affected functionality + - **Impact Analysis**: + - All files that will be affected (directly and indirectly) + - Potential breaking changes + - Performance implications + - **Implementation Steps**: + - Detailed, ordered steps for each file modification + - Specific code changes with context + - Validation and error handling requirements + - **Testing Strategy**: + - Unit tests for individual components + - Integration tests for component interactions + - Edge cases and error scenarios + + 5. **Save the Plan**: Write the complete implementation plan to `.roo/temp/issue-fixer-orchestrator/[TASK_ID]/implementation_plan.md`. + + **Critical Requirements:** + - Always search for and analyze paired operations (import/export, save/load, etc.) + - Map the complete data flow before proposing changes + - Identify all integration points and dependencies + - Consider backward compatibility and migration needs **Completion Protocol:** - This is your only task. Do not deviate from these instructions. @@ -110,19 +141,43 @@ **Task: Implement Code Changes Based on Plan** - You are an expert software developer. Your task is to implement the code changes exactly as described in the provided implementation plan. + You are an expert software developer. Your task is to implement the code changes with full awareness of system interactions and dependencies. - 1. **Read the Plan**: The implementation plan is located at `.roo/temp/issue-fixer-orchestrator/[TASK_ID]/implementation_plan.md`. Follow its instructions carefully. + 1. **Read the Plan**: The implementation plan is located at `.roo/temp/issue-fixer-orchestrator/[TASK_ID]/implementation_plan.md`. Pay special attention to: + - The architectural context section + - Component interaction diagrams + - Identified dependencies and related operations + - Impact analysis - 2. **Implement Changes**: Use `apply_diff` and `write_to_file` to make the specified code changes. Adhere to all coding standards and patterns mentioned in the plan. + 2. **Validate Understanding**: Before coding, ensure you understand: + - How data flows through the system + - All related operations that must be updated together + - Dependencies that could be affected + - Integration points with other components - 3. **Implement Tests**: Write new unit and integration tests as specified in the plan to ensure quality and prevent regressions. + 3. **Implement Holistically**: + - **Update Related Operations Together**: If modifying one operation, update all related operations + - **Maintain Consistency**: Ensure data structures, validation, and error handling are consistent + - **Consider Side Effects**: Account for how changes propagate through the system + - **Follow Existing Patterns**: Use established patterns from similar features - 4. **Track Modified Files**: As you modify or create files, keep a running list. + 4. **Implement Tests**: + - Write tests that verify component interactions + - Test related operations together + - Include edge cases and error scenarios + - Verify data consistency across operations - 5. **Save Modified Files List**: After all changes are implemented and tested, save the list of all file paths you created or modified to `.roo/temp/issue-fixer-orchestrator/[TASK_ID]/modified_files.json`. The format should be a JSON array of strings. + 5. **Track Modified Files**: As you modify or create files, keep a running list. + + 6. **Save Modified Files List**: After all changes are implemented and tested, save the list of all file paths you created or modified to `.roo/temp/issue-fixer-orchestrator/[TASK_ID]/modified_files.json`. The format should be a JSON array of strings. Example: `["src/components/NewFeature.tsx", "src/__tests__/NewFeature.spec.ts"]` + **Critical Reminders:** + - Never implement changes in isolation - consider the full system impact + - Always update related operations together to maintain consistency + - Test component interactions, not just individual functions + - Follow the architectural analysis from the planning phase + Once the `modified_files.json` file is saved, your task is complete. @@ -325,122 +380,233 @@ - Create Pull Request + Delegate: Review Changes Before PR - This is the final step where the orchestrator takes all the prepared materials and creates the pull request. + Before creating the pull request, delegate to the PR reviewer mode to get feedback on the implementation and proposed changes. - 1. **Read PR Summary**: - - - - .roo/temp/issue-fixer-orchestrator/[TASK_ID]/pr_summary.json - - - - - 2. **Get Final Approval**: Present the PR title and body to the user for final approval, providing an option to request changes. + + pr-reviewer + + **Task: Review Implementation Before PR Creation** - - - I have prepared the pull request. Please review and confirm. + You are an expert code reviewer. Your task is to review the implementation for issue #[issue-number] and provide feedback before a pull request is created. - **Title**: [Insert title from pr_summary.json] + **Context Files:** + - **Issue Details**: `.roo/temp/issue-fixer-orchestrator/[TASK_ID]/issue_context.json` + - **Implementation Plan**: `.roo/temp/issue-fixer-orchestrator/[TASK_ID]/implementation_plan.md` + - **Modified Files**: `.roo/temp/issue-fixer-orchestrator/[TASK_ID]/modified_files.json` + - **Verification Results**: `.roo/temp/issue-fixer-orchestrator/[TASK_ID]/verification_results.md` + - **Translation Summary** (if exists): `.roo/temp/issue-fixer-orchestrator/[TASK_ID]/translation_summary.md` + - **Draft PR Summary**: `.roo/temp/issue-fixer-orchestrator/[TASK_ID]/pr_summary.json` + + **Your Review Focus:** + 1. **Code Quality**: Review the actual code changes for: + - Adherence to project coding standards + - Proper error handling and edge cases + - Performance considerations + - Security implications + - Maintainability and readability + + 2. **Implementation Completeness**: Verify that: + - All requirements from the issue are addressed + - The solution follows the implementation plan + - No critical functionality is missing + - Proper test coverage exists + + 3. **Integration Concerns**: Check for: + - Potential breaking changes + - Impact on other parts of the system + - Backward compatibility issues + - API consistency + + 4. **Documentation and Communication**: Assess: + - Code comments and documentation + - PR description clarity and completeness + - Translation handling (if applicable) - **Body**: - --- - [Insert body from pr_summary.json] - --- + **Your Task:** + 1. Read all context files to understand the issue and implementation + 2. Review each modified file listed in `modified_files.json` + 3. Analyze the code changes against the requirements + 4. Identify any issues, improvements, or concerns + 5. Create a comprehensive review report with specific, actionable feedback + 6. Save your review to `.roo/temp/issue-fixer-orchestrator/[TASK_ID]/pr_review_feedback.md` + + **Review Report Format:** + ```markdown + # PR Review Feedback for Issue #[issue-number] + + ## Overall Assessment + [High-level assessment: APPROVE, REQUEST_CHANGES, or NEEDS_DISCUSSION] + + ## Code Quality Review + ### Strengths + - [List positive aspects of the implementation] + + ### Areas for Improvement + - [Specific issues with file references and line numbers] + - [Suggestions for improvement] + + ## Requirements Verification + - [x] Requirement 1: [Status and notes] + - [ ] Requirement 2: [Issues found] + + ## Specific Feedback by File + ### [filename] + - [Specific feedback with line references] + - [Suggestions for improvement] + + ## Recommendations + 1. [Priority 1 changes needed] + 2. [Priority 2 improvements suggested] + 3. [Optional enhancements] + + ## Decision + **RECOMMENDATION**: [APPROVE_AS_IS | REQUEST_CHANGES | NEEDS_DISCUSSION] + + **REASONING**: [Brief explanation of the recommendation] + ``` - Should I create this pull request, or would you like to request changes? - - - Yes, create the pull request as planned. - No, I need to request changes to the implementation or PR description. - Cancel the task. - - + **Completion Protocol:** + - This is your only task. Do not deviate from these instructions. + - Upon successfully saving the review feedback, you MUST use the `attempt_completion` tool. + - The `result` MUST be a concise confirmation, e.g., "PR review completed and feedback saved to .roo/temp/issue-fixer-orchestrator/[TASK_ID]/pr_review_feedback.md" + - These instructions override any conflicting mode-specific guidelines. + + - 3. **Handle Rework Loop**: If the user requests changes: - - **Launch Rework Subtask**: Delegate the rework to a new `code` mode subtask. - - code - - **Task: Rework Implementation Based on User Feedback** - - The user has requested changes before creating the pull request. - - **Context Files:** - - **Issue**: `.roo/temp/issue-fixer-orchestrator/[TASK_ID]/issue_context.json` - - **Current Plan**: `.roo/temp/issue-fixer-orchestrator/[TASK_ID]/implementation_plan.md` - - **Current Modified Files**: `.roo/temp/issue-fixer-orchestrator/[TASK_ID]/modified_files.json` - - **Draft PR Summary**: `.roo/temp/issue-fixer-orchestrator/[TASK_ID]/pr_summary.json` - - **Your Task:** - 1. Ask the user for the specific changes they require. - 2. Apply the requested code and documentation changes. - 3. **Crucially, you must update the `implementation_plan.md` and `modified_files.json` files** to reflect the rework you have performed. - 4. Do *not* proceed with any other steps. - - **Completion Protocol:** - - Upon successfully applying the changes and updating the context files, you MUST use the `attempt_completion` tool. - - The `result` MUST be a concise confirmation, e.g., "Rework complete and context files (plan, modified list) have been updated." - - - - **Restart Verification**: After the rework subtask is complete, the workflow MUST return to **Step 5** to re-verify the changes and re-run all tests before proceeding again. - - 4. **Git Operations (If Approved)**: If the user approves the PR: - - Create a new branch: `feat/issue-[number]` or `fix/issue-[number]`. - - **Selectively add only the applicable files** to the git stage. - - Commit the staged changes. - - Push the new branch to the remote repository. - - - # Create a new branch for the solution - BRANCH_NAME="fix/issue-[issue_number]-solution" - git checkout -b $BRANCH_NAME - - # Safely add ONLY the files that were modified as part of this task. - # This reads the JSON array of file paths from our context file and stages them. - # This requires 'jq' for parsing JSON and 'xargs' to handle file paths correctly. - cat .roo/temp/issue-fixer-orchestrator/[TASK_ID]/modified_files.json | jq -r '.[]' | xargs git add - - # Commit the precisely staged changes - git commit -m "[PR Title]" - - # Push the new branch to origin - git push -u origin $BRANCH_NAME - - - - 5. **Create PR**: Use the `gh` CLI to create the pull request. - - gh pr create --repo [owner]/[repo] --base main --title "[PR Title from JSON]" --body "[PR Body from JSON]" - - - 6. **Link to Issue**: Comment on the original issue with the PR link. - - gh issue comment [issue_number] --repo [owner]/[repo] --body "PR #[new PR number] has been created." - + After the review subtask completes, read and process the feedback. - Monitor PR Checks and Cleanup + Process Review Feedback and Decide Next Steps - After creating the PR, monitor the CI checks and then clean up the temporary files. + After the PR review is complete, read the feedback and decide whether to make changes or proceed with PR creation. + + 1. **Read Review Feedback**: + + + + .roo/temp/issue-fixer-orchestrator/[TASK_ID]/pr_review_feedback.md + + + + + 2. **Present Feedback to User**: Show the review feedback and ask for direction. + + + The PR review has been completed. Here is the feedback: + + --- + [Insert content of pr_review_feedback.md here] + --- + + Based on this review, how would you like to proceed? + + + Implement the suggested changes before creating the PR + Create the PR as-is, ignoring the review feedback + Discuss specific feedback points before deciding + Cancel the task + + + + 3. **Handle User Decision**: + + **If user chooses to implement changes:** + - Launch a rework subtask to address the review feedback + + code + + **Task: Address PR Review Feedback** + + The PR review has identified areas for improvement. Your task is to address the feedback before creating the pull request. + + **Context Files:** + - **Issue**: `.roo/temp/issue-fixer-orchestrator/[TASK_ID]/issue_context.json` + - **Current Plan**: `.roo/temp/issue-fixer-orchestrator/[TASK_ID]/implementation_plan.md` + - **Current Modified Files**: `.roo/temp/issue-fixer-orchestrator/[TASK_ID]/modified_files.json` + - **Review Feedback**: `.roo/temp/issue-fixer-orchestrator/[TASK_ID]/pr_review_feedback.md` + - **Draft PR Summary**: `.roo/temp/issue-fixer-orchestrator/[TASK_ID]/pr_summary.json` + + **Your Task:** + 1. Read the review feedback carefully + 2. Address each point raised by the reviewer + 3. Make the necessary code changes + 4. Update tests if needed + 5. **Update the `modified_files.json` file** to reflect any new or changed files + 6. **Update the `implementation_plan.md`** if the approach has changed significantly + + **Important Notes:** + - Focus on the specific issues identified in the review + - Maintain the overall solution approach unless the review suggests otherwise + - Ensure all changes are properly tested + - Do not proceed with any other workflow steps + + **Completion Protocol:** + - Upon successfully addressing the feedback and updating context files, you MUST use the `attempt_completion` tool. + - The `result` MUST be a concise confirmation, e.g., "Review feedback addressed and context files updated." + + + - **After rework completion**: Return to **Step 5** (Verify and Test) to re-verify the changes + + **If user chooses to proceed as-is:** + - Continue to the next step (Create Pull Request) + + **If user wants to discuss or cancel:** + - Handle accordingly based on user input + + - 1. **Monitor Checks**: Use `--watch` to monitor CI status in real-time. - - gh pr checks [PR URL or number] --repo [owner]/[repo] --watch - + + Prepare Branch and Present PR Template + + This step prepares the branch and commits, then presents the PR template to the user for confirmation before creating the actual pull request. + + 1. Read Issue Context for Issue Number: + Use read_file to get the issue context from .roo/temp/issue-fixer-orchestrator/[TASK_ID]/issue_context.json + + 2. Git Operations - Create branch and commit changes: + - Create a new branch: feat/issue-[number] or fix/issue-[number] + - Selectively add only the applicable files to the git stage + - Commit the staged changes + - Push the new branch to the remote repository + + Use execute_command with: + BRANCH_NAME="fix/issue-[issue_number]-solution" + git checkout -b $BRANCH_NAME + cat .roo/temp/issue-fixer-orchestrator/[TASK_ID]/modified_files.json | jq -r '.[]' | xargs git add + git commit -m "[PR Title]" + git push -u origin $BRANCH_NAME + + 3. Present PR Template - Instead of creating the PR automatically, present the standardized PR template to the user: + Use ask_followup_question to ask: "The branch has been created and changes have been committed. I have prepared a standardized PR template for this issue. Would you like me to create the pull request using the standard Roo Code PR template, or would you prefer to make changes first?" + + Provide these options: + - Yes, create the pull request with the standard template + - No, I want to make changes to the implementation first + - No, I want to customize the PR template before creating it + - Cancel the task + + 4. Handle User Decision: + If user chooses to create the PR: Use gh CLI to create the pull request with the standard template + If user chooses to make changes: Launch a rework subtask using new_task with code mode + If user wants to customize the template: Ask for their preferred PR title and body + + 5. Link to Issue - After PR creation, comment on the original issue with the PR link using gh issue comment + + - 2. **Report Status**: Inform the user of the final status of the checks. + + Monitor PR Checks and Cleanup + + After creating the PR (if created), monitor the CI checks and then clean up the temporary files. - 3. **Cleanup**: Remove the temporary task directory. - - rm -rf .roo/temp/issue-fixer-orchestrator/[TASK_ID] - - + 1. Monitor Checks - Use gh pr checks with --watch to monitor CI status in real-time + 2. Report Status - Inform the user of the final status of the checks + 3. Cleanup - Remove the temporary task directory using rm -rf .roo/temp/issue-fixer-orchestrator/[TASK_ID] + This concludes the orchestration workflow. diff --git a/.roo/rules-issue-fixer-orchestrator/2_best_practices.xml b/.roo/rules-issue-fixer-orchestrator/2_best_practices.xml index e6251d67b23..b9a9b52db78 100644 --- a/.roo/rules-issue-fixer-orchestrator/2_best_practices.xml +++ b/.roo/rules-issue-fixer-orchestrator/2_best_practices.xml @@ -27,6 +27,17 @@ Always use `codebase_search` FIRST to understand the codebase structure and find all related files before using other tools like `read_file`. + + Critical: Understand Component Interactions + + Map the complete data flow from input to output + Identify ALL paired operations (import/export, save/load, encode/decode) + Find all consumers and dependencies of the affected code + Trace how data transformations occur throughout the system + Understand error propagation and handling patterns + + + Investigation Checklist for Bug Fixes Search for the specific error message or broken functionality. @@ -34,6 +45,8 @@ Locate related test files to understand expected behavior. Identify all dependencies and import/export patterns for the affected code. Find similar, working patterns in the codebase to use as a reference. + **CRITICAL**: For any operation being fixed, find and analyze its paired operations + Trace the complete data flow to understand all affected components @@ -42,10 +55,26 @@ Find potential integration points (e.g., API routes, UI component registries). Locate relevant configuration files that may need to be updated. Identify common patterns, components, and utilities that should be reused. + **CRITICAL**: Design paired operations together (e.g., both import AND export) + Map all data transformations and state changes + Identify all downstream consumers of the new functionality + + Always Implement Paired Operations Together + + When fixing export, ALWAYS check and update import + When modifying save, ALWAYS verify load handles the changes + When changing serialization, ALWAYS update deserialization + When updating create, consider read/update/delete operations + + + Paired operations must maintain consistency. Changes to one without the other leads to data corruption, import failures, or broken functionality. + + + - Always read multiple related files together to understand the full context, including coding conventions, testing patterns, and error handling approaches. + Always read multiple related files together to understand the full context. Never assume a change is isolated - trace its impact through the entire system. \ No newline at end of file diff --git a/.roomodes b/.roomodes index 3b178f234fa..213d7b8314a 100644 --- a/.roomodes +++ b/.roomodes @@ -9,7 +9,7 @@ customModes: - Ensuring modes have appropriate tool group permissions - Crafting clear whenToUse descriptions for the Orchestrator - Following XML structuring best practices for clarity and parseability - + You help users create new modes by: - Gathering requirements about the mode's purpose and workflow - Defining appropriate roleDefinition and whenToUse descriptions @@ -182,4 +182,3 @@ customModes: - edit - command source: project - description: Issue Fixer mode ported into an orchestrator From 5e28a1035da0a58db0875a1f4d4047b11c95648e Mon Sep 17 00:00:00 2001 From: MuriloFP Date: Tue, 15 Jul 2025 12:45:48 -0300 Subject: [PATCH 2/5] feat: add configurable consecutive mistake limit (#5654) --- packages/types/src/provider-settings.ts | 6 +++ src/core/config/ProviderSettingsManager.ts | 22 ++++++++ .../__tests__/ProviderSettingsManager.spec.ts | 42 ++++++++++++++++ src/core/task/Task.ts | 9 ++-- src/core/task/__tests__/Task.spec.ts | 50 +++++++++++++++++++ src/core/webview/ClineProvider.ts | 2 + .../src/components/settings/ApiOptions.tsx | 5 ++ .../ConsecutiveMistakeLimitControl.tsx | 48 ++++++++++++++++++ webview-ui/src/i18n/locales/ca/settings.json | 6 +++ webview-ui/src/i18n/locales/de/settings.json | 6 +++ webview-ui/src/i18n/locales/en/settings.json | 6 +++ webview-ui/src/i18n/locales/es/settings.json | 6 +++ webview-ui/src/i18n/locales/fr/settings.json | 6 +++ webview-ui/src/i18n/locales/hi/settings.json | 6 +++ webview-ui/src/i18n/locales/id/settings.json | 6 +++ webview-ui/src/i18n/locales/it/settings.json | 6 +++ webview-ui/src/i18n/locales/ja/settings.json | 6 +++ webview-ui/src/i18n/locales/ko/settings.json | 6 +++ webview-ui/src/i18n/locales/nl/settings.json | 6 +++ webview-ui/src/i18n/locales/pl/settings.json | 6 +++ .../src/i18n/locales/pt-BR/settings.json | 6 +++ webview-ui/src/i18n/locales/ru/settings.json | 6 +++ webview-ui/src/i18n/locales/tr/settings.json | 6 +++ webview-ui/src/i18n/locales/vi/settings.json | 6 +++ .../src/i18n/locales/zh-CN/settings.json | 6 +++ .../src/i18n/locales/zh-TW/settings.json | 6 +++ 26 files changed, 289 insertions(+), 3 deletions(-) create mode 100644 webview-ui/src/components/settings/ConsecutiveMistakeLimitControl.tsx diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index 4cf4b30972f..3b53627295e 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -53,12 +53,18 @@ export type ProviderSettingsEntry = z.infer * ProviderSettings */ +/** + * Default value for consecutive mistake limit + */ +export const DEFAULT_CONSECUTIVE_MISTAKE_LIMIT = 3 + const baseProviderSettingsSchema = z.object({ includeMaxTokens: z.boolean().optional(), diffEnabled: z.boolean().optional(), fuzzyMatchThreshold: z.number().optional(), modelTemperature: z.number().nullish(), rateLimitSeconds: z.number().optional(), + consecutiveMistakeLimit: z.number().min(0).optional(), // Model reasoning. enableReasoningEffort: z.boolean().optional(), diff --git a/src/core/config/ProviderSettingsManager.ts b/src/core/config/ProviderSettingsManager.ts index 32c0135d3b6..004dfd346cc 100644 --- a/src/core/config/ProviderSettingsManager.ts +++ b/src/core/config/ProviderSettingsManager.ts @@ -5,6 +5,7 @@ import { type ProviderSettingsEntry, providerSettingsSchema, providerSettingsSchemaDiscriminated, + DEFAULT_CONSECUTIVE_MISTAKE_LIMIT, } from "@roo-code/types" import { TelemetryService } from "@roo-code/telemetry" @@ -26,6 +27,7 @@ export const providerProfilesSchema = z.object({ rateLimitSecondsMigrated: z.boolean().optional(), diffSettingsMigrated: z.boolean().optional(), openAiHeadersMigrated: z.boolean().optional(), + consecutiveMistakeLimitMigrated: z.boolean().optional(), }) .optional(), }) @@ -48,6 +50,7 @@ export class ProviderSettingsManager { rateLimitSecondsMigrated: true, // Mark as migrated on fresh installs diffSettingsMigrated: true, // Mark as migrated on fresh installs openAiHeadersMigrated: true, // Mark as migrated on fresh installs + consecutiveMistakeLimitMigrated: true, // Mark as migrated on fresh installs }, } @@ -113,6 +116,7 @@ export class ProviderSettingsManager { rateLimitSecondsMigrated: false, diffSettingsMigrated: false, openAiHeadersMigrated: false, + consecutiveMistakeLimitMigrated: false, } // Initialize with default values isDirty = true } @@ -135,6 +139,12 @@ export class ProviderSettingsManager { isDirty = true } + if (!providerProfiles.migrations.consecutiveMistakeLimitMigrated) { + await this.migrateConsecutiveMistakeLimit(providerProfiles) + providerProfiles.migrations.consecutiveMistakeLimitMigrated = true + isDirty = true + } + if (isDirty) { await this.store(providerProfiles) } @@ -228,6 +238,18 @@ export class ProviderSettingsManager { } } + private async migrateConsecutiveMistakeLimit(providerProfiles: ProviderProfiles) { + try { + for (const [_name, apiConfig] of Object.entries(providerProfiles.apiConfigs)) { + if (apiConfig.consecutiveMistakeLimit == null) { + apiConfig.consecutiveMistakeLimit = DEFAULT_CONSECUTIVE_MISTAKE_LIMIT + } + } + } catch (error) { + console.error(`[MigrateConsecutiveMistakeLimit] Failed to migrate consecutive mistake limit:`, error) + } + } + /** * List all available configs with metadata. */ diff --git a/src/core/config/__tests__/ProviderSettingsManager.spec.ts b/src/core/config/__tests__/ProviderSettingsManager.spec.ts index 6c37d733c47..6d24c631016 100644 --- a/src/core/config/__tests__/ProviderSettingsManager.spec.ts +++ b/src/core/config/__tests__/ProviderSettingsManager.spec.ts @@ -66,6 +66,7 @@ describe("ProviderSettingsManager", () => { rateLimitSecondsMigrated: true, diffSettingsMigrated: true, openAiHeadersMigrated: true, + consecutiveMistakeLimitMigrated: true, }, }), ) @@ -144,6 +145,47 @@ describe("ProviderSettingsManager", () => { expect(storedConfig.apiConfigs.existing.rateLimitSeconds).toEqual(43) }) + it("should call migrateConsecutiveMistakeLimit if it has not done so already", async () => { + mockSecrets.get.mockResolvedValue( + JSON.stringify({ + currentApiConfigName: "default", + apiConfigs: { + default: { + config: {}, + id: "default", + consecutiveMistakeLimit: undefined, + }, + test: { + apiProvider: "anthropic", + consecutiveMistakeLimit: undefined, + }, + existing: { + apiProvider: "anthropic", + // this should not really be possible, unless someone has loaded a hand edited config, + // but we don't overwrite so we'll check that + consecutiveMistakeLimit: 5, + }, + }, + migrations: { + rateLimitSecondsMigrated: true, + diffSettingsMigrated: true, + openAiHeadersMigrated: true, + consecutiveMistakeLimitMigrated: false, + }, + }), + ) + + await providerSettingsManager.initialize() + + // Get the last call to store, which should contain the migrated config + const calls = mockSecrets.store.mock.calls + const storedConfig = JSON.parse(calls[calls.length - 1][1]) + expect(storedConfig.apiConfigs.default.consecutiveMistakeLimit).toEqual(3) + expect(storedConfig.apiConfigs.test.consecutiveMistakeLimit).toEqual(3) + expect(storedConfig.apiConfigs.existing.consecutiveMistakeLimit).toEqual(5) + expect(storedConfig.migrations.consecutiveMistakeLimitMigrated).toEqual(true) + }) + it("should throw error if secrets storage fails", async () => { mockSecrets.get.mockRejectedValue(new Error("Storage failed")) diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index aa0590fedd9..3e06588e3db 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -18,6 +18,7 @@ import { type ClineMessage, type ClineSay, type ToolProgressStatus, + DEFAULT_CONSECUTIVE_MISTAKE_LIMIT, type HistoryItem, TelemetryEventName, TodoItem, @@ -216,7 +217,7 @@ export class Task extends EventEmitter { enableDiff = false, enableCheckpoints = true, fuzzyMatchThreshold = 1.0, - consecutiveMistakeLimit = 3, + consecutiveMistakeLimit = DEFAULT_CONSECUTIVE_MISTAKE_LIMIT, task, images, historyItem, @@ -255,7 +256,7 @@ export class Task extends EventEmitter { this.browserSession = new BrowserSession(provider.context) this.diffEnabled = enableDiff this.fuzzyMatchThreshold = fuzzyMatchThreshold - this.consecutiveMistakeLimit = consecutiveMistakeLimit + this.consecutiveMistakeLimit = consecutiveMistakeLimit === 0 ? Infinity : (consecutiveMistakeLimit ?? 3) this.providerRef = new WeakRef(provider) this.globalStoragePath = provider.context.globalStorageUri.fsPath this.diffViewProvider = new DiffViewProvider(this.cwd) @@ -289,7 +290,9 @@ export class Task extends EventEmitter { }) } - this.toolRepetitionDetector = new ToolRepetitionDetector(this.consecutiveMistakeLimit) + this.toolRepetitionDetector = new ToolRepetitionDetector( + this.consecutiveMistakeLimit === Infinity ? Number.MAX_SAFE_INTEGER : this.consecutiveMistakeLimit, + ) onCreated?.(this) diff --git a/src/core/task/__tests__/Task.spec.ts b/src/core/task/__tests__/Task.spec.ts index 693f72d1c7b..e6d8a9944e7 100644 --- a/src/core/task/__tests__/Task.spec.ts +++ b/src/core/task/__tests__/Task.spec.ts @@ -320,6 +320,56 @@ describe("Cline", () => { expect(cline.diffStrategy).toBeDefined() }) + it("should use default consecutiveMistakeLimit when not provided", () => { + const cline = new Task({ + provider: mockProvider, + apiConfiguration: mockApiConfig, + task: "test task", + startTask: false, + }) + + expect(cline.consecutiveMistakeLimit).toBe(3) + }) + + it("should respect provided consecutiveMistakeLimit", () => { + const cline = new Task({ + provider: mockProvider, + apiConfiguration: mockApiConfig, + consecutiveMistakeLimit: 5, + task: "test task", + startTask: false, + }) + + expect(cline.consecutiveMistakeLimit).toBe(5) + }) + + it("should convert consecutiveMistakeLimit of 0 to Infinity", () => { + const cline = new Task({ + provider: mockProvider, + apiConfiguration: mockApiConfig, + consecutiveMistakeLimit: 0, + task: "test task", + startTask: false, + }) + + expect(cline.consecutiveMistakeLimit).toBe(Infinity) + }) + + it("should pass correct value to ToolRepetitionDetector when limit is Infinity", () => { + const cline = new Task({ + provider: mockProvider, + apiConfiguration: mockApiConfig, + consecutiveMistakeLimit: 0, + task: "test task", + startTask: false, + }) + + // The toolRepetitionDetector should be initialized with MAX_SAFE_INTEGER when limit is Infinity + expect(cline.toolRepetitionDetector).toBeDefined() + // We can't directly check the internal state, but we can verify the limit was converted + expect(cline.consecutiveMistakeLimit).toBe(Infinity) + }) + it("should require either task or historyItem", () => { expect(() => { new Task({ provider: mockProvider, apiConfiguration: mockApiConfig }) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 8fa9ceccfa6..32b9276ba8d 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -553,6 +553,7 @@ export class ClineProvider enableDiff, enableCheckpoints, fuzzyMatchThreshold, + consecutiveMistakeLimit: apiConfiguration.consecutiveMistakeLimit, task, images, experiments, @@ -589,6 +590,7 @@ export class ClineProvider enableDiff, enableCheckpoints, fuzzyMatchThreshold, + consecutiveMistakeLimit: apiConfiguration.consecutiveMistakeLimit, historyItem, experiments, rootTask: historyItem.rootTask, diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 5f869290439..a19279cb9f2 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -64,6 +64,7 @@ import { ThinkingBudget } from "./ThinkingBudget" import { DiffSettingsControl } from "./DiffSettingsControl" import { TemperatureControl } from "./TemperatureControl" import { RateLimitSecondsControl } from "./RateLimitSecondsControl" +import { ConsecutiveMistakeLimitControl } from "./ConsecutiveMistakeLimitControl" import { BedrockCustomArn } from "./providers/BedrockCustomArn" import { buildDocLink } from "@src/utils/docLinks" @@ -547,6 +548,10 @@ const ApiOptions = ({ value={apiConfiguration.rateLimitSeconds || 0} onChange={(value) => setApiConfigurationField("rateLimitSeconds", value)} /> + setApiConfigurationField("consecutiveMistakeLimit", value)} + /> )} diff --git a/webview-ui/src/components/settings/ConsecutiveMistakeLimitControl.tsx b/webview-ui/src/components/settings/ConsecutiveMistakeLimitControl.tsx new file mode 100644 index 00000000000..342f45d6d30 --- /dev/null +++ b/webview-ui/src/components/settings/ConsecutiveMistakeLimitControl.tsx @@ -0,0 +1,48 @@ +import React, { useCallback } from "react" +import { Slider } from "@/components/ui" +import { useAppTranslation } from "@/i18n/TranslationContext" +import { DEFAULT_CONSECUTIVE_MISTAKE_LIMIT } from "@roo-code/types" + +interface ConsecutiveMistakeLimitControlProps { + value: number + onChange: (value: number) => void +} + +export const ConsecutiveMistakeLimitControl: React.FC = ({ value, onChange }) => { + const { t } = useAppTranslation() + + const handleValueChange = useCallback( + (newValue: number) => { + onChange(newValue) + }, + [onChange], + ) + + return ( +
+ +
+ handleValueChange(newValue[0])} + /> + {value ?? DEFAULT_CONSECUTIVE_MISTAKE_LIMIT} +
+
+ {value === 0 + ? t("settings:providers.consecutiveMistakeLimit.unlimitedDescription") + : t("settings:providers.consecutiveMistakeLimit.description", { + value: value ?? DEFAULT_CONSECUTIVE_MISTAKE_LIMIT, + })} +
+ {value === 0 && ( +
+ {t("settings:providers.consecutiveMistakeLimit.warning")} +
+ )} +
+ ) +} diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index 0dd08afb29d..81a37e115f5 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -367,6 +367,12 @@ "label": "Límit de freqüència", "description": "Temps mínim entre sol·licituds d'API." }, + "consecutiveMistakeLimit": { + "label": "Límit d'errors consecutius", + "description": "Nombre d'errors consecutius abans de mostrar el diàleg 'Continua igualment'. Actual: {{value}}", + "unlimitedDescription": "Reintents il·limitats activats (continuació automàtica). El diàleg no apareixerà mai.", + "warning": "⚠️ Establir-ho a 0 permet reintents il·limitats que poden consumir un ús significatiu de l'API" + }, "reasoningEffort": { "label": "Esforç de raonament del model", "high": "Alt", diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index 923475d3fd7..be690561423 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -367,6 +367,12 @@ "label": "Ratenbegrenzung", "description": "Minimale Zeit zwischen API-Anfragen." }, + "consecutiveMistakeLimit": { + "label": "Limit für aufeinanderfolgende Fehler", + "description": "Anzahl der aufeinanderfolgenden Fehler, bevor der Dialog 'Trotzdem fortfahren' angezeigt wird. Aktuell: {{value}}", + "unlimitedDescription": "Unbegrenzte Wiederholungen aktiviert (automatisches Fortfahren). Der Dialog wird nie angezeigt.", + "warning": "⚠️ Das Setzen auf 0 erlaubt unbegrenzte Wiederholungen, was zu einem erheblichen API-Verbrauch führen kann" + }, "reasoningEffort": { "label": "Modell-Denkaufwand", "high": "Hoch", diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 25428cfb16c..f8211fdfe95 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -367,6 +367,12 @@ "label": "Rate limit", "description": "Minimum time between API requests." }, + "consecutiveMistakeLimit": { + "label": "Consecutive Mistake Limit", + "description": "Number of consecutive mistakes before showing 'Proceed Anyways' dialog. Current: {{value}}", + "unlimitedDescription": "Unlimited retries enabled (auto-proceed). The dialog will never appear.", + "warning": "⚠️ Setting to 0 allows unlimited retries which may consume significant API usage" + }, "reasoningEffort": { "label": "Model Reasoning Effort", "high": "High", diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index 7f6af9bd72b..89455746ecb 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -367,6 +367,12 @@ "label": "Límite de tasa", "description": "Tiempo mínimo entre solicitudes de API." }, + "consecutiveMistakeLimit": { + "label": "Límite de errores consecutivos", + "description": "Número de errores consecutivos antes de mostrar el diálogo 'Proceder de todos modos'. Actual: {{value}}", + "unlimitedDescription": "Reintentos ilimitados habilitados (proceder automáticamente). El diálogo nunca aparecerá.", + "warning": "⚠️ Establecer el valor en 0 permite reintentos ilimitados que pueden consumir un uso significativo de la API" + }, "reasoningEffort": { "label": "Esfuerzo de razonamiento del modelo", "high": "Alto", diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index 5986a5bf17c..4de79703f20 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -367,6 +367,12 @@ "label": "Limite de débit", "description": "Temps minimum entre les requêtes API." }, + "consecutiveMistakeLimit": { + "label": "Limite d'erreurs consécutives", + "description": "Nombre d'erreurs consécutives avant d'afficher la boîte de dialogue 'Continuer quand même'. Actuel : {{value}}", + "unlimitedDescription": "Nouvelles tentatives illimitées activées (poursuite automatique). La boîte de dialogue n'apparaîtra jamais.", + "warning": "⚠️ Mettre à 0 autorise des nouvelles tentatives illimitées, ce qui peut consommer une utilisation importante de l'API" + }, "reasoningEffort": { "label": "Effort de raisonnement du modèle", "high": "Élevé", diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index 0a990645889..7ad8f65fbda 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -367,6 +367,12 @@ "label": "दर सीमा", "description": "API अनुरोधों के बीच न्यूनतम समय।" }, + "consecutiveMistakeLimit": { + "label": "लगातार गलती की सीमा", + "description": "'वैसे भी आगे बढ़ें' डायलॉग दिखाने से पहले लगातार गलतियों की संख्या। वर्तमान: {{value}}", + "unlimitedDescription": "असीमित पुन: प्रयास सक्षम (ऑटो-प्रोसीड)। डायलॉग कभी नहीं दिखाई देगा।", + "warning": "⚠️ 0 पर सेट करने से असीमित पुन: प्रयास की अनुमति मिलती है जिससे महत्वपूर्ण एपीआई उपयोग हो सकता है" + }, "reasoningEffort": { "label": "मॉडल तर्क प्रयास", "high": "उच्च", diff --git a/webview-ui/src/i18n/locales/id/settings.json b/webview-ui/src/i18n/locales/id/settings.json index 9ae5dd846c4..994a5091a42 100644 --- a/webview-ui/src/i18n/locales/id/settings.json +++ b/webview-ui/src/i18n/locales/id/settings.json @@ -371,6 +371,12 @@ "label": "Rate limit", "description": "Waktu minimum antara permintaan API." }, + "consecutiveMistakeLimit": { + "label": "Batas Kesalahan Berturut-turut", + "description": "Jumlah kesalahan berturut-turut sebelum menampilkan dialog 'Lanjutkan Saja'. Saat ini: {{value}}", + "unlimitedDescription": "Coba lagi tanpa batas diaktifkan (lanjutkan otomatis). Dialog tidak akan pernah muncul.", + "warning": "⚠️ Mengatur ke 0 memungkinkan coba lagi tanpa batas yang dapat menghabiskan penggunaan API yang signifikan" + }, "reasoningEffort": { "label": "Upaya Reasoning Model", "high": "Tinggi", diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index a2897496ec6..a05b473d37d 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -367,6 +367,12 @@ "label": "Limite di frequenza", "description": "Tempo minimo tra le richieste API." }, + "consecutiveMistakeLimit": { + "label": "Limite di errori consecutivi", + "description": "Numero di errori consecutivi prima di mostrare la finestra di dialogo 'Procedi comunque'. Attuale: {{value}}", + "unlimitedDescription": "Ripetizioni illimitate abilitate (procedi automaticamente). La finestra di dialogo non apparirà mai.", + "warning": "⚠️ L'impostazione a 0 consente tentativi illimitati che possono consumare un notevole utilizzo dell'API" + }, "reasoningEffort": { "label": "Sforzo di ragionamento del modello", "high": "Alto", diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index bda41828512..fc054b4a549 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -367,6 +367,12 @@ "label": "レート制限", "description": "APIリクエスト間の最小時間。" }, + "consecutiveMistakeLimit": { + "label": "連続ミスの制限", + "description": "「それでも続行」ダイアログを表示するまでの連続ミス数。現在: {{value}}", + "unlimitedDescription": "無制限リトライが有効です(自動進行)。ダイアログは表示されません。", + "warning": "⚠️ 0に設定すると無制限にリトライが許可され、API使用量が大幅に増加する可能性があります" + }, "reasoningEffort": { "label": "モデル推論の労力", "high": "高", diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index 5053ed62f30..b219e684ebc 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -367,6 +367,12 @@ "label": "속도 제한", "description": "API 요청 간 최소 시간." }, + "consecutiveMistakeLimit": { + "label": "연속 실수 제한", + "description": "'계속 진행' 대화 상자를 표시하기 전의 연속 실수 횟수. 현재: {{value}}", + "unlimitedDescription": "무제한 재시도 활성화됨(자동 진행). 대화 상자가 표시되지 않습니다.", + "warning": "⚠️ 0으로 설정하면 무제한 재시도가 허용되어 상당한 API 사용량이 발생할 수 있습니다" + }, "reasoningEffort": { "label": "모델 추론 노력", "high": "높음", diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index ab12e1931c3..a4a08b83e77 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -367,6 +367,12 @@ "label": "Snelheidslimiet", "description": "Minimale tijd tussen API-verzoeken." }, + "consecutiveMistakeLimit": { + "label": "Limiet voor opeenvolgende fouten", + "description": "Aantal opeenvolgende fouten voordat het dialoogvenster 'Toch doorgaan' wordt weergegeven. Huidig: {{value}}", + "unlimitedDescription": "Onbeperkt aantal nieuwe pogingen ingeschakeld (automatisch doorgaan). Het dialoogvenster verschijnt nooit.", + "warning": "⚠️ Instellen op 0 staat onbeperkte nieuwe pogingen toe, wat aanzienlijk API-gebruik kan verbruiken" + }, "reasoningEffort": { "label": "Model redeneervermogen", "high": "Hoog", diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index 266c6686065..61d37375e30 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -367,6 +367,12 @@ "label": "Limit szybkości", "description": "Minimalny czas między żądaniami API." }, + "consecutiveMistakeLimit": { + "label": "Limit kolejnych błędów", + "description": "Liczba kolejnych błędów przed wyświetleniem okna dialogowego 'Kontynuuj mimo to'. Bieżący: {{value}}", + "unlimitedDescription": "Włączono nieograniczone ponowienia (kontynuacja automatyczna). Okno dialogowe nigdy się nie pojawi.", + "warning": "⚠️ Ustawienie na 0 pozwala na nieograniczone ponowienia, co może zużyć znaczną ilość zasobów API" + }, "reasoningEffort": { "label": "Wysiłek rozumowania modelu", "high": "Wysoki", diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index e9825ae749a..cab90bb7db8 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -367,6 +367,12 @@ "label": "Limite de taxa", "description": "Tempo mínimo entre requisições de API." }, + "consecutiveMistakeLimit": { + "label": "Limite de erros consecutivos", + "description": "Número de erros consecutivos antes de exibir a caixa de diálogo 'Prosseguir mesmo assim'. Atual: {{value}}", + "unlimitedDescription": "Tentativas ilimitadas ativadas (prosseguimento automático). A caixa de diálogo nunca aparecerá.", + "warning": "⚠️ Definir como 0 permite tentativas ilimitadas, o que pode consumir um uso significativo da API" + }, "reasoningEffort": { "label": "Esforço de raciocínio do modelo", "high": "Alto", diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index 1b74b2253f6..9a4962d07af 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -367,6 +367,12 @@ "label": "Лимит скорости", "description": "Минимальное время между запросами к API." }, + "consecutiveMistakeLimit": { + "label": "Лимит последовательных ошибок", + "description": "Количество последовательных ошибок перед показом диалогового окна 'Все равно продолжить'. Текущее значение: {{value}}", + "unlimitedDescription": "Включены неограниченные повторные попытки (автоматическое продолжение). Диалоговое окно никогда не появится.", + "warning": "⚠️ Установка значения 0 разрешает неограниченные повторные попытки, что может привести к значительному использованию API" + }, "reasoningEffort": { "label": "Усилия по рассуждению модели", "high": "Высокие", diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index 49d1803ddf9..9d529b5da53 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -367,6 +367,12 @@ "label": "Hız sınırı", "description": "API istekleri arasındaki minimum süre." }, + "consecutiveMistakeLimit": { + "label": "Ardışık Hata Limiti", + "description": "'Yine de Devam Et' iletişim kutusunu göstermeden önceki ardışık hata sayısı. Mevcut: {{value}}", + "unlimitedDescription": "Sınırsız yeniden deneme etkin (otomatik devam et). İletişim kutusu asla görünmez.", + "warning": "⚠️ 0'a ayarlamak, önemli API kullanımına neden olabilecek sınırsız yeniden denemeye izin verir" + }, "reasoningEffort": { "label": "Model Akıl Yürütme Çabası", "high": "Yüksek", diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index c12b5778a2d..cf76aac1dc4 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -367,6 +367,12 @@ "label": "Giới hạn tốc độ", "description": "Thời gian tối thiểu giữa các yêu cầu API." }, + "consecutiveMistakeLimit": { + "label": "Giới hạn lỗi liên tiếp", + "description": "Số lỗi liên tiếp trước khi hiển thị hộp thoại 'Tiếp tục dù sao'. Hiện tại: {{value}}", + "unlimitedDescription": "Đã bật thử lại không giới hạn (tự động tiếp tục). Hộp thoại sẽ không bao giờ xuất hiện.", + "warning": "⚠️ Đặt thành 0 cho phép thử lại không giới hạn, có thể tiêu tốn mức sử dụng API đáng kể" + }, "reasoningEffort": { "label": "Nỗ lực suy luận của mô hình", "high": "Cao", diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index c75578067dc..d86c2e641bd 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -367,6 +367,12 @@ "label": "API 请求频率限制", "description": "设置API请求的最小间隔时间" }, + "consecutiveMistakeLimit": { + "label": "连续错误上限", + "description": "“强制继续”对话框出现前允许的连续错误次数。当前: {{value}}", + "unlimitedDescription": "已启用无限制重试(自动继续),对话框将不再出现。", + "warning": "⚠️ 设置为 0 将允许无限制重试,可能消耗大量 API" + }, "reasoningEffort": { "label": "模型推理强度", "high": "高", diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index dec9e936b24..f7ca5181653 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -367,6 +367,12 @@ "label": "速率限制", "description": "API 請求間的最短時間" }, + "consecutiveMistakeLimit": { + "label": "連續錯誤上限", + "description": "「強制繼續」對話方塊出現前允許的連續錯誤次數。目前: {{value}}", + "unlimitedDescription": "已啟用無限制重試(自動繼續),對話方塊將不再出現。", + "warning": "⚠️ 設定為 0 將允許無限制重試,可能消耗大量 API" + }, "reasoningEffort": { "label": "模型推理強度", "high": "高", From 916875d805fab3c9ad972a49aa1279578572cb79 Mon Sep 17 00:00:00 2001 From: MuriloFP Date: Tue, 15 Jul 2025 13:22:41 -0300 Subject: [PATCH 3/5] fix: separate tool repetition limit from consecutive mistake limit The ToolRepetitionDetector was incorrectly using the consecutiveMistakeLimit, causing the 'Roo is having trouble' dialog to appear for tool repetitions. Tool repetition detection should remain at its default limit of 3, independent of the consecutive mistake limit setting. --- src/core/task/Task.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 3e06588e3db..5dc1d9ada32 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -290,9 +290,7 @@ export class Task extends EventEmitter { }) } - this.toolRepetitionDetector = new ToolRepetitionDetector( - this.consecutiveMistakeLimit === Infinity ? Number.MAX_SAFE_INTEGER : this.consecutiveMistakeLimit, - ) + this.toolRepetitionDetector = new ToolRepetitionDetector(3) onCreated?.(this) From f8b4bf71c15c8e69a3321303c0c4107ac61095c8 Mon Sep 17 00:00:00 2001 From: MuriloFP Date: Tue, 15 Jul 2025 14:37:54 -0300 Subject: [PATCH 4/5] feat: Add configurable error & repetition limit with unified control (#5654) --- src/core/config/ProviderSettingsManager.ts | 6 +++++- src/core/task/Task.ts | 4 +++- src/core/task/__tests__/Task.spec.ts | 16 +++++++++++++++- src/core/tools/ToolRepetitionDetector.ts | 7 +++++-- .../__tests__/ToolRepetitionDetector.spec.ts | 11 +++++++++++ webview-ui/src/i18n/locales/ca/settings.json | 8 ++++---- webview-ui/src/i18n/locales/de/settings.json | 8 ++++---- webview-ui/src/i18n/locales/en/settings.json | 4 ++-- webview-ui/src/i18n/locales/es/settings.json | 6 +++--- webview-ui/src/i18n/locales/fr/settings.json | 8 ++++---- webview-ui/src/i18n/locales/hi/settings.json | 8 ++++---- webview-ui/src/i18n/locales/id/settings.json | 8 ++++---- webview-ui/src/i18n/locales/it/settings.json | 6 +++--- webview-ui/src/i18n/locales/ja/settings.json | 8 ++++---- webview-ui/src/i18n/locales/ko/settings.json | 6 +++--- webview-ui/src/i18n/locales/nl/settings.json | 6 +++--- webview-ui/src/i18n/locales/pl/settings.json | 8 ++++---- webview-ui/src/i18n/locales/pt-BR/settings.json | 6 +++--- webview-ui/src/i18n/locales/ru/settings.json | 6 +++--- webview-ui/src/i18n/locales/tr/settings.json | 6 +++--- webview-ui/src/i18n/locales/vi/settings.json | 6 +++--- webview-ui/src/i18n/locales/zh-CN/settings.json | 8 ++++---- webview-ui/src/i18n/locales/zh-TW/settings.json | 8 ++++---- 23 files changed, 101 insertions(+), 67 deletions(-) diff --git a/src/core/config/ProviderSettingsManager.ts b/src/core/config/ProviderSettingsManager.ts index 004dfd346cc..3f64656706e 100644 --- a/src/core/config/ProviderSettingsManager.ts +++ b/src/core/config/ProviderSettingsManager.ts @@ -240,9 +240,13 @@ export class ProviderSettingsManager { private async migrateConsecutiveMistakeLimit(providerProfiles: ProviderProfiles) { try { - for (const [_name, apiConfig] of Object.entries(providerProfiles.apiConfigs)) { + for (const [name, apiConfig] of Object.entries(providerProfiles.apiConfigs)) { if (apiConfig.consecutiveMistakeLimit == null) { apiConfig.consecutiveMistakeLimit = DEFAULT_CONSECUTIVE_MISTAKE_LIMIT + } else { + console.log( + `[ProviderSettingsManager] Profile ${name} already has consecutiveMistakeLimit: ${apiConfig.consecutiveMistakeLimit}`, + ) } } } catch (error) { diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 5dc1d9ada32..f160141d198 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -290,7 +290,9 @@ export class Task extends EventEmitter { }) } - this.toolRepetitionDetector = new ToolRepetitionDetector(3) + this.toolRepetitionDetector = new ToolRepetitionDetector( + this.consecutiveMistakeLimit === Infinity ? 0 : this.consecutiveMistakeLimit, + ) onCreated?.(this) diff --git a/src/core/task/__tests__/Task.spec.ts b/src/core/task/__tests__/Task.spec.ts index e6d8a9944e7..a245455298d 100644 --- a/src/core/task/__tests__/Task.spec.ts +++ b/src/core/task/__tests__/Task.spec.ts @@ -364,12 +364,26 @@ describe("Cline", () => { startTask: false, }) - // The toolRepetitionDetector should be initialized with MAX_SAFE_INTEGER when limit is Infinity + // The toolRepetitionDetector should be initialized with 0 when limit is Infinity (unlimited) expect(cline.toolRepetitionDetector).toBeDefined() // We can't directly check the internal state, but we can verify the limit was converted expect(cline.consecutiveMistakeLimit).toBe(Infinity) }) + it("should pass consecutiveMistakeLimit to ToolRepetitionDetector", () => { + const cline = new Task({ + provider: mockProvider, + apiConfiguration: mockApiConfig, + consecutiveMistakeLimit: 5, + task: "test task", + startTask: false, + }) + + // The toolRepetitionDetector should be initialized with the same limit + expect(cline.toolRepetitionDetector).toBeDefined() + expect(cline.consecutiveMistakeLimit).toBe(5) + }) + it("should require either task or historyItem", () => { expect(() => { new Task({ provider: mockProvider, apiConfiguration: mockApiConfig }) diff --git a/src/core/tools/ToolRepetitionDetector.ts b/src/core/tools/ToolRepetitionDetector.ts index a82574ba0e3..927b031e3b0 100644 --- a/src/core/tools/ToolRepetitionDetector.ts +++ b/src/core/tools/ToolRepetitionDetector.ts @@ -43,8 +43,11 @@ export class ToolRepetitionDetector { this.previousToolCallJson = currentToolCallJson } - // Check if limit is reached - if (this.consecutiveIdenticalToolCallCount >= this.consecutiveIdenticalToolCallLimit) { + // Check if limit is reached (0 means unlimited) + if ( + this.consecutiveIdenticalToolCallLimit > 0 && + this.consecutiveIdenticalToolCallCount >= this.consecutiveIdenticalToolCallLimit + ) { // Reset counters to allow recovery if user guides the AI past this point this.consecutiveIdenticalToolCallCount = 0 this.previousToolCallJson = null diff --git a/src/core/tools/__tests__/ToolRepetitionDetector.spec.ts b/src/core/tools/__tests__/ToolRepetitionDetector.spec.ts index 972d401141f..35117fd9ba1 100644 --- a/src/core/tools/__tests__/ToolRepetitionDetector.spec.ts +++ b/src/core/tools/__tests__/ToolRepetitionDetector.spec.ts @@ -301,5 +301,16 @@ describe("ToolRepetitionDetector", () => { expect(result3.allowExecution).toBe(false) expect(result3.askUser).toBeDefined() }) + + it("should never block when limit is 0 (unlimited)", () => { + const detector = new ToolRepetitionDetector(0) + + // Try many identical calls + for (let i = 0; i < 10; i++) { + const result = detector.check(createToolUse("tool", "tool-name")) + expect(result.allowExecution).toBe(true) + expect(result.askUser).toBeUndefined() + } + }) }) }) diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index 81a37e115f5..503b8c36ed2 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -368,10 +368,10 @@ "description": "Temps mínim entre sol·licituds d'API." }, "consecutiveMistakeLimit": { - "label": "Límit d'errors consecutius", - "description": "Nombre d'errors consecutius abans de mostrar el diàleg 'Continua igualment'. Actual: {{value}}", - "unlimitedDescription": "Reintents il·limitats activats (continuació automàtica). El diàleg no apareixerà mai.", - "warning": "⚠️ Establir-ho a 0 permet reintents il·limitats que poden consumir un ús significatiu de l'API" + "label": "Límit d'errors i repeticions", + "description": "Nombre d'errors consecutius o accions repetides abans de mostrar el diàleg 'En Roo està tenint problemes'", + "unlimitedDescription": "Reintents il·limitats habilitats (procediment automàtic). El diàleg no apareixerà mai.", + "warning": "⚠️ Establir a 0 permet reintents il·limitats que poden consumir un ús significatiu de l'API" }, "reasoningEffort": { "label": "Esforç de raonament del model", diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index be690561423..48d0d41a417 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -368,10 +368,10 @@ "description": "Minimale Zeit zwischen API-Anfragen." }, "consecutiveMistakeLimit": { - "label": "Limit für aufeinanderfolgende Fehler", - "description": "Anzahl der aufeinanderfolgenden Fehler, bevor der Dialog 'Trotzdem fortfahren' angezeigt wird. Aktuell: {{value}}", - "unlimitedDescription": "Unbegrenzte Wiederholungen aktiviert (automatisches Fortfahren). Der Dialog wird nie angezeigt.", - "warning": "⚠️ Das Setzen auf 0 erlaubt unbegrenzte Wiederholungen, was zu einem erheblichen API-Verbrauch führen kann" + "label": "Fehler- & Wiederholungslimit", + "description": "Anzahl aufeinanderfolgender Fehler oder wiederholter Aktionen, bevor der Dialog 'Roo hat Probleme' angezeigt wird", + "unlimitedDescription": "Unbegrenzte Wiederholungen aktiviert (automatisches Fortfahren). Der Dialog wird niemals angezeigt.", + "warning": "⚠️ Das Setzen auf 0 erlaubt unbegrenzte Wiederholungen, was zu erheblichem API-Verbrauch führen kann" }, "reasoningEffort": { "label": "Modell-Denkaufwand", diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index f8211fdfe95..69ca01154d6 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -368,8 +368,8 @@ "description": "Minimum time between API requests." }, "consecutiveMistakeLimit": { - "label": "Consecutive Mistake Limit", - "description": "Number of consecutive mistakes before showing 'Proceed Anyways' dialog. Current: {{value}}", + "label": "Error & Repetition Limit", + "description": "Number of consecutive errors or repeated actions before showing 'Roo is having trouble' dialog", "unlimitedDescription": "Unlimited retries enabled (auto-proceed). The dialog will never appear.", "warning": "⚠️ Setting to 0 allows unlimited retries which may consume significant API usage" }, diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index 89455746ecb..684949488ec 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -368,10 +368,10 @@ "description": "Tiempo mínimo entre solicitudes de API." }, "consecutiveMistakeLimit": { - "label": "Límite de errores consecutivos", - "description": "Número de errores consecutivos antes de mostrar el diálogo 'Proceder de todos modos'. Actual: {{value}}", + "label": "Límite de errores y repeticiones", + "description": "Número de errores consecutivos o acciones repetidas antes de mostrar el diálogo 'Roo está teniendo problemas'", "unlimitedDescription": "Reintentos ilimitados habilitados (proceder automáticamente). El diálogo nunca aparecerá.", - "warning": "⚠️ Establecer el valor en 0 permite reintentos ilimitados que pueden consumir un uso significativo de la API" + "warning": "⚠️ Establecer en 0 permite reintentos ilimitados que pueden consumir un uso significativo de la API" }, "reasoningEffort": { "label": "Esfuerzo de razonamiento del modelo", diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index 4de79703f20..80801584463 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -368,10 +368,10 @@ "description": "Temps minimum entre les requêtes API." }, "consecutiveMistakeLimit": { - "label": "Limite d'erreurs consécutives", - "description": "Nombre d'erreurs consécutives avant d'afficher la boîte de dialogue 'Continuer quand même'. Actuel : {{value}}", - "unlimitedDescription": "Nouvelles tentatives illimitées activées (poursuite automatique). La boîte de dialogue n'apparaîtra jamais.", - "warning": "⚠️ Mettre à 0 autorise des nouvelles tentatives illimitées, ce qui peut consommer une utilisation importante de l'API" + "label": "Limite d'erreurs et de répétitions", + "description": "Nombre d'erreurs consécutives ou d'actions répétées avant d'afficher la boîte de dialogue 'Roo a des difficultés'", + "unlimitedDescription": "Réessais illimités activés (poursuite automatique). La boîte de dialogue n'apparaîtra jamais.", + "warning": "⚠️ Mettre à 0 autorise des réessais illimités, ce qui peut consommer une utilisation importante de l'API" }, "reasoningEffort": { "label": "Effort de raisonnement du modèle", diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index 7ad8f65fbda..30a34de4341 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -368,10 +368,10 @@ "description": "API अनुरोधों के बीच न्यूनतम समय।" }, "consecutiveMistakeLimit": { - "label": "लगातार गलती की सीमा", - "description": "'वैसे भी आगे बढ़ें' डायलॉग दिखाने से पहले लगातार गलतियों की संख्या। वर्तमान: {{value}}", - "unlimitedDescription": "असीमित पुन: प्रयास सक्षम (ऑटो-प्रोसीड)। डायलॉग कभी नहीं दिखाई देगा।", - "warning": "⚠️ 0 पर सेट करने से असीमित पुन: प्रयास की अनुमति मिलती है जिससे महत्वपूर्ण एपीआई उपयोग हो सकता है" + "label": "त्रुटि और पुनरावृत्ति सीमा", + "description": "'रू को समस्या हो रही है' संवाद दिखाने से पहले लगातार त्रुटियों या दोहराए गए कार्यों की संख्या", + "unlimitedDescription": "असीमित पुनः प्रयास सक्षम (स्वतः आगे बढ़ें)। संवाद कभी नहीं दिखाई देगा।", + "warning": "⚠️ 0 पर सेट करने से असीमित पुनः प्रयास की अनुमति मिलती है जिससे महत्वपूर्ण एपीआई उपयोग हो सकता है" }, "reasoningEffort": { "label": "मॉडल तर्क प्रयास", diff --git a/webview-ui/src/i18n/locales/id/settings.json b/webview-ui/src/i18n/locales/id/settings.json index 994a5091a42..6089d5874e3 100644 --- a/webview-ui/src/i18n/locales/id/settings.json +++ b/webview-ui/src/i18n/locales/id/settings.json @@ -372,10 +372,10 @@ "description": "Waktu minimum antara permintaan API." }, "consecutiveMistakeLimit": { - "label": "Batas Kesalahan Berturut-turut", - "description": "Jumlah kesalahan berturut-turut sebelum menampilkan dialog 'Lanjutkan Saja'. Saat ini: {{value}}", - "unlimitedDescription": "Coba lagi tanpa batas diaktifkan (lanjutkan otomatis). Dialog tidak akan pernah muncul.", - "warning": "⚠️ Mengatur ke 0 memungkinkan coba lagi tanpa batas yang dapat menghabiskan penggunaan API yang signifikan" + "label": "Batas Kesalahan & Pengulangan", + "description": "Jumlah kesalahan berturut-turut atau tindakan berulang sebelum menampilkan dialog 'Roo mengalami masalah'", + "unlimitedDescription": "Percobaan ulang tak terbatas diaktifkan (lanjut otomatis). Dialog tidak akan pernah muncul.", + "warning": "⚠️ Mengatur ke 0 memungkinkan percobaan ulang tak terbatas yang dapat menghabiskan penggunaan API yang signifikan" }, "reasoningEffort": { "label": "Upaya Reasoning Model", diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index a05b473d37d..189efe0bed9 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -368,9 +368,9 @@ "description": "Tempo minimo tra le richieste API." }, "consecutiveMistakeLimit": { - "label": "Limite di errori consecutivi", - "description": "Numero di errori consecutivi prima di mostrare la finestra di dialogo 'Procedi comunque'. Attuale: {{value}}", - "unlimitedDescription": "Ripetizioni illimitate abilitate (procedi automaticamente). La finestra di dialogo non apparirà mai.", + "label": "Limite di errori e ripetizioni", + "description": "Numero di errori consecutivi o azioni ripetute prima di mostrare la finestra di dialogo 'Roo sta riscontrando problemi'", + "unlimitedDescription": "Tentativi illimitati abilitati (procedi automaticamente). La finestra di dialogo non verrà mai visualizzata.", "warning": "⚠️ L'impostazione a 0 consente tentativi illimitati che possono consumare un notevole utilizzo dell'API" }, "reasoningEffort": { diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index fc054b4a549..a63d0b68008 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -368,10 +368,10 @@ "description": "APIリクエスト間の最小時間。" }, "consecutiveMistakeLimit": { - "label": "連続ミスの制限", - "description": "「それでも続行」ダイアログを表示するまでの連続ミス数。現在: {{value}}", - "unlimitedDescription": "無制限リトライが有効です(自動進行)。ダイアログは表示されません。", - "warning": "⚠️ 0に設定すると無制限にリトライが許可され、API使用量が大幅に増加する可能性があります" + "label": "エラーと繰り返しの制限", + "description": "「Rooが問題を抱えています」ダイアログを表示するまでの連続エラーまたは繰り返しアクションの数", + "unlimitedDescription": "無制限のリトライが有効です(自動進行)。ダイアログは表示されません。", + "warning": "⚠️ 0に設定すると無制限のリトライが可能になり、API使用量が大幅に増加する可能性があります" }, "reasoningEffort": { "label": "モデル推論の労力", diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index b219e684ebc..c4da7c86022 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -368,9 +368,9 @@ "description": "API 요청 간 최소 시간." }, "consecutiveMistakeLimit": { - "label": "연속 실수 제한", - "description": "'계속 진행' 대화 상자를 표시하기 전의 연속 실수 횟수. 현재: {{value}}", - "unlimitedDescription": "무제한 재시도 활성화됨(자동 진행). 대화 상자가 표시되지 않습니다.", + "label": "오류 및 반복 제한", + "description": "'Roo에 문제가 발생했습니다' 대화 상자를 표시하기 전의 연속 오류 또는 반복 작업 수", + "unlimitedDescription": "무제한 재시도 활성화 (자동 진행). 대화 상자가 나타나지 않습니다.", "warning": "⚠️ 0으로 설정하면 무제한 재시도가 허용되어 상당한 API 사용량이 발생할 수 있습니다" }, "reasoningEffort": { diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index a4a08b83e77..535c462fe2f 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -368,9 +368,9 @@ "description": "Minimale tijd tussen API-verzoeken." }, "consecutiveMistakeLimit": { - "label": "Limiet voor opeenvolgende fouten", - "description": "Aantal opeenvolgende fouten voordat het dialoogvenster 'Toch doorgaan' wordt weergegeven. Huidig: {{value}}", - "unlimitedDescription": "Onbeperkt aantal nieuwe pogingen ingeschakeld (automatisch doorgaan). Het dialoogvenster verschijnt nooit.", + "label": "Fout- & Herhalingslimiet", + "description": "Aantal opeenvolgende fouten of herhaalde acties voordat het dialoogvenster 'Roo ondervindt problemen' wordt weergegeven", + "unlimitedDescription": "Onbeperkt aantal nieuwe pogingen ingeschakeld (automatisch doorgaan). Het dialoogvenster zal nooit verschijnen.", "warning": "⚠️ Instellen op 0 staat onbeperkte nieuwe pogingen toe, wat aanzienlijk API-gebruik kan verbruiken" }, "reasoningEffort": { diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index 61d37375e30..f7d80cd1a6a 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -368,10 +368,10 @@ "description": "Minimalny czas między żądaniami API." }, "consecutiveMistakeLimit": { - "label": "Limit kolejnych błędów", - "description": "Liczba kolejnych błędów przed wyświetleniem okna dialogowego 'Kontynuuj mimo to'. Bieżący: {{value}}", - "unlimitedDescription": "Włączono nieograniczone ponowienia (kontynuacja automatyczna). Okno dialogowe nigdy się nie pojawi.", - "warning": "⚠️ Ustawienie na 0 pozwala na nieograniczone ponowienia, co może zużyć znaczną ilość zasobów API" + "label": "Limit błędów i powtórzeń", + "description": "Liczba kolejnych błędów lub powtórzonych akcji przed wyświetleniem okna dialogowego 'Roo ma problemy'", + "unlimitedDescription": "Włączono nieograniczone próby (automatyczne kontynuowanie). Okno dialogowe nigdy się nie pojawi.", + "warning": "⚠️ Ustawienie na 0 pozwala na nieograniczone próby, co może zużyć znaczną ilość API" }, "reasoningEffort": { "label": "Wysiłek rozumowania modelu", diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index cab90bb7db8..a9ccd98838b 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -368,9 +368,9 @@ "description": "Tempo mínimo entre requisições de API." }, "consecutiveMistakeLimit": { - "label": "Limite de erros consecutivos", - "description": "Número de erros consecutivos antes de exibir a caixa de diálogo 'Prosseguir mesmo assim'. Atual: {{value}}", - "unlimitedDescription": "Tentativas ilimitadas ativadas (prosseguimento automático). A caixa de diálogo nunca aparecerá.", + "label": "Limite de Erros e Repetições", + "description": "Número de erros consecutivos ou ações repetidas antes de exibir o diálogo 'Roo está com problemas'", + "unlimitedDescription": "Tentativas ilimitadas ativadas (prosseguimento automático). O diálogo nunca aparecerá.", "warning": "⚠️ Definir como 0 permite tentativas ilimitadas, o que pode consumir um uso significativo da API" }, "reasoningEffort": { diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index 9a4962d07af..696c5ef641c 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -368,10 +368,10 @@ "description": "Минимальное время между запросами к API." }, "consecutiveMistakeLimit": { - "label": "Лимит последовательных ошибок", - "description": "Количество последовательных ошибок перед показом диалогового окна 'Все равно продолжить'. Текущее значение: {{value}}", + "label": "Лимит ошибок и повторений", + "description": "Количество последовательных ошибок или повторных действий перед показом диалогового окна 'У Roo возникли проблемы'", "unlimitedDescription": "Включены неограниченные повторные попытки (автоматическое продолжение). Диалоговое окно никогда не появится.", - "warning": "⚠️ Установка значения 0 разрешает неограниченные повторные попытки, что может привести к значительному использованию API" + "warning": "⚠️ Установка значения 0 разрешает неограниченные повторные попытки, что может значительно увеличить использование API" }, "reasoningEffort": { "label": "Усилия по рассуждению модели", diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index 9d529b5da53..f81bfd98eaa 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -368,9 +368,9 @@ "description": "API istekleri arasındaki minimum süre." }, "consecutiveMistakeLimit": { - "label": "Ardışık Hata Limiti", - "description": "'Yine de Devam Et' iletişim kutusunu göstermeden önceki ardışık hata sayısı. Mevcut: {{value}}", - "unlimitedDescription": "Sınırsız yeniden deneme etkin (otomatik devam et). İletişim kutusu asla görünmez.", + "label": "Hata ve Tekrar Limiti", + "description": "'Roo sorun yaşıyor' iletişim kutusunu göstermeden önceki ardışık hata veya tekrarlanan eylem sayısı", + "unlimitedDescription": "Sınırsız yeniden deneme etkin (otomatik devam et). Diyalog asla görünmeyecek.", "warning": "⚠️ 0'a ayarlamak, önemli API kullanımına neden olabilecek sınırsız yeniden denemeye izin verir" }, "reasoningEffort": { diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index cf76aac1dc4..97f24b47333 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -368,10 +368,10 @@ "description": "Thời gian tối thiểu giữa các yêu cầu API." }, "consecutiveMistakeLimit": { - "label": "Giới hạn lỗi liên tiếp", - "description": "Số lỗi liên tiếp trước khi hiển thị hộp thoại 'Tiếp tục dù sao'. Hiện tại: {{value}}", + "label": "Giới hạn lỗi và lặp lại", + "description": "Số lỗi liên tiếp hoặc hành động lặp lại trước khi hiển thị hộp thoại 'Roo đang gặp sự cố'", "unlimitedDescription": "Đã bật thử lại không giới hạn (tự động tiếp tục). Hộp thoại sẽ không bao giờ xuất hiện.", - "warning": "⚠️ Đặt thành 0 cho phép thử lại không giới hạn, có thể tiêu tốn mức sử dụng API đáng kể" + "warning": "⚠️ Đặt thành 0 cho phép thử lại không giới hạn, điều này có thể tiêu tốn mức sử dụng API đáng kể" }, "reasoningEffort": { "label": "Nỗ lực suy luận của mô hình", diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index d86c2e641bd..bbe85d0e84a 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -368,10 +368,10 @@ "description": "设置API请求的最小间隔时间" }, "consecutiveMistakeLimit": { - "label": "连续错误上限", - "description": "“强制继续”对话框出现前允许的连续错误次数。当前: {{value}}", - "unlimitedDescription": "已启用无限制重试(自动继续),对话框将不再出现。", - "warning": "⚠️ 设置为 0 将允许无限制重试,可能消耗大量 API" + "label": "错误和重复限制", + "description": "在显示“Roo遇到问题”对话框前允许的连续错误或重复操作次数", + "unlimitedDescription": "已启用无限重试(自动继续)。对话框将永远不会出现。", + "warning": "⚠️ 设置为 0 允许无限重试,这可能会消耗大量 API 使用量" }, "reasoningEffort": { "label": "模型推理强度", diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index f7ca5181653..4ca64bbc5c4 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -368,10 +368,10 @@ "description": "API 請求間的最短時間" }, "consecutiveMistakeLimit": { - "label": "連續錯誤上限", - "description": "「強制繼續」對話方塊出現前允許的連續錯誤次數。目前: {{value}}", - "unlimitedDescription": "已啟用無限制重試(自動繼續),對話方塊將不再出現。", - "warning": "⚠️ 設定為 0 將允許無限制重試,可能消耗大量 API" + "label": "錯誤和重複限制", + "description": "在顯示「Roo 遇到問題」對話方塊前允許的連續錯誤或重複操作次數", + "unlimitedDescription": "已啟用無限重試(自動繼續)。對話方塊將永遠不會出現。", + "warning": "⚠️ 設定為 0 允許無限重試,這可能會消耗大量 API 使用量" }, "reasoningEffort": { "label": "模型推理強度", From 8d180035ed9bde7ec6d2c13062b6bd96cf6f2153 Mon Sep 17 00:00:00 2001 From: Daniel Riccio Date: Tue, 15 Jul 2025 17:14:10 -0500 Subject: [PATCH 5/5] fix: address PR review feedback for configurable error & repetition limit - Simplified conversion logic by removing Infinity conversion - Added validation for negative values in UI component - Removed unnecessary console logging in migration function - Added JSDoc comments for consecutiveMistakeLimit parameter - Improved test coverage for ToolRepetitionDetector behavior - Fixed UI issue where 0 was showing as 3 in ApiOptions --- src/core/config/ProviderSettingsManager.ts | 4 -- src/core/task/Task.ts | 8 ++-- src/core/task/__tests__/Task.spec.ts | 12 ++--- .../__tests__/ToolRepetitionDetector.spec.ts | 45 +++++++++++++++++++ .../src/components/settings/ApiOptions.tsx | 7 ++- .../ConsecutiveMistakeLimitControl.tsx | 6 ++- 6 files changed, 64 insertions(+), 18 deletions(-) diff --git a/src/core/config/ProviderSettingsManager.ts b/src/core/config/ProviderSettingsManager.ts index 3f64656706e..7823a3040af 100644 --- a/src/core/config/ProviderSettingsManager.ts +++ b/src/core/config/ProviderSettingsManager.ts @@ -243,10 +243,6 @@ export class ProviderSettingsManager { for (const [name, apiConfig] of Object.entries(providerProfiles.apiConfigs)) { if (apiConfig.consecutiveMistakeLimit == null) { apiConfig.consecutiveMistakeLimit = DEFAULT_CONSECUTIVE_MISTAKE_LIMIT - } else { - console.log( - `[ProviderSettingsManager] Profile ${name} already has consecutiveMistakeLimit: ${apiConfig.consecutiveMistakeLimit}`, - ) } } } catch (error) { diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index f160141d198..8a1bf1101d4 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -256,7 +256,7 @@ export class Task extends EventEmitter { this.browserSession = new BrowserSession(provider.context) this.diffEnabled = enableDiff this.fuzzyMatchThreshold = fuzzyMatchThreshold - this.consecutiveMistakeLimit = consecutiveMistakeLimit === 0 ? Infinity : (consecutiveMistakeLimit ?? 3) + this.consecutiveMistakeLimit = consecutiveMistakeLimit ?? DEFAULT_CONSECUTIVE_MISTAKE_LIMIT this.providerRef = new WeakRef(provider) this.globalStoragePath = provider.context.globalStorageUri.fsPath this.diffViewProvider = new DiffViewProvider(this.cwd) @@ -290,9 +290,7 @@ export class Task extends EventEmitter { }) } - this.toolRepetitionDetector = new ToolRepetitionDetector( - this.consecutiveMistakeLimit === Infinity ? 0 : this.consecutiveMistakeLimit, - ) + this.toolRepetitionDetector = new ToolRepetitionDetector(this.consecutiveMistakeLimit) onCreated?.(this) @@ -1162,7 +1160,7 @@ export class Task extends EventEmitter { throw new Error(`[RooCode#recursivelyMakeRooRequests] task ${this.taskId}.${this.instanceId} aborted`) } - if (this.consecutiveMistakeCount >= this.consecutiveMistakeLimit) { + if (this.consecutiveMistakeLimit > 0 && this.consecutiveMistakeCount >= this.consecutiveMistakeLimit) { const { response, text, images } = await this.ask( "mistake_limit_reached", t("common:errors.mistake_limit_guidance"), diff --git a/src/core/task/__tests__/Task.spec.ts b/src/core/task/__tests__/Task.spec.ts index a245455298d..797714cde8f 100644 --- a/src/core/task/__tests__/Task.spec.ts +++ b/src/core/task/__tests__/Task.spec.ts @@ -343,7 +343,7 @@ describe("Cline", () => { expect(cline.consecutiveMistakeLimit).toBe(5) }) - it("should convert consecutiveMistakeLimit of 0 to Infinity", () => { + it("should keep consecutiveMistakeLimit of 0 as 0 for unlimited", () => { const cline = new Task({ provider: mockProvider, apiConfiguration: mockApiConfig, @@ -352,10 +352,10 @@ describe("Cline", () => { startTask: false, }) - expect(cline.consecutiveMistakeLimit).toBe(Infinity) + expect(cline.consecutiveMistakeLimit).toBe(0) }) - it("should pass correct value to ToolRepetitionDetector when limit is Infinity", () => { + it("should pass 0 to ToolRepetitionDetector for unlimited mode", () => { const cline = new Task({ provider: mockProvider, apiConfiguration: mockApiConfig, @@ -364,10 +364,10 @@ describe("Cline", () => { startTask: false, }) - // The toolRepetitionDetector should be initialized with 0 when limit is Infinity (unlimited) + // The toolRepetitionDetector should be initialized with 0 for unlimited mode expect(cline.toolRepetitionDetector).toBeDefined() - // We can't directly check the internal state, but we can verify the limit was converted - expect(cline.consecutiveMistakeLimit).toBe(Infinity) + // Verify the limit remains as 0 + expect(cline.consecutiveMistakeLimit).toBe(0) }) it("should pass consecutiveMistakeLimit to ToolRepetitionDetector", () => { diff --git a/src/core/tools/__tests__/ToolRepetitionDetector.spec.ts b/src/core/tools/__tests__/ToolRepetitionDetector.spec.ts index 35117fd9ba1..42041c1a464 100644 --- a/src/core/tools/__tests__/ToolRepetitionDetector.spec.ts +++ b/src/core/tools/__tests__/ToolRepetitionDetector.spec.ts @@ -312,5 +312,50 @@ describe("ToolRepetitionDetector", () => { expect(result.askUser).toBeUndefined() } }) + + it("should handle different limits correctly", () => { + // Test with limit of 5 + const detector5 = new ToolRepetitionDetector(5) + const tool = createToolUse("tool", "tool-name") + + // First 4 calls should be allowed + for (let i = 0; i < 4; i++) { + const result = detector5.check(tool) + expect(result.allowExecution).toBe(true) + expect(result.askUser).toBeUndefined() + } + + // 5th call should be blocked + const result5 = detector5.check(tool) + expect(result5.allowExecution).toBe(false) + expect(result5.askUser).toBeDefined() + expect(result5.askUser?.messageKey).toBe("mistake_limit_reached") + }) + + it("should reset counter after blocking and allow new attempts", () => { + const detector = new ToolRepetitionDetector(2) + const tool = createToolUse("tool", "tool-name") + + // First call allowed + expect(detector.check(tool).allowExecution).toBe(true) + + // Second call should block (limit is 2) + const blocked = detector.check(tool) + expect(blocked.allowExecution).toBe(false) + + // After blocking, counter should reset and allow new attempts + expect(detector.check(tool).allowExecution).toBe(true) + }) + + it("should handle negative limits as 0 (unlimited)", () => { + const detector = new ToolRepetitionDetector(-1) + + // Should behave like unlimited + for (let i = 0; i < 5; i++) { + const result = detector.check(createToolUse("tool", "tool-name")) + expect(result.allowExecution).toBe(true) + expect(result.askUser).toBeUndefined() + } + }) }) }) diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index a19279cb9f2..e3f5ae7a0d8 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -6,6 +6,7 @@ import { VSCodeLink } from "@vscode/webview-ui-toolkit/react" import { type ProviderName, type ProviderSettings, + DEFAULT_CONSECUTIVE_MISTAKE_LIMIT, openRouterDefaultModelId, requestyDefaultModelId, glamaDefaultModelId, @@ -549,7 +550,11 @@ const ApiOptions = ({ onChange={(value) => setApiConfigurationField("rateLimitSeconds", value)} /> setApiConfigurationField("consecutiveMistakeLimit", value)} /> diff --git a/webview-ui/src/components/settings/ConsecutiveMistakeLimitControl.tsx b/webview-ui/src/components/settings/ConsecutiveMistakeLimitControl.tsx index 342f45d6d30..e60b2db3232 100644 --- a/webview-ui/src/components/settings/ConsecutiveMistakeLimitControl.tsx +++ b/webview-ui/src/components/settings/ConsecutiveMistakeLimitControl.tsx @@ -13,7 +13,9 @@ export const ConsecutiveMistakeLimitControl: React.FC { - onChange(newValue) + // Ensure value is not negative + const validValue = Math.max(0, newValue) + onChange(validValue) }, [onChange], ) @@ -29,7 +31,7 @@ export const ConsecutiveMistakeLimitControl: React.FC handleValueChange(newValue[0])} /> - {value ?? DEFAULT_CONSECUTIVE_MISTAKE_LIMIT} + {Math.max(0, value ?? DEFAULT_CONSECUTIVE_MISTAKE_LIMIT)}
{value === 0