The previous fix (dbcc7cb5) only added os.waitpid() to _kill_process(),
covering the timeout/cancel path of one-shot exec. Zombies continued to
accumulate because several other exit paths never reaped children:
- _ExecSession.kill(): sent SIGKILL + process.wait(5s) but had no
os.waitpid() fallback if the wait timed out
- ExecTool.execute() generic exception handler: leaked the subprocess
if communicate() raised an unexpected error
- Normal completion paths: relied entirely on asyncio's child-watcher,
which can miss exits inside Docker containers (pidfd/SIGCHLD gaps)
Changes:
- Extract _reap_pid() helper for consistent, safe os.waitpid(WNOHANG)
- Add _reap_pid() fallback to _ExecSession.kill() via try/finally
- Add _reap_pid() safety-net after normal process exit in both
ExecTool.execute() and _ExecSession.poll()
- Kill + reap subprocess in the generic except Exception handler
- Add periodic zombie reaper background task (every 30s) in the
gateway as a last line of defense
Terminals speaking the CSI-u (kitty / fixterms) keyboard protocol -- kitty,
Ghostty, WezTerm, and terminal panes that default to it -- encode Shift+Enter
as the escape sequence "\x1b[13;2u". prompt_toolkit 3.0 has no support for
that protocol and no default mapping for the sequence, so its Vt100Parser
fails to recognise it and dumps the raw bytes ("^[[13;2u") straight into the
prompt buffer. The multiline-input work kept only Alt+Enter, so on these
terminals Shift+Enter now leaks visible escape garbage into the input.
Register "\x1b[13;2u" (absent from prompt_toolkit's default ANSI_SEQUENCES,
so setdefault() overrides nothing) and bind it to insert a newline, matching
the Alt+Enter behaviour. This is best-effort: prompt_toolkit cannot negotiate
the protocol, so we only react to a CSI-u sequence a terminal already emits;
terminals that collapse Shift+Enter into plain Enter fall back to Alt+Enter,
which stays the primary shortcut.
Add a real-PromptSession regression test asserting the sequence inserts a
newline rather than leaking raw escape bytes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
maintainer edit: handle review feedback by treating bootstrap responses without api_token as auth-required, and remove the obsolete issue_token(api_token=...) compatibility path now that API tokens are issued separately.