Skip to content

Fix Windows terminal Ctrl-C process cleanup#3171

Open
neubig wants to merge 4 commits intomainfrom
windows-terminal-backend
Open

Fix Windows terminal Ctrl-C process cleanup#3171
neubig wants to merge 4 commits intomainfrom
windows-terminal-backend

Conversation

@neubig
Copy link
Copy Markdown
Contributor

@neubig neubig commented May 9, 2026

Summary

  • terminate descendant processes for the Windows PowerShell terminal backend when handling Ctrl-C interrupts
  • also clean up descendant processes when closing/resetting the Windows terminal session
  • add a Windows regression test showing a timed-out child process tree is stopped after C-c

Tests

  • uv run pytest tests/tools/terminal/test_windows_ctrl_c.py tests/tools/terminal/test_send_keys.py -q
  • uv run pre-commit run --files openhands-tools/openhands/tools/terminal/terminal/windows_terminal.py tests/tools/terminal/test_windows_ctrl_c.py

This pull request was created by an AI agent (OpenHands) on behalf of the user.


Agent Server images for this PR

GHCR package: https://github.com/OpenHands/agent-sdk/pkgs/container/agent-server

Variants & Base Images

Variant Architectures Base Image Docs / Tags
java amd64, arm64 eclipse-temurin:17-jdk Link
python amd64, arm64 nikolaik/python-nodejs:python3.13-nodejs22-slim Link
golang amd64, arm64 golang:1.21-bookworm Link

Pull (multi-arch manifest)

# Each variant is a multi-arch manifest supporting both amd64 and arm64
docker pull ghcr.io/openhands/agent-server:0090c24-python

Run

docker run -it --rm \
  -p 8000:8000 \
  --name agent-server-0090c24-python \
  ghcr.io/openhands/agent-server:0090c24-python

All tags pushed for this build

ghcr.io/openhands/agent-server:0090c24-golang-amd64
ghcr.io/openhands/agent-server:0090c2445b34a668a45f2454b8d3667351e10e2b-golang-amd64
ghcr.io/openhands/agent-server:windows-terminal-backend-golang-amd64
ghcr.io/openhands/agent-server:0090c24-golang_tag_1.21-bookworm-amd64
ghcr.io/openhands/agent-server:0090c24-golang-arm64
ghcr.io/openhands/agent-server:0090c2445b34a668a45f2454b8d3667351e10e2b-golang-arm64
ghcr.io/openhands/agent-server:windows-terminal-backend-golang-arm64
ghcr.io/openhands/agent-server:0090c24-golang_tag_1.21-bookworm-arm64
ghcr.io/openhands/agent-server:0090c24-java-amd64
ghcr.io/openhands/agent-server:0090c2445b34a668a45f2454b8d3667351e10e2b-java-amd64
ghcr.io/openhands/agent-server:windows-terminal-backend-java-amd64
ghcr.io/openhands/agent-server:0090c24-eclipse-temurin_tag_17-jdk-amd64
ghcr.io/openhands/agent-server:0090c24-java-arm64
ghcr.io/openhands/agent-server:0090c2445b34a668a45f2454b8d3667351e10e2b-java-arm64
ghcr.io/openhands/agent-server:windows-terminal-backend-java-arm64
ghcr.io/openhands/agent-server:0090c24-eclipse-temurin_tag_17-jdk-arm64
ghcr.io/openhands/agent-server:0090c24-python-amd64
ghcr.io/openhands/agent-server:0090c2445b34a668a45f2454b8d3667351e10e2b-python-amd64
ghcr.io/openhands/agent-server:windows-terminal-backend-python-amd64
ghcr.io/openhands/agent-server:0090c24-nikolaik_s_python-nodejs_tag_python3.13-nodejs22-slim-amd64
ghcr.io/openhands/agent-server:0090c24-python-arm64
ghcr.io/openhands/agent-server:0090c2445b34a668a45f2454b8d3667351e10e2b-python-arm64
ghcr.io/openhands/agent-server:windows-terminal-backend-python-arm64
ghcr.io/openhands/agent-server:0090c24-nikolaik_s_python-nodejs_tag_python3.13-nodejs22-slim-arm64
ghcr.io/openhands/agent-server:0090c24-golang
ghcr.io/openhands/agent-server:0090c2445b34a668a45f2454b8d3667351e10e2b-golang
ghcr.io/openhands/agent-server:windows-terminal-backend-golang
ghcr.io/openhands/agent-server:0090c24-golang_tag_1.21-bookworm
ghcr.io/openhands/agent-server:0090c24-java
ghcr.io/openhands/agent-server:0090c2445b34a668a45f2454b8d3667351e10e2b-java
ghcr.io/openhands/agent-server:windows-terminal-backend-java
ghcr.io/openhands/agent-server:0090c24-eclipse-temurin_tag_17-jdk
ghcr.io/openhands/agent-server:0090c24-python
ghcr.io/openhands/agent-server:0090c2445b34a668a45f2454b8d3667351e10e2b-python
ghcr.io/openhands/agent-server:windows-terminal-backend-python
ghcr.io/openhands/agent-server:0090c24-nikolaik_s_python-nodejs_tag_python3.13-nodejs22-slim

