Skip to content

fix(tui v2): stop tool subprocesses inheriting TTY stdin (multi-session key-eating on macOS/Linux)#656

Open
ggandmee-cloud wants to merge 1 commit into
lsdefine:mainfrom
ggandmee-cloud:fix/tui-stdin-inherit
Open

fix(tui v2): stop tool subprocesses inheriting TTY stdin (multi-session key-eating on macOS/Linux)#656
ggandmee-cloud wants to merge 1 commit into
lsdefine:mainfrom
ggandmee-cloud:fix/tui-stdin-inherit

Conversation

@ggandmee-cloud

Copy link
Copy Markdown
Contributor

Symptom

Running ga tui2 with several concurrent sessions on macOS randomly swallows typed input — letters, digits, space and Enter vanish while agents are running tools. A single idle session is mostly unaffected, so it presents as a "multi-session" bug.

Environment

  • macOS (Apple Silicon), zsh, Ghostty terminal, frontends/tuiapp_v2.py (Textual)
  • Not terminal-specific: any POSIX platform is affected
  • Windows never shows this — by accident (see below)

Root cause

code_run (ga.py) spawns tool subprocesses with stdout/stderr piped but stdin left untouched, so every bash/python child inherits the TUI's terminal as fd 0, in the same foreground process group as Textual. Any child that reads stdin — input(), interactive prompts, stdin-probing tools, or readline's tcsetattr on import — races Textual for the same keyboard bytes and can flip the tty out of raw mode. More sessions → more concurrent children → more collisions.

Windows is immune because the existing creationflags=CREATE_NO_WINDOW branch detaches children from the console, so they can never touch its input buffer. The POSIX branch passes 0 — no isolation at all. This fix gives POSIX the same isolation Windows already had; Windows behavior is unchanged.

Fix

Pass stdin=subprocess.DEVNULL wherever no child legitimately needs the terminal:

  • ga.py code_run Popen — the main path every tool call goes through
  • tuiapp_v2.py !shell inline command (30s window)
  • tuiapp_v2.py clipboard helper (only when no input= is fed; pbcopy/xclip data path unaffected)
  • checklist_helper.py _PK spawns (long-lived worker/master/bbs processes)

Verification

  • py_compile clean on all three files
  • code_run('echo ...') output unchanged
  • code_run('read -t 5 x; echo rc=$?') child now gets immediate EOF (~1s) instead of holding the tty for 5 seconds
  • Manually tested ga tui2 with multiple concurrent sessions running tools while typing — no more swallowed keystrokes

@ggandmee-cloud ggandmee-cloud force-pushed the fix/tui-stdin-inherit branch from 6bcf7fa to ed3d9c7 Compare July 3, 2026 14:51
…ion key-eating on macOS/Linux

Symptom
  ga tui2 with several concurrent sessions randomly swallows typed
  input — letters, digits, space and Enter vanish while agents are
  running tools. Single idle session is mostly unaffected.

Environment
  Observed on macOS (Apple Silicon, zsh, Ghostty) with ga tui2
  (frontends/tuiapp_v2.py, Textual). Not terminal-specific: any POSIX
  platform is affected. Windows never showed it — see below.

Root cause
  code_run (ga.py) spawned tool subprocesses with stdout/stderr piped
  but stdin left untouched, so every bash/python child inherited the
  TUI's terminal as fd 0, in the same foreground process group as
  Textual. Any child that reads stdin (input(), interactive prompts,
  stdin-probing tools, readline tcsetattr on import) races Textual for
  the same keyboard bytes and can flip the tty out of raw mode. More
  sessions -> more concurrent children -> more collisions, hence
  'multi-session' presentation.

  Windows was immune by accident: the existing
  creationflags=CREATE_NO_WINDOW branch detaches children from the
  console, so they can never touch its input buffer. The POSIX branch
  passed 0 — no isolation at all. This fix gives POSIX the same
  isolation Windows already had; behavior on Windows is unchanged.

Fix
  Pass stdin=DEVNULL wherever no child legitimately needs the
  terminal:
  - ga.py code_run Popen (the main path every tool call goes through)
  - tuiapp_v2.py !shell inline command (30s window)
  - tuiapp_v2.py clipboard helper (only when no input= is fed)
  - checklist_helper.py _PK spawns (long-lived worker/master/bbs)

Verification
  py_compile clean; code_run 'echo' unchanged; 'read -t 5 x' child now
  gets immediate EOF (~1s) instead of holding the tty for 5s.
@ggandmee-cloud ggandmee-cloud force-pushed the fix/tui-stdin-inherit branch from ed3d9c7 to 0798c1e Compare July 3, 2026 15:31
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