Problem
Two deterministic defects in services/agent_terminal/command_executor.py affect every agent-issued terminal command. The second one makes the reported result untrustworthy.
1. The output poll can never see agent command output, so every command waits the full timeout
_extract_terminal_output (:45), _search_for_exit_marker (:265) and the error-pattern fallback (:363) accept only chat messages with sender == "terminal".
- The only writer of agent-issued command output,
service.py:_save_command_to_chat (:787, :803), always writes sender="agent_terminal".
- So
_poll_for_current_output always returns "". The stability check in _intelligent_poll_output never short-circuits on an empty string, and every command burns the full 30 s default timeout, however fast it finishes.
2. A timeout cancel kills the PTY, then an exit-code marker is written into a freshly recreated shell
- On timeout,
_handle_poll_timeout → cancel_command sends SIGINT, waits 2 s (SERVICE_STARTUP_DELAY), and then SIGKILLs the PTY (_force_close_pty_session → simple_pty_manager.close_session).
_poll_and_detect_return_code then unconditionally calls _detect_return_code → _write_to_pty. It finds the session gone (not alive (exists=False), recreating...) and creates a new blank shell. The echo '__EXIT_CODE_…__:'$? marker then runs there.
- The reported exit code belongs to the new shell, not to the command.
$? is 0 in a fresh shell, so the user sees SUCCESS | EXIT_CODE: 0 for a command that was actually killed.
Evidence (live install, 2026-09-19, one chat session)
- 00:24:57: approved
git log --oneline -1 was written into an idle, freshly created PTY.
- 00:25:28.995:
[CANCEL] Cancelling command due to timeout, 30.5 s after the write.
- 00:25:31.064: the PTY was force-closed.
- 00:25:31.163:
[PTY_WRITE] ... not alive (exists=False), recreating..., 99 ms later, in the same coroutine.
- 00:25:43:
SUCCESS | EXIT_CODE: 0 recorded.
Not a race: SimplePTYManager guards its registry with a lock, and every step runs sequentially in one coroutine. It's reproducible for any agent command that SIGINT doesn't stop within 2 s. It happened once tonight; the defect is always armed.
Acceptance criteria
Related: #17052 (approver identity; the same session recorded approved_by=web_user), #17053.
Problem
Two deterministic defects in
services/agent_terminal/command_executor.pyaffect every agent-issued terminal command. The second one makes the reported result untrustworthy.1. The output poll can never see agent command output, so every command waits the full timeout
_extract_terminal_output(:45),_search_for_exit_marker(:265) and the error-pattern fallback (:363) accept only chat messages withsender == "terminal".service.py:_save_command_to_chat(:787,:803), always writessender="agent_terminal"._poll_for_current_outputalways returns"". The stability check in_intelligent_poll_outputnever short-circuits on an empty string, and every command burns the full 30 s default timeout, however fast it finishes.2. A timeout cancel kills the PTY, then an exit-code marker is written into a freshly recreated shell
_handle_poll_timeout→cancel_commandsends SIGINT, waits 2 s (SERVICE_STARTUP_DELAY), and then SIGKILLs the PTY (_force_close_pty_session→simple_pty_manager.close_session)._poll_and_detect_return_codethen unconditionally calls_detect_return_code→_write_to_pty. It finds the session gone (not alive (exists=False), recreating...) and creates a new blank shell. Theecho '__EXIT_CODE_…__:'$?marker then runs there.$?is 0 in a fresh shell, so the user seesSUCCESS | EXIT_CODE: 0for a command that was actually killed.Evidence (live install, 2026-09-19, one chat session)
git log --oneline -1was written into an idle, freshly created PTY.[CANCEL] Cancelling command due to timeout, 30.5 s after the write.[PTY_WRITE] ... not alive (exists=False), recreating..., 99 ms later, in the same coroutine.SUCCESS | EXIT_CODE: 0recorded.Not a race:
SimplePTYManagerguards its registry with a lock, and every step runs sequentially in one coroutine. It's reproducible for any agent command that SIGINT doesn't stop within 2 s. It happened once tonight; the defect is always armed.Acceptance criteria
agent_terminalmessages, or better, a per-command marker or session token instead of the sender string. A fast command completes as soon as its output is stable, not at the timeout.SUCCESS | EXIT_CODE: 0.30.0.Related: #17052 (approver identity; the same session recorded
approved_by=web_user), #17053.