About Multi-Architecture Support

  • Each variant tag (e.g., 0090c24-python) is a multi-arch manifest supporting both amd64 and arm64
  • Docker automatically pulls the correct architecture for your platform
  • Individual architecture tags (e.g., 0090c24-python-amd64) are also available if needed

Terminate descendant processes when interrupting or closing the Windows PowerShell backend so timed-out child process trees do not survive Ctrl-C/reset.

Co-authored-by: openhands <openhands@all-hands.dev>
@neubig neubig force-pushed the windows-terminal-backend branch from 98881c1 to 2082074 Compare May 9, 2026 03:23
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 9, 2026

Python API breakage checks — ✅ PASSED

Result:PASSED

Action log

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 9, 2026

REST API breakage checks (OpenAPI) — ✅ PASSED

Result:PASSED

Action log

@neubig neubig enabled auto-merge (squash) May 9, 2026 03:28
@neubig neubig requested a review from all-hands-bot May 9, 2026 03:28
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 9, 2026

Coverage

Coverage Report •
FileStmtsMissCoverMissing
TOTAL262711313949% 
report-only-changed-files is enabled. No files were changed during this commit :)

Copy link
Copy Markdown
Collaborator

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

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

Taste Rating: 🟢 Good taste - Clean, targeted fix for a real Windows process cleanup bug.

Code Quality Assessment

This is a well-implemented fix for a legitimate bug where Windows PowerShell child processes aren't cleaned up after timeouts. The implementation is excellent:

  • PowerShell script logic: Correctly enumerates process tree using Win32_Process, builds parent-child map, recursively collects descendants, and terminates in reverse order (leaves first)
  • Safety: Excludes the script's own process ($PID), uses -ErrorAction SilentlyContinue, has proper timeout handling
  • Interrupt flow: Sensible fallback logic (try CTRL_BREAK → wait → terminate children → Ctrl-C input as last resort)
  • Testing: Good regression test that verifies real subprocess tree cleanup (not mocks)
  • Error handling: Appropriate try/except and return value semantics

Eval Risk Flag

[RISK ASSESSMENT]

  • [Overall PR] ⚠️ Risk Assessment: 🟡 MEDIUM

Per repository guidelines, terminal/stdin/stdout handling changes require eval validation before approval. While the actual risk is low (Windows-specific bug fix, most evals run on Linux, makes timeout behavior work as intended), it falls under the eval-review category.

Recommendation: Maintainer should confirm whether Windows eval validation is needed, or approve if Windows is not a critical eval platform. The code itself is production-ready.

VERDICT:
Code quality: Excellent
⏸️ Merge decision: Deferred to maintainer for eval policy decision on Windows terminal changes


