diff --git a/cli.py b/cli.py index ebc8b7637..d679b24b5 100644 --- a/cli.py +++ b/cli.py @@ -989,6 +989,7 @@ def _prune_orphaned_branches(repo_root: str) -> None: _ACCENT_ANSI_DEFAULT = "\033[1;38;2;255;215;0m" # True-color #FFD700 bold — fallback _BOLD = "\033[1m" _RST = "\033[0m" +_STREAM_PAD = " " # 4-space indent for streamed response text (matches Panel padding) def _hex_to_ansi(hex_color: str, *, bold: bool = False) -> str: @@ -2580,7 +2581,7 @@ class HermesCLI: _tc = getattr(self, "_stream_text_ansi", "") while "\n" in self._stream_buf: line, self._stream_buf = self._stream_buf.split("\n", 1) - _cprint(f"{_tc}{line}{_RST}" if _tc else line) + _cprint(f"{_STREAM_PAD}{_tc}{line}{_RST}" if _tc else f"{_STREAM_PAD}{line}") def _flush_stream(self) -> None: """Emit any remaining partial line from the stream buffer and close the box.""" @@ -2597,7 +2598,7 @@ class HermesCLI: if self._stream_buf: _tc = getattr(self, "_stream_text_ansi", "") - _cprint(f"{_tc}{self._stream_buf}{_RST}" if _tc else self._stream_buf) + _cprint(f"{_STREAM_PAD}{_tc}{self._stream_buf}{_RST}" if _tc else f"{_STREAM_PAD}{self._stream_buf}") self._stream_buf = "" # Close the response box @@ -5761,7 +5762,7 @@ class HermesCLI: border_style=_resp_color, style=_resp_text, box=rich_box.HORIZONTALS, - padding=(1, 2), + padding=(1, 4), )) else: _cprint(" (No response generated)") @@ -5885,7 +5886,7 @@ class HermesCLI: title_align="left", border_style=_resp_color, box=rich_box.HORIZONTALS, - padding=(1, 2), + padding=(1, 4), )) else: _cprint(" 💬 /btw: (no response)") @@ -7648,7 +7649,7 @@ class HermesCLI: label = " ⚕ Hermes " fill = w - 2 - len(label) _cprint(f"\n{_ACCENT}╭─{label}{'─' * max(fill - 1, 0)}╮{_RST}") - _cprint(sentence.rstrip()) + _cprint(f"{_STREAM_PAD}{sentence.rstrip()}") tts_thread = threading.Thread( target=stream_tts_to_speaker, @@ -7879,7 +7880,7 @@ class HermesCLI: border_style=_resp_color, style=_resp_text, box=rich_box.HORIZONTALS, - padding=(1, 2), + padding=(1, 4), )) diff --git a/run_agent.py b/run_agent.py index 626951b27..5f4ac68dc 100644 --- a/run_agent.py +++ b/run_agent.py @@ -6975,6 +6975,31 @@ class AIAgent: skip_pre_tool_call_hook=True, ) + @staticmethod + def _wrap_verbose(label: str, text: str, indent: str = " ") -> str: + """Word-wrap verbose tool output to fit the terminal width. + + Splits *text* on existing newlines and wraps each line individually, + preserving intentional line breaks (e.g. pretty-printed JSON). + Returns a ready-to-print string with *label* on the first line and + continuation lines indented. + """ + import shutil as _shutil + import textwrap as _tw + cols = _shutil.get_terminal_size((120, 24)).columns + wrap_width = max(40, cols - len(indent)) + out_lines: list[str] = [] + for raw_line in text.split("\n"): + if len(raw_line) <= wrap_width: + out_lines.append(raw_line) + else: + wrapped = _tw.wrap(raw_line, width=wrap_width, + break_long_words=True, + break_on_hyphens=False) + out_lines.extend(wrapped or [raw_line]) + body = ("\n" + indent).join(out_lines) + return f"{indent}{label}{body}" + def _execute_tool_calls_concurrent(self, assistant_message, messages: list, effective_task_id: str, api_call_count: int = 0) -> None: """Execute multiple tool calls concurrently using a thread pool. @@ -7045,7 +7070,7 @@ class AIAgent: args_str = json.dumps(args, ensure_ascii=False) if self.verbose_logging: print(f" 📞 Tool {i}: {name}({list(args.keys())})") - print(f" Args: {args_str}") + print(self._wrap_verbose("Args: ", json.dumps(args, indent=2, ensure_ascii=False))) else: args_preview = args_str[:self.log_prefix_chars] + "..." if len(args_str) > self.log_prefix_chars else args_str print(f" 📞 Tool {i}: {name}({list(args.keys())}) - {args_preview}") @@ -7143,7 +7168,7 @@ class AIAgent: elif not self.quiet_mode: if self.verbose_logging: print(f" ✅ Tool {i+1} completed in {tool_duration:.2f}s") - print(f" Result: {function_result}") + print(self._wrap_verbose("Result: ", function_result)) else: response_preview = function_result[:self.log_prefix_chars] + "..." if len(function_result) > self.log_prefix_chars else function_result print(f" ✅ Tool {i+1} completed in {tool_duration:.2f}s - {response_preview}") @@ -7236,7 +7261,7 @@ class AIAgent: args_str = json.dumps(function_args, ensure_ascii=False) if self.verbose_logging: print(f" 📞 Tool {i}: {function_name}({list(function_args.keys())})") - print(f" Args: {args_str}") + print(self._wrap_verbose("Args: ", json.dumps(function_args, indent=2, ensure_ascii=False))) else: args_preview = args_str[:self.log_prefix_chars] + "..." if len(args_str) > self.log_prefix_chars else args_str print(f" 📞 Tool {i}: {function_name}({list(function_args.keys())}) - {args_preview}") @@ -7524,7 +7549,7 @@ class AIAgent: if not self.quiet_mode: if self.verbose_logging: print(f" ✅ Tool {i} completed in {tool_duration:.2f}s") - print(f" Result: {function_result}") + print(self._wrap_verbose("Result: ", function_result)) else: response_preview = function_result[:self.log_prefix_chars] + "..." if len(function_result) > self.log_prefix_chars else function_result print(f" ✅ Tool {i} completed in {tool_duration:.2f}s - {response_preview}")