Skip to content

fix: prevent tool call mixing and retry storms#974

Open
Jpkoech30 wants to merge 1 commit into
Zoo-Code-Org:mainfrom
Jpkoech30:fix/tool-execution-mixing-retry-storms
Open

fix: prevent tool call mixing and retry storms#974
Jpkoech30 wants to merge 1 commit into
Zoo-Code-Org:mainfrom
Jpkoech30:fix/tool-execution-mixing-retry-storms

Conversation

@Jpkoech30

@Jpkoech30 Jpkoech30 commented Jul 21, 2026

Copy link
Copy Markdown

Summary

Fixes 3 root causes of tool call mixing and retry storms in sequential tool execution:

Fix 1: Tool Call Mixing (3 files)

  • TerminalRegistry.ts: New terminal per execute_command — prevents output interleaving
  • presentAssistantMessage.ts: Batch approval guard with clear skip count messaging
  • validateToolResultIds.ts: Removed positional ID fallback; logs mismatches instead

Fix 2: Retry Storm Prevention (2 files)

  • ToolRepetitionDetector.ts: Sliding window (last 5 calls) catches intermittent retry patterns
  • ExecuteCommandTool.ts: Fixed error message, timeout race, shell integration double-execution

Fix 3: Retry Budget (1 file)

  • Task.ts: 3-attempt budget per tool per turn with reset on user feedback

Closes #6

Summary by CodeRabbit

  • Bug Fixes

    • Improved command execution reliability by preventing stale approvals, duplicate runs, and mixed terminal output.
    • Strengthened timeout handling to prevent delayed interruptions after commands finish.
    • Prevented invalid tool results from being incorrectly matched to other tool calls.
    • Added safeguards against repeated tool retries and retry storms.
  • User Experience

    • Tool rejection messages now report how many tools were skipped in the current batch.
    • Shell integration errors provide clearer recovery guidance and terminal-state instructions.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Tool execution now limits retry storms, reports skipped rejected tools, rejects unsafe result-ID remapping, and isolates command terminals while tightening fallback and timeout cleanup.

Changes

Tool execution reliability

Layer / File(s) Summary
Retry budgets and rejection reporting
src/core/task/Task.ts, src/core/tools/ToolRepetitionDetector.ts, src/core/assistant-message/presentAssistantMessage.ts
Task turns enforce retry budgets, repetition detection blocks retry storms, and rejected tool batches report skipped counts.
Tool-result ID validation
src/core/task/validateToolResultIds.ts
Invalid and duplicate result IDs are dropped instead of positionally remapped, with warnings for discarded results.
Command terminal isolation and retry wiring
src/core/tools/ExecuteCommandTool.ts, src/integrations/terminal/TerminalRegistry.ts
Command execution clears stale asks, retries shell-integration failures on fresh terminals, reports specific errors, and uses dedicated terminals.
Command timeout cleanup
src/core/tools/ExecuteCommandTool.ts
Completion checks and timer cleanup reduce timeout races and dangling abort timers.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related issues

Possibly related PRs

Suggested labels: awaiting-review

Suggested reviewers: navedmerchant, taltas, edelauna

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is missing the required template sections like related issue, test procedure, checklist, and documentation updates. Reformat the PR description to match the template and add the missing sections, including issue linkage, tests run, checklist, docs notes, and reviewer guidance.
Linked Issues check ⚠️ Warning The linked issue requires DeepSeek V4 support, but this PR only changes tool execution, terminal handling, and retry logic. Implement the DeepSeek V4 metadata, defaults, request construction, aliases, and tests required by issue #6, or relink the PR to the correct issue.
Out of Scope Changes check ⚠️ Warning All listed code changes are unrelated to the linked DeepSeek V4 support issue and appear out of scope for the stated objective. Remove the tool-mixing and retry-storm changes, or update the linked issue to match this terminal/tool-execution work.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main change set: preventing tool call mixing and retry storms.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Biome (2.5.3)
src/core/tools/ExecuteCommandTool.ts

File contains syntax errors that prevent linting: Line 188: Expected a statement but instead found 'catch (error: unknown)'.; Line 235: Expected a statement but instead found 'catch (error)'.; Line 241: Expected a catch clause but instead found 'override'.; Line 241: Expected a semicolon or an implicit semicolon after a statement, but found none; Line 241: Expected a semicolon or an implicit semicolon after a statement, but found none; Line 241: expected , but instead found :; Line 241: Expected a semicolon or an implicit semicolon after a statement, but found none; Line 247: expected a semicolon to end the class property, but found none; Line 247: expected a semicolon to end the class property, but found none; Line 249: expected , but instead found command; Line 250: expected , but instead found customCwd; Line 250: expected , but instead found ?; Line 251: expected , but instead found terminalShellIntegrationDisabled; Line 251: expected , but instead found ?;

... [truncated 929 characters] ...

of modules.; Line 553: await is only allowed within async functions and at the top levels of modules.; Line 579: await is only allowed within async functions and at the top levels of modules.; Line 587: await is only allowed within async functions and at the top levels of modules.; Line 598: await is only allowed within async functions and at the top levels of modules.; Line 647: expected a semicolon to end the class property, but found none; Line 675: expected a semicolon to end the class property, but found none; Line 699: expected a semicolon to end the class property, but found none; Line 709: expected a semicolon to end the class property, but found none; Line 709: expected a semicolon to end the class property, but found none; Line 710: expected } but instead the file ends

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Jpkoech30

Copy link
Copy Markdown
Author

What this PR fixes

This PR addresses two interrelated problems in sequential tool execution:

Problem 1: Tool Call Results Get Mixed Up

When the LLM returns multiple tool calls in one response, three bugs cause outputs to be assigned to the wrong tools:

  1. Shared terminal — TerminalRegistry.getOrCreateTerminal() reuses the same VSCode terminal for all commands from the same task+cwd. Two sequential execute_command calls share one terminal → outputs interleave.

  2. Cascading skips on rejection — When user rejects tool Roo to zoo upgrade #2 in a batch of 5, tools chore: disable automatic deploy workflows #3, feat(ci): add code coverage pipeline and E2E mocking with aimock #4, Remove Roo Code Cloud and Router remnants #5 all get silently skipped. No indication of how many were skipped.

  3. Positional ID fallback — validateToolResultIds.ts remaps tool_results by array position when IDs dont match. Results silently go to wrong tools.

Problem 2: Retry Storms Waste 3-5x Tokens

  1. Shell integration double-execution — ExecuteCommandTool.ts re-executes command on ShellIntegrationError. Command may run TWICE.

  2. Intermittent retry blind spot — ToolRepetitionDetector only catches exact consecutive duplicates. Pattern execute(X)→read_file→execute(X)→read_file→execute(X) goes undetected.

  3. Error message encourages retry — Returns "inspect the cause and adjust as needed" which LLM interprets as "retry same command."

  4. No circuit breaker — No retry budget, no exponential backoff.

Impact

Without these fixes, a single command failure cascades into 3-5 retry attempts, wasting ~4,000 tokens per failed task.

Full analysis

https://github.com/Jpkoech30/zoocode-agency/blob/master/plans/agency-speed-cost-coordination-diagnosis.md

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/core/tools/ExecuteCommandTool.ts (1)

523-548: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Fix runtime crash due to missing resolve parameter and missing Promise.race().

There are two critical failures in this code block:

  1. The new Promise<void>((_, reject) => executor does not define resolve in its parameters, but it calls resolve() synchronously inside the setTimeout. This will throw a ReferenceError at runtime.
  2. The code pushes the promises to the racers array but completely fails to await Promise.race(racers). Without this await, the block immediately exits, instantly clearing the timeouts and failing to wait for the command to finish.
🐛 Proposed fix for the timeout race and missing await
-				new Promise<void>((_, reject) => {
+				new Promise<void>((resolve, reject) => {
 					userTimeoutId = setTimeout(() => {
 						// Fix timeout race: Only fire the timeout if the command hasn't
 						// already completed. Check `completed` under a microtask to avoid
 						// a race where onCompleted sets completed=true between the timeout
 						// firing and this check.
 						if (completed) {
 							resolve()
 							return
 						}
 						isUserTimedOut = true
 						task.terminalProcess?.abort()
 						reject(new Error(`Command execution timed out after ${commandExecutionTimeout}ms`))
 					}, commandExecutionTimeout)
 				}),
 			)
 		}
 
+		await Promise.race(racers)
+
 		// Fix timeout race: After Promise.race resolves (either process completed,
 		// agent timeout, or user timeout), ensure the other timeout is cleared
 		// immediately. Without this, a user timeout could fire AFTER the command
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/tools/ExecuteCommandTool.ts` around lines 523 - 548, Update the
timeout promise executor in the command execution flow to accept and use the
resolve callback when completed is already true, preventing the undefined
resolve ReferenceError. Before clearing agentTimeoutId and userTimeoutId, await
Promise.race(racers) so the flow waits for command completion or either timeout.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/core/assistant-message/presentAssistantMessage.ts`:
- Around line 74-77: Update presentAssistantMessage so batchSkipCount is reset
only when processing the first block of the batch, not on recursive calls for
subsequent blocks. Guard the reset using the existing block or batch position
indicator, preserving accumulated counts so nextSkipCount reflects prior tool
evaluations.

In `@src/core/task/Task.ts`:
- Around line 4774-4819: The retry-budget helpers are unused, so retries are
never blocked. Wire checkToolRetryBudget into the tool retry path before
allowing a retry, and invoke recordSuccessfulToolUse after successful tool
completion using the same tool name; otherwise remove both helpers and their
state if the budget is not intended to be enforced.

In `@src/core/tools/ExecuteCommandTool.ts`:
- Around line 157-189: Remove the duplicated malformed catch block within the
execute flow in ExecuteCommandTool, retaining only the valid error-handling path
for executeCommandInTerminal. Delete references that do not belong to this
scope, including onCompletedPromise and the duplicate retry/result logic, and
ensure the surrounding try/catch structure is syntactically valid.

---

Outside diff comments:
In `@src/core/tools/ExecuteCommandTool.ts`:
- Around line 523-548: Update the timeout promise executor in the command
execution flow to accept and use the resolve callback when completed is already
true, preventing the undefined resolve ReferenceError. Before clearing
agentTimeoutId and userTimeoutId, await Promise.race(racers) so the flow waits
for command completion or either timeout.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ff2acf02-071b-4543-afc9-df397726ec7e

📥 Commits

Reviewing files that changed from the base of the PR and between f2bdcb6 and 3a33cf6.

📒 Files selected for processing (6)
  • src/core/assistant-message/presentAssistantMessage.ts
  • src/core/task/Task.ts
  • src/core/task/validateToolResultIds.ts
  • src/core/tools/ExecuteCommandTool.ts
  • src/core/tools/ToolRepetitionDetector.ts
  • src/integrations/terminal/TerminalRegistry.ts

Comment on lines +74 to +77
// Reset batch skip counter at the start of each present cycle.
// This ensures the count is fresh for each batch of tool calls.
;(cline as any).batchSkipCount = 0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Fix batchSkipCount reset to prevent it from resetting on every block.

Because presentAssistantMessage recursively calls itself to process each subsequent block, resetting batchSkipCount here causes it to be 0 for every single tool evaluation. This breaks the skip counting logic, meaning nextSkipCount will always incorrectly evaluate to 1.

Only reset the counter at the start of the entire batch (i.e., when processing the first block).

🐛 Proposed fix for the skip counter
 	// Reset batch skip counter at the start of each present cycle.
 	// This ensures the count is fresh for each batch of tool calls.
-	;(cline as any).batchSkipCount = 0
+	if (cline.currentStreamingContentIndex === 0) {
+		;(cline as any).batchSkipCount = 0
+	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Reset batch skip counter at the start of each present cycle.
// This ensures the count is fresh for each batch of tool calls.
;(cline as any).batchSkipCount = 0
// Reset batch skip counter at the start of each present cycle.
// This ensures the count is fresh for each batch of tool calls.
if (cline.currentStreamingContentIndex === 0) {
;(cline as any).batchSkipCount = 0
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/assistant-message/presentAssistantMessage.ts` around lines 74 - 77,
Update presentAssistantMessage so batchSkipCount is reset only when processing
the first block of the batch, not on recursive calls for subsequent blocks.
Guard the reset using the existing block or batch position indicator, preserving
accumulated counts so nextSkipCount reflects prior tool evaluations.

Comment thread src/core/task/Task.ts
Comment on lines +4774 to +4819
/**
* Checks if the retry budget for a given tool has been exceeded.
* Each tool gets MAX_TOOL_RETRY_BUDGET (3) consecutive retries per turn.
* After that, further retries are blocked until user feedback arrives.
*
* @param toolName - The name of the tool being retried
* @returns true if the retry budget is still available, false if blocked
*/
public checkToolRetryBudget(toolName: string): boolean {
if (this.toolRetryBudgetExceeded) {
console.warn(
`[Task#${this.taskId}] Tool retry budget globally exceeded. Blocking further retries for "${toolName}".`,
)
return false
}

const currentRetries = this.toolRetryBudget.get(toolName) ?? 0

if (currentRetries >= MAX_TOOL_RETRY_BUDGET) {
this.toolRetryBudgetExceeded = true
console.warn(
`[Task#${this.taskId}] Tool retry budget exceeded for "${toolName}": ` +
`${currentRetries} retries >= ${MAX_TOOL_RETRY_BUDGET} max. ` +
"Blocking further retries until user provides new input.",
)
return false
}

// Increment the retry counter for this tool
this.toolRetryBudget.set(toolName, currentRetries + 1)
return true
}

/**
* Records a tool use as successful, which resets the retry budget for that tool.
* This is called when a tool completes successfully, allowing the model to use
* the tool again (the budget is per-retry, not per-use).
*
* @param toolName - The name of the tool that succeeded
*/
public recordSuccessfulToolUse(toolName: string): void {
// Only reset the retry budget for this specific tool, not all tools.
// This allows the model to switch to a different tool after exhausting
// the budget on one tool.
this.toolRetryBudget.delete(toolName)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Search for usages of the retry budget methods across the codebase.

rg -n 'checkToolRetryBudget|recordSuccessfulToolUse'

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 159


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## Files mentioning retry budget or tool retries\n'
rg -n -i 'retry budget|toolRetryBudget|MAX_TOOL_RETRY_BUDGET|recordSuccessfulToolUse|checkToolRetryBudget|retry.*tool' src || true

printf '\n## Task.ts context around the methods\n'
FILE='src/core/task/Task.ts'
if [ -f "$FILE" ]; then
  line=$(rg -n 'checkToolRetryBudget|recordSuccessfulToolUse' "$FILE" | head -n 1 | cut -d: -f1 || true)
  if [ -n "${line:-}" ]; then
    start=$((line-40)); [ "$start" -lt 1 ] && start=1
    end=$((line+80))
    sed -n "${start},${end}p" "$FILE"
  fi
fi

printf '\n## presentAssistantMessage.ts references\n'
if [ -f 'src/core/task/presentAssistantMessage.ts' ]; then
  rg -n -i 'retry|toolRetryBudget|checkToolRetryBudget|recordSuccessfulToolUse|tool' src/core/task/presentAssistantMessage.ts || true
fi

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 6904


Wire the retry budget into the tool execution path
These helpers are only defined here; nothing in the task flow calls them, so the retry budget never blocks retries. Hook them into the tool retry/success path, or remove the dead code if it is no longer needed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/task/Task.ts` around lines 4774 - 4819, The retry-budget helpers are
unused, so retries are never blocked. Wire checkToolRetryBudget into the tool
retry path before allowing a retry, and invoke recordSuccessfulToolUse after
successful tool completion using the same tool name; otherwise remove both
helpers and their state if the budget is not intended to be enforced.

Comment on lines 157 to 189
try {
const [rejected, result] = await executeCommandInTerminal(task, options)
} catch (error: unknown) {
// Invalidate pending ask from first execution to prevent race condition
task.supersedePendingAsk()

if (canRetryShellIntegrationError(error)) {
// Silent retry via execa — shell startup race, command was not submitted.
const status: CommandExecutionStatus = { executionId, status: "fallback" }
provider?.postMessageToWebview({ type: "commandExecutionStatus", text: JSON.stringify(status) })

const [rejected, result] = await executeCommandInTerminal(task, {
...options,
terminalShellIntegrationDisabled: true,
})

if (rejected) {
task.didRejectTool = true
}

// Fix: Mark the first execution result as consumed so we don't send
// duplicate pushToolResult calls. The shell integration retry below
// will produce the single authoritative result.
// Also await onCompletedPromise to avoid a race where the first
// command's onCompleted fires after we've already started the retry,
// corrupting shared state (completed, persistedResult, etc.).
if (!rejected && !runInBackground) {
await onCompletedPromise
}

pushToolResult(result)
} catch (error: unknown) {
// Invalidate pending ask from first execution to prevent race condition

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Remove the duplicated and malformed try-catch block.

There is a severe syntax error here caused by a botched git merge. Lines 159-188 duplicate the catch block and contain invalid references (such as onCompletedPromise, which belongs inside executeCommandInTerminal, not execute). The invalid syntax } catch () { ... } catch () { completely breaks parser execution and surfaces upstream as static analysis errors.

🐛 Proposed fix for the merge conflict
 			try {
 				const [rejected, result] = await executeCommandInTerminal(task, options)
-			} catch (error: unknown) {
-				// Invalidate pending ask from first execution to prevent race condition
-				task.supersedePendingAsk()
-
-				if (canRetryShellIntegrationError(error)) {
-					// Silent retry via execa — shell startup race, command was not submitted.
-					const status: CommandExecutionStatus = { executionId, status: "fallback" }
-					provider?.postMessageToWebview({ type: "commandExecutionStatus", text: JSON.stringify(status) })
-
-					const [rejected, result] = await executeCommandInTerminal(task, {
-						...options,
-						terminalShellIntegrationDisabled: true,
-					})
-
-				if (rejected) {
-					task.didRejectTool = true
-				}
-
-				// Fix: Mark the first execution result as consumed so we don't send
-				// duplicate pushToolResult calls. The shell integration retry below
-				// will produce the single authoritative result.
-				// Also await onCompletedPromise to avoid a race where the first
-				// command's onCompleted fires after we've already started the retry,
-				// corrupting shared state (completed, persistedResult, etc.).
-				if (!rejected && !runInBackground) {
-					await onCompletedPromise
-				}
-
-				pushToolResult(result)
-			} catch (error: unknown) {
+				
+				if (rejected) {
+					task.didRejectTool = true
+				}
+				
+				pushToolResult(result)
+			} catch (error: unknown) {
 				// Invalidate pending ask from first execution to prevent race condition
 				task.supersedePendingAsk()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try {
const [rejected, result] = await executeCommandInTerminal(task, options)
} catch (error: unknown) {
// Invalidate pending ask from first execution to prevent race condition
task.supersedePendingAsk()
if (canRetryShellIntegrationError(error)) {
// Silent retry via execa — shell startup race, command was not submitted.
const status: CommandExecutionStatus = { executionId, status: "fallback" }
provider?.postMessageToWebview({ type: "commandExecutionStatus", text: JSON.stringify(status) })
const [rejected, result] = await executeCommandInTerminal(task, {
...options,
terminalShellIntegrationDisabled: true,
})
if (rejected) {
task.didRejectTool = true
}
// Fix: Mark the first execution result as consumed so we don't send
// duplicate pushToolResult calls. The shell integration retry below
// will produce the single authoritative result.
// Also await onCompletedPromise to avoid a race where the first
// command's onCompleted fires after we've already started the retry,
// corrupting shared state (completed, persistedResult, etc.).
if (!rejected && !runInBackground) {
await onCompletedPromise
}
pushToolResult(result)
} catch (error: unknown) {
// Invalidate pending ask from first execution to prevent race condition
try {
const [rejected, result] = await executeCommandInTerminal(task, options)
if (rejected) {
task.didRejectTool = true
}
pushToolResult(result)
} catch (error: unknown) {
// Invalidate pending ask from first execution to prevent race condition
task.supersedePendingAsk()
🧰 Tools
🪛 Biome (2.5.3)

[error] 188-188: Expected a statement but instead found 'catch (error: unknown)'.

(parse)

🪛 GitHub Actions: Code QA Roo Code / 0_compile.txt

[error] 188-188: ESLint parsing error during linting. 'try' expected.

🪛 GitHub Actions: Code QA Roo Code / compile

[error] 188-188: ESLint failed with parsing error: "'try' expected".

🪛 GitHub Actions: E2E Tests (Mocked) / 0_e2e-mock.txt

[error] 188-188: esbuild: ERROR: Unexpected "catch" (Build failed with 1 error).

🪛 GitHub Actions: E2E Tests (Mocked) / e2e-mock

[error] 188-188: esbuild failed: ERROR: Unexpected "catch" (core/tools/ExecuteCommandTool.ts:188:5).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/tools/ExecuteCommandTool.ts` around lines 157 - 189, Remove the
duplicated malformed catch block within the execute flow in ExecuteCommandTool,
retaining only the valid error-handling path for executeCommandInTerminal.
Delete references that do not belong to this scope, including onCompletedPromise
and the duplicate retry/result logic, and ensure the surrounding try/catch
structure is syntactically valid.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant