Skip to content

fix(terminal): tell model when command was aborted by the user#880

Open
umi008 wants to merge 8 commits into
Zoo-Code-Org:mainfrom
umi008:fix/833-abort-message
Open

fix(terminal): tell model when command was aborted by the user#880
umi008 wants to merge 8 commits into
Zoo-Code-Org:mainfrom
umi008:fix/833-abort-message

Conversation

@umi008

@umi008 umi008 commented Jul 11, 2026

Copy link
Copy Markdown

Related GitHub Issue

Closes: #833

Description

When the user clicked the Abort button on a running shell command, ExecaTerminalProcess emitted shell_execution_complete with { exitCode: 0 } regardless of whether the process completed normally or was killed by the user. The model received no indication that the user intervened — it would see either a successful exit or SIGKILL and could suspect Linux OOM-killer or other system events, leading to incorrect diagnosis and wrong follow-up actions.

Fix:

  1. Added optional aborted field to ExitCodeDetails interface
  2. ExecaTerminalProcess.run() emits { exitCode: 0, aborted: this.aborted } so the flag reflects whether abort() was called
  3. formatExitStatus() checks aborted first and returns "Command was aborted by the user." — clear, unambiguous, and takes priority over exit code or signal info

Test Procedure

  1. Start Zoo Code in any mode that can run terminal commands
  2. Send a long-running command (e.g., sleep 60)
  3. Click the Abort button in the UI while the command is running
  4. Verify the model receives: Command executed in terminal... Command was aborted by the user.
  5. Verify the model does NOT speculate about OOM-killer, SIGKILL, or other system events

Pre-Submission Checklist

  • Issue Linked: Closes [BUG] Zoo Code does not tell model when command Abort button was pressed #833
  • Scope: One focused fix — communicates user abort to the model
  • Self-Review: Backward-compatible; aborted is optional so all existing callers work unchanged
  • Testing: TypeScript compilation verified; abort path in ExecaTerminalProcess tested end-to-end
  • Documentation Impact: No documentation updates required
  • Contribution Guidelines: Read and agree

Screenshots / Videos

N/A — backend message formatting, no UI changes.

Documentation Updates

  • No documentation updates are required.

Additional Notes

The VSCode terminal path (TerminalProcess) sends Ctrl+C instead of SIGKILL when aborting, which correctly produces SIGINT in the exit details — that path was not affected by this bug. The fix targets the execa-based fallback path specifically.

Summary by CodeRabbit

  • Bug Fixes
    • Show clearer messaging when a command is intentionally aborted by the user.
    • Improve shell command completion reporting by consistently including an aborted flag in completion event payloads.
    • Refine exit-status text formatting for aborted vs non-aborted outcomes.
  • Tests
    • Expanded terminal process tests to verify aborted on success and added abort/error scenarios.
    • Added unit coverage for exit-status formatting across aborted, signal, and undefined inputs.

When the user clicks Abort on a running shell command, the terminal
previously emitted shell_execution_complete with exitCode: 0 and no
indication of user intervention. The model would see a successful exit
code or SIGKILL and suspect the Linux OOM-killer, leading to incorrect
diagnosis and wrong follow-up actions.

Now the ExitCodeDetails interface carries an aborted: true flag when the
user explicitly stopped the process. formatExitStatus() checks this
first and returns 'Command was aborted by the user.' so the model
immediately knows what happened and doesn't speculate about OOM-killer.

Closes Zoo-Code-Org#833
@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Command abort state is added to terminal completion data, documented in exit details, tested across successful and error paths, and rendered as a dedicated “aborted by the user” message.

Changes

Command abort status

Layer / File(s) Summary
Abort status contract and propagation
src/integrations/terminal/types.ts, src/integrations/terminal/ExecaTerminalProcess.ts, src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts
ExitCodeDetails documents the optional aborted flag, terminal completion events include it across success and error paths, and tests verify the emitted payloads.
Aborted status formatting
src/core/tools/ExecuteCommandTool.ts, src/core/tools/__tests__/executeCommandTool.spec.ts
formatExitStatus() is exported and returns a dedicated message for user-aborted commands, with coverage for related exit-status inputs.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: navedmerchant

Sequence Diagram(s)

sequenceDiagram
  participant ExecaTerminalProcess
  participant shell_execution_complete
  participant ExecuteCommandTool
  ExecaTerminalProcess->>shell_execution_complete: Emit exitCode, signalName, and aborted
  shell_execution_complete->>ExecuteCommandTool: Provide ExitCodeDetails
  ExecuteCommandTool->>ExecuteCommandTool: Format aborted status
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main fix: reporting user-aborted commands to the model.
Description check ✅ Passed The description is mostly complete and covers the issue link, change summary, testing steps, checklist, and notes.
Linked Issues check ✅ Passed The changes satisfy #833 by propagating an abort flag and formatting aborted commands with explicit user-abort messaging.
Out of Scope Changes check ✅ Passed The modified code, tests, and exported type changes all support the abort-reporting fix and appear in scope.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@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.

Caution

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

⚠️ Outside diff range comments (1)
src/integrations/terminal/ExecaTerminalProcess.ts (1)

134-144: 🎯 Functional Correctness | 🔴 Critical | 🏗️ Heavy lift

Error-path emissions are missing the aborted flag, causing a race condition that undermines the PR's objective.

When abort() sends SIGKILL, the subprocess stream can throw an ExecaError (with signal: "SIGKILL") before the for await loop at line 89 checks this.aborted. Execution then falls into the catch block, which emits shell_execution_complete without aborted: this.aborted. Downstream, formatExitStatus would then return "Process terminated by signal SIGKILL" — the exact ambiguous message this PR aims to eliminate.

Both error paths (lines 137 and 143) must propagate aborted: this.aborted so the abort flag survives regardless of which path executes.

🐛 Proposed fix: include `aborted` in error-path emissions
 		} catch (error) {
 			if (error instanceof ExecaError) {
 				console.error(`[ExecaTerminalProcess#run] shell execution error: ${error.message}`)
-				this.emit("shell_execution_complete", { exitCode: error.exitCode ?? 0, signalName: error.signal })
+				this.emit("shell_execution_complete", { exitCode: error.exitCode ?? 0, signalName: error.signal, aborted: this.aborted })
 			} else {
 				console.error(
 					`[ExecaTerminalProcess#run] shell execution error: ${error instanceof Error ? error.message : String(error)}`,
 				)
 
-				this.emit("shell_execution_complete", { exitCode: 1 })
+				this.emit("shell_execution_complete", { exitCode: 1, aborted: this.aborted })
 			}
🤖 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/integrations/terminal/ExecaTerminalProcess.ts` around lines 134 - 144,
Include aborted: this.aborted in both shell_execution_complete payloads within
ExecaTerminalProcess#run’s catch block, covering both ExecaError and generic
error paths so abort state is propagated consistently.
🤖 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.

Outside diff comments:
In `@src/integrations/terminal/ExecaTerminalProcess.ts`:
- Around line 134-144: Include aborted: this.aborted in both
shell_execution_complete payloads within ExecaTerminalProcess#run’s catch block,
covering both ExecaError and generic error paths so abort state is propagated
consistently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2e181b82-ca9e-4335-bf62-b2f42ca6ec27

📥 Commits

Reviewing files that changed from the base of the PR and between 116b70e and 689894b.

📒 Files selected for processing (3)
  • src/core/tools/ExecuteCommandTool.ts
  • src/integrations/terminal/ExecaTerminalProcess.ts
  • src/integrations/terminal/types.ts

The shell_execution_complete emission now includes aborted: false
for normal process exits. Update the test expectation to match.
@codecov

codecov Bot commented Jul 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.00000% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/integrations/terminal/ExecaTerminalProcess.ts 66.66% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@umi008
umi008 force-pushed the fix/833-abort-message branch 2 times, most recently from 3c9ca0a to 5f21e21 Compare July 11, 2026 03:11
@github-actions github-actions Bot added the awaiting-review PR changes are ready and waiting for maintainer re-review label Jul 11, 2026
When abort() sends SIGKILL and the subprocess throws ExecaError before
the for-await loop checks this.aborted, the catch block was emitting
shell_execution_complete without aborted: this.aborted. This caused
formatExitStatus to return 'Process terminated by signal SIGKILL' —
the ambiguous message this PR was designed to eliminate.

Both error paths now propagate aborted: this.aborted.

@edelauna edelauna 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.

Nice! Thanks for taking this on, this has bugged me for a while.

Since we're touching these files, can we also increase coverage so that the codecov plugin is happy as well.

Comment thread src/integrations/terminal/types.ts Outdated
Comment thread src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts
umi008 added 2 commits July 17, 2026 03:30
- Export formatExitStatus from ExecuteCommandTool for testing
- Add tests for formatExitStatus: aborted=true returns user message,
  aborted=false/signal/undefined all produce correct output
- Add ExecaTerminalProcess error-path tests: both ExecaError and
  generic error catch paths assert aborted: false in emission
- Rename existing test to reflect aborted: false assertion

Increases codecov patch coverage from 20% to 100%
Addresses PR Zoo-Code-Org#880 review comment from edelauna
- Add test that calls abort() during execution and verifies
  shell_execution_complete includes aborted: true
- Fix docstring wording: abort() is also called by timeout and
  task teardown paths, not just user button press

Addresses PR Zoo-Code-Org#880 review comments from edelauna (2 threads)
@umi008

umi008 commented Jul 17, 2026

Copy link
Copy Markdown
Author

addressed all review feedback from @edelauna:

  1. coverage increased — added 7 new tests:

    • abort path test: calls abort() mid-execution, verifies aborted: true emission
    • 2 error-path tests: ExecaError + generic error both assert aborted: false
    • 5 formatExitStatus unit tests: aborted=true msg, normal exit, signal, undefined
    • codecov patch coverage is now 100%
  2. wording fix — docstring updated to reflect abort() is also called by timeout/teardown, not just user button press

both threads replied inline

@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: 1

🤖 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/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts`:
- Around line 149-198: Add a regression test in the “error-path abort emission”
suite that exercises the user-abort flow through ExecaTerminalProcess’s existing
abort/kill mechanism, then verifies shell_execution_complete emits aborted:
true. Keep the test focused on actual abort propagation rather than generic or
ExecaError failures, and reuse the suite’s existing terminalProcess and
event-spy setup.
🪄 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: 2726ad0d-2a6d-4b52-93c2-5161bf81cdb5

📥 Commits

Reviewing files that changed from the base of the PR and between 053f12a and f296dca.

📒 Files selected for processing (3)
  • src/core/tools/ExecuteCommandTool.ts
  • src/core/tools/__tests__/executeCommandTool.spec.ts
  • src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/core/tools/ExecuteCommandTool.ts

Comment thread src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts Outdated
umi008 added 2 commits July 17, 2026 04:05
…enerators

add eslint-disable-next-line comments before the async generator
functions that throw immediately (no yield needed) to pass the CI
compile/lint check
@github-actions github-actions Bot added awaiting-author PR is waiting for the author to address requested changes and removed awaiting-review PR changes are ready and waiting for maintainer re-review labels Jul 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting-author PR is waiting for the author to address requested changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Zoo Code does not tell model when command Abort button was pressed

2 participants