Improve this review? If any feedback above seems incorrect or irrelevant to this repository, you can teach the reviewer to do better:

  1. Add a .agents/skills/custom-codereview-guide.md file to your branch (or edit it if one already exists) with the /codereview trigger and the context the reviewer is missing (e.g., "Security concerns about X do not apply here because Y"). See the customization docs for the required frontmatter format.
  2. Re-request a review - the reviewer reads guidelines from the PR branch, so your changes take effect immediately.
  3. When your PR is merged, the guideline file goes through normal code review by repository maintainers.

Resolve with AI? Install the iterate skill in your agent and run /iterate to automatically drive this PR through CI, review, and QA until it's merge-ready.

Copy link
Copy Markdown
Collaborator

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

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

✅ QA Report: PASS WITH LIMITATIONS

Windows-specific functionality verified through CI; unable to test directly on Linux.

Does this PR achieve its stated goal?

Yes. The PR successfully fixes Windows terminal Ctrl-C process cleanup. CI evidence shows the new regression test test_windows_ctrl_c_interrupt_kills_child_process_tree passed on Windows, verifying that child processes are now properly terminated when Ctrl-C is sent after a timeout. The test creates a child PowerShell process that sleeps for 120 seconds, triggers a timeout, sends Ctrl-C, and confirms the child process is stopped—exactly the behavior the PR promises to deliver.

Phase Result
Environment Setup ✅ Dependencies synced successfully
CI Status ✅ All checks passing (windows-tests: PASS, tools-tests: PASS)
Functional Verification ⚠️ Verified via Windows CI logs; direct execution not possible on Linux
Functional Verification

Test 1: Windows Ctrl-C Regression Test (via CI)

Platform constraint:
QA environment is Linux. The PR changes are Windows-specific (PowerShell terminal backend). The test is correctly marked with:

pytestmark = pytest.mark.skipif(
    platform.system() != "Windows",
    reason="Windows CTRL_BREAK/PowerShell process behavior only applies on Windows",
)

Verification approach:
Inspected CI logs for the windows-tests workflow run.

Evidence from CI logs:

windows-tests Run Windows test suite 2026-05-09T03:26:07.1850999Z 
tests/tools/terminal/test_windows_ctrl_c.py::test_windows_ctrl_c_interrupt_kills_child_process_tree PASSED

Interpretation:
The new test passed on Windows. This test:

  1. Creates a PowerShell script that spawns a child process (Start-Sleep -Seconds 120)
  2. Executes the script and waits for a timeout
  3. Sends Ctrl-C via session.execute(TerminalAction(command="C-c", is_input=True))
  4. Asserts the child process was terminated: assert not child_was_still_running

The test passing proves the fix works: child processes are now properly cleaned up.


Test 2: Linux Test Skip Behavior

Ran on Linux:

uv run pytest tests/tools/terminal/test_windows_ctrl_c.py -v

Output:

tests/tools/terminal/test_windows_ctrl_c.py::test_windows_ctrl_c_interrupt_kills_child_process_tree SKIPPED (Windows CTRL_BREAK/PowerShell process behavior only applies on Windows)

Interpretation:
Test is correctly skipped on non-Windows platforms. No false failures.


Test 3: No Regressions in send_keys Tests

Ran:

uv run pytest tests/tools/terminal/test_send_keys.py -v

Output:

23 passed, 5 warnings in 7.83s

Interpretation:
All send_keys tests passed. The changes to interrupt() and addition of _terminate_child_processes() did not break existing Ctrl-C handling or special key functionality.


Test 4: Code Quality Checks

Ran:

uv run pre-commit run --files openhands-tools/openhands/tools/terminal/terminal/windows_terminal.py tests/tools/terminal/test_windows_ctrl_c.py

Output:

Ruff format..............................................................Passed
Ruff lint................................................................Passed
PEP8 style check (pycodestyle)...........................................Passed
Type check with pyright..................................................Passed
Check import dependency rules............................................Passed
Check Tool subclass registration.........................................Passed

Interpretation:
All style, lint, and type checks passed. Code adheres to project standards.

Unable to Verify

Windows-Specific Behavior on Linux

What could not be verified:

  • Direct execution of the Windows terminal backend on Linux
  • Manual testing of the PowerShell child process termination logic
  • Interactive verification of Ctrl-C behavior with real PowerShell processes

What was attempted:
Attempted to run test_windows_ctrl_c_interrupt_kills_child_process_tree on Linux, but it was correctly skipped due to platform restrictions.

Why verification failed:
The QA environment runs on Linux. The PR's changes are specific to Windows PowerShell terminal behavior and require:

  • Windows OS
  • PowerShell (powershell.exe)
  • Windows process management APIs (CTRL_BREAK_EVENT, Win32_Process)

Suggested AGENTS.md guidance:
For future QA of Windows-specific terminal features:

  • Rely on CI windows-tests workflow results as the primary verification
  • Ensure the Windows CI runner is configured with PowerShell
  • For local verification, use a Windows development environment or Windows Subsystem for Linux (WSL) with PowerShell installed
  • Consider adding a smoke test that can run on Linux but validates the code structure (e.g., ensuring _terminate_child_processes() returns False on non-Windows platforms)

Evidence relied upon instead:
CI logs from the windows-tests workflow run confirming the new test passed.

Issues Found

None.


Verdict: This PR successfully delivers on its stated goal. The Windows CI runner verified the fix works as intended, and all code quality checks passed. The inability to test directly on Linux is a platform limitation, not a deficiency in the PR.

Comment thread openhands-tools/openhands/tools/terminal/terminal/windows_terminal.py Outdated
@neubig
Copy link
Copy Markdown
Contributor Author

neubig commented May 9, 2026

@OpenHands fix the code review comment

@openhands-ai
Copy link
Copy Markdown

openhands-ai Bot commented May 9, 2026

I'm on it! neubig can track my progress at all-hands.dev

Co-authored-by: openhands <openhands@all-hands.dev>
@openhands-ai
Copy link
Copy Markdown

openhands-ai Bot commented May 9, 2026

OpenHands encountered an error: Request timeout after 30 seconds to https://xyftbatlgupjregx.prod-runtime.all-hands.dev/api/conversations/6d45534e-99d8-4bf9-a7f6-934567fb7635/ask_agent

See the conversation for more information.

@neubig
Copy link
Copy Markdown
Contributor Author

neubig commented May 10, 2026

@OpenHands the windows CI on this PR seems to be failing, perhaps due to a timeout. Double-check current and previous CI runs for windows-tests, diagnose the problem, and /iterate until CI passes and the PR is ready to merge.

@openhands-ai
Copy link
Copy Markdown

openhands-ai Bot commented May 10, 2026

I'm on it! neubig can track my progress at all-hands.dev

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
Copy link
Copy Markdown
Contributor Author

neubig commented May 10, 2026

Update on the Windows CI timeout investigation:

  • Confirmed the prior Windows run on 20820742 passed, while the current branch timed out repeatedly around tests/tools/browser_use/test_browser_toolset.py before the terminal regression test was reached.
  • Pushed a small test cleanup fix so the mocked BrowserToolExecutor in the browser toolset tests does not run real cleanup/finalizer logic against mocked attributes.
  • The latest SHA 0090c244 is now green: 30 checks passed, 1 skipped, 0 pending, 0 failed. In particular, Run tests / windows-tests passed.
  • PR is mergeable with no unresolved review threads; the remaining GitHub state is REVIEW_REQUIRED.

This comment was created by an AI agent (OpenHands) on behalf of the user.

@openhands-ai
Copy link
Copy Markdown

openhands-ai Bot commented May 10, 2026

OpenHands encountered an error: Request timeout after 30 seconds to https://nbsmvawoiyeenyvd.prod-runtime.all-hands.dev/api/conversations/59d3dd67-977a-496e-97ed-40539bba9013/ask_agent

See the conversation for more information.

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.

4 participants