fix(cli): stop spinner before printing tool progress lines

The Rich console.status() spinner ('nanobot is thinking...') was not
cleared when tool call progress lines were printed during processing,
causing overlapping/garbled terminal output.

Replace the context-manager approach with explicit start/stop lifecycle:
- _pause_spinner() stops the spinner before any progress line is printed
- _resume_spinner() restarts it after printing
- Applied to both single-message mode (_cli_progress) and interactive
  mode (_consume_outbound)

Closes #1956
This commit is contained in:
who96
2026-03-15 15:26:26 +08:00
committed by Xubin Ren
parent 92f3d5a8b3
commit 48fe92a8ad

View File

@@ -635,26 +635,56 @@ def agent(
)
# Show spinner when logs are off (no output to miss); skip when logs are on
def _thinking_ctx():
def _make_spinner():
if logs:
from contextlib import nullcontext
return nullcontext()
# Animated spinner is safe to use with prompt_toolkit input handling
return None
return console.status("[dim]nanobot is thinking...[/dim]", spinner="dots")
# Shared reference so progress callbacks can pause/resume the spinner
_active_spinner = None
def _pause_spinner() -> None:
"""Temporarily stop the spinner before printing progress."""
if _active_spinner is not None:
try:
_active_spinner.stop()
except Exception:
pass
def _resume_spinner() -> None:
"""Restart the spinner after printing progress."""
if _active_spinner is not None:
try:
_active_spinner.start()
except Exception:
pass
async def _cli_progress(content: str, *, tool_hint: bool = False) -> None:
ch = agent_loop.channels_config
if ch and tool_hint and not ch.send_tool_hints:
return
if ch and not tool_hint and not ch.send_progress:
return
console.print(f" [dim]↳ {content}[/dim]")
_pause_spinner()
try:
console.print(f" [dim]↳ {content}[/dim]")
finally:
_resume_spinner()
if message:
# Single message mode — direct call, no bus needed
async def run_once():
with _thinking_ctx():
nonlocal _active_spinner
spinner = _make_spinner()
_active_spinner = spinner
if spinner:
spinner.start()
try:
response = await agent_loop.process_direct(message, session_id, on_progress=_cli_progress)
finally:
if spinner:
spinner.stop()
_active_spinner = None
_print_agent_response(response, render_markdown=markdown)
await agent_loop.close_mcp()
@@ -704,7 +734,11 @@ def agent(
elif ch and not is_tool_hint and not ch.send_progress:
pass
else:
await _print_interactive_line(msg.content)
_pause_spinner()
try:
await _print_interactive_line(msg.content)
finally:
_resume_spinner()
elif not turn_done.is_set():
if msg.content:
@@ -744,8 +778,17 @@ def agent(
content=user_input,
))
with _thinking_ctx():
nonlocal _active_spinner
spinner = _make_spinner()
_active_spinner = spinner
if spinner:
spinner.start()
try:
await turn_done.wait()
finally:
if spinner:
spinner.stop()
_active_spinner = None
if turn_response:
_print_agent_response(turn_response[0], render_markdown=markdown)