fix(cli): commit tool scrollback lines in verbose mode (non-streaming/MoA) (#53785)

In the interactive CLI, the aggregator's tool calls under a MoA preset (or
any non-streaming model call, e.g. copilot-acp) appeared to overwrite each
other instead of building scrollable history. Each tool only updated the
transient spinner line; no committed scrollback line was printed.

Root cause: persistent tool lines in _on_tool_progress's tool.completed
branch were gated on tool_progress_mode in {all, new}, omitting 'verbose'.
Streaming models hid the bug because _on_tool_gen_start commits a 'preparing'
line per tool during streaming; non-streaming calls (MoA forces
_use_streaming=False) never emit that, so under 'verbose' there was no
committed line at all — only the self-overwriting spinner.

'verbose' is strictly more than 'all', so it now commits the same scrollback
line. Verified live via interactive PTY on the MoA opus-gpt preset: three
terminal calls in turn 1 and two in turn 2 each render as separate persistent
lines.
This commit is contained in:
Teknium 2026-06-27 12:29:55 -07:00 committed by GitHub
parent 227e6c0143
commit d470ed0c4c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 38 additions and 5 deletions

View file

@ -169,14 +169,39 @@ class TestToolProgressScrollback:
assert mock_print.call_count == 2
def test_verbose_mode_no_duplicate_scrollback(self):
"""In 'verbose' mode, scrollback lines are NOT printed (run_agent handles verbose output)."""
def test_verbose_mode_commits_scrollback_line(self):
"""In 'verbose' mode, tool.completed commits a persistent scrollback line.
Regression: 'verbose' used to be omitted from the scrollback gate on
the premise that run_agent renders verbose output. That premise is
false in the interactive CLI run_agent's verbose prints are gated on
``not quiet_mode`` and the interactive CLI runs quiet_mode=True. So a
non-streaming model call (MoA aggregator, copilot-acp) under 'verbose'
rendered each tool only into the self-overwriting spinner, building no
scrollable history. 'verbose' is strictly more than 'all', so it must
commit at least the same line.
"""
cli = _make_cli(tool_progress="verbose")
with patch.object(_cli_mod, "_cprint") as mock_print:
cli._on_tool_progress("tool.started", "terminal", "ls", {"command": "ls"})
cli._on_tool_progress("tool.completed", "terminal", None, None, duration=0.5, is_error=False)
mock_print.assert_not_called()
mock_print.assert_called_once()
def test_verbose_mode_commits_every_call(self):
"""In 'verbose' mode, consecutive same-tool calls each commit a line.
Mirrors 'all' (no consecutive-repeat suppression that is 'new'-only),
so a multi-step turn builds a full scrollable tool history.
"""
cli = _make_cli(tool_progress="verbose")
with patch.object(_cli_mod, "_cprint") as mock_print:
cli._on_tool_progress("tool.started", "terminal", "echo one", {"command": "echo one"})
cli._on_tool_progress("tool.completed", "terminal", None, None, duration=0.1, is_error=False)
cli._on_tool_progress("tool.started", "terminal", "echo two", {"command": "echo two"})
cli._on_tool_progress("tool.completed", "terminal", None, None, duration=0.1, is_error=False)
assert mock_print.call_count == 2
def test_verbose_mode_config_does_not_enable_global_debug_logging(self):
"""display.tool_progress=verbose controls TOOL-CALL DISPLAY ONLY.