diff --git a/agent/display.py b/agent/display.py index 22b918e1b..caa94b7d0 100644 --- a/agent/display.py +++ b/agent/display.py @@ -284,11 +284,11 @@ class KawaiiSpinner: The CLI already drives a TUI widget (_spinner_text) for spinner display, so KawaiiSpinner's \\r-based animation is redundant under StdoutProxy. """ - out = self._out - # StdoutProxy has a 'raw' attribute (bool) that plain file objects lack. - if hasattr(out, 'raw') and type(out).__name__ == 'StdoutProxy': - return True - return False + try: + from prompt_toolkit.patch_stdout import StdoutProxy + return isinstance(self._out, StdoutProxy) + except ImportError: + return False def _animate(self): # When stdout is not a real terminal (e.g. Docker, systemd, pipe), diff --git a/cli.py b/cli.py index ca93fbb00..750a7a14a 100644 --- a/cli.py +++ b/cli.py @@ -4034,6 +4034,17 @@ class HermesCLI: provider_data_collection=self._provider_data_collection, fallback_model=self._fallback_model, ) + # Silence raw spinner; route thinking through TUI widget when no foreground agent is active. + bg_agent._print_fn = lambda *_a, **_kw: None + + def _bg_thinking(text: str) -> None: + # Concurrent bg tasks may race on _spinner_text; acceptable for best-effort UI. + if not self._agent_running: + self._spinner_text = text + if self._app: + self._app.invalidate() + + bg_agent.thinking_callback = _bg_thinking result = bg_agent.run_conversation( user_message=prompt, @@ -4096,6 +4107,9 @@ class HermesCLI: _cprint(f" ❌ Background task #{task_num} failed: {e}") finally: self._background_tasks.pop(task_id, None) + # Clear spinner only if no foreground agent owns it + if not self._agent_running: + self._spinner_text = "" if self._app: self._invalidate(min_interval=0)