Skip to content

fix(api): default TERM to xterm-256color for tmux PTY attach#256

Merged
haofeif merged 1 commit into
awslabs:mainfrom
call-me-ram:fix/issue-150-default-term-xterm
May 23, 2026
Merged

fix(api): default TERM to xterm-256color for tmux PTY attach#256
haofeif merged 1 commit into
awslabs:mainfrom
call-me-ram:fix/issue-150-default-term-xterm

Conversation

@call-me-ram

Copy link
Copy Markdown
Contributor

Summary

The WebSocket PTY attach endpoint in cao-server calls subprocess.Popen(["tmux", "-u", "attach-session", ...]) without an env= argument, so the tmux child inherits the parent process environment as-is. Inside Docker and devcontainers TERM is routinely unset or set to dumb, and that value flows straight through to the agent CLI's TUI renderer. The result is broken colours, garbled cursor positioning, and corrupted Ink output in the browser.

This PR adds a small _build_pty_env() helper that copies the parent environment and rewrites TERM to xterm-256color when the inherited value is unset, empty, or dumb. Explicit non-dumb values (e.g. screen-256color) are preserved.

Why this isn't #249

PR #249 attempted the same fix but was closed. It used pty_env.setdefault("TERM", "xterm-256color"), which only rewrites the unset case — TERM=dumb (the value the issue title explicitly calls out) still flowed through unchanged. It also shipped no tests, and Codecov flagged 0% patch coverage on the two changed lines.

This PR:

  • Treats unset, empty string, and "dumb" as equally unusable values that must be overridden. Anything else operator-set is preserved.
  • Pulls the env-building logic into a named helper so it can be unit-tested without standing up a real WebSocket / PTY / tmux server.
  • Ships 7 tests:
    • 5 helper cases (unset → defaulted, empty → defaulted, dumb → overridden, xterm-256color → preserved, screen-256color → preserved, all other env vars inherited unchanged).
    • 1 wiring guard that patches subprocess.Popen and asserts the endpoint actually hands the corrected env in — catches the regression of "helper exists but never gets called".

Changes

  • src/cli_agent_orchestrator/api/main.py — new _build_pty_env() helper near the existing module-level helpers, plus the one-line wiring change at the terminal_ws Popen call to pass env=_build_pty_env().
  • test/api/test_terminals.py — new TestBuildPtyEnv (6 tests) and TestWebSocketSubprocessTerm (1 wiring test).

Test plan

  • uv run pytest test/api/test_terminals.py::TestBuildPtyEnv test/api/test_terminals.py::TestWebSocketSubprocessTerm -v — 7/7 pass.
  • Full unit suite — uv run pytest test/ --ignore=test/e2e -m "not integration" — 1713 passed, 1 skipped on Python 3.10, 3.11, and 3.12 (the CI matrix).
  • uv run black --check src/ test/ — clean.
  • uv run isort --check-only src/ test/ — clean.
  • uv run mypy src/cli_agent_orchestrator/api/main.py — clean (no new findings on the changed module).

Closes #150.

Container and devcontainer environments routinely leave TERM unset or
set to ``dumb``. tmux's ``attach-session`` subprocess inherited that
value because the WebSocket PTY endpoint never passed ``env=`` to
``subprocess.Popen``, which broke colours, cursor positioning, and the
Ink-based TUIs that Claude Code / Codex / Gemini render. The browser-
side xterm.js then received text that was never written in a form it
could render.

Add a small ``_build_pty_env`` helper that copies the parent process
environment and forces ``TERM`` to ``xterm-256color`` when the
inherited value is unset, empty, or ``dumb``. Explicit non-dumb values
(e.g. ``screen-256color``) are preserved verbatim.

Resolves awslabs#150. Supersedes the abandoned awslabs#249, which used
``dict.setdefault`` and therefore only handled the unset case — not
``TERM=dumb`` — and shipped without unit tests (codecov flagged 0%).

Tests cover the helper (unset / empty / dumb / xterm / custom value
preserved / non-TERM env vars inherited) and a wiring guard that
patches ``subprocess.Popen`` to assert the endpoint actually hands
the corrected env to the tmux attach call.

Copilot AI 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.

Pull request overview

This PR fixes broken terminal rendering for the WebSocket PTY attach endpoint by ensuring the tmux attach-session subprocess receives a usable TERM value even when running in container/devcontainer environments where TERM is unset/empty/dumb.

Changes:

  • Added a _build_pty_env() helper to copy the parent environment and override TERM only when it’s unset/empty/dumb.
  • Wired the helper into terminal_ws by passing env= to the subprocess.Popen call that launches tmux attach-session.
  • Added unit tests covering helper behavior plus a wiring test asserting the corrected env is actually passed to Popen.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/cli_agent_orchestrator/api/main.py Adds _build_pty_env() and passes env= to tmux attach to force a sane default TERM when needed.
test/api/test_terminals.py Adds targeted tests for env-building behavior and a wiring test to prevent regressions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (main@93d1f63). Learn more about missing BASE report.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #256   +/-   ##
=======================================
  Coverage        ?   92.23%           
=======================================
  Files           ?       68           
  Lines           ?     6153           
  Branches        ?        0           
=======================================
  Hits            ?     5675           
  Misses          ?      478           
  Partials        ?        0           
Flag Coverage Δ
unittests 92.23% <100.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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

Thanks @call-me-ram looks great!

@haofeif haofeif merged commit dec0347 into awslabs:main May 23, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] tmux PTY attach inherits TERM=dumb in container environments

4 participants