diff --git a/src/strands_compose/renderers/ansi.py b/src/strands_compose/renderers/ansi.py index fecfd86..bffdd97 100644 --- a/src/strands_compose/renderers/ansi.py +++ b/src/strands_compose/renderers/ansi.py @@ -67,6 +67,7 @@ def __init__( self._green = "\033[32m" if is_tty else "" self._red = "\033[31m" if is_tty else "" self._yellow = "\033[33m" if is_tty else "" + self._magenta = "\033[95m" if is_tty else "" self._reset = "\033[0m" if is_tty else "" if separator_width is not None: @@ -120,16 +121,19 @@ def _handle_agent_start(self, event: StreamEvent) -> None: self._break() self._mode = None self._active_agent = None - self._out.write(f"\n{self._cyan}{self._bold}[{event.agent_name}]{self._reset} starting…\n") + self._out.write(self._separator(event.agent_name, "AGENT START", color=self._magenta)) + self._out.write(f"{self._cyan}{self._bold}[{event.agent_name}]{self._reset} starting…\n") self._out.flush() def _handle_tool_start(self, event: StreamEvent) -> None: self._break() self._mode = None + self._active_agent = None data = event.data label = data.get("tool_label") or data.get("tool_name", "unknown") tool_input = data.get("tool_input", {}) preview = str(tool_input)[:80] + ("…" if len(str(tool_input)) > 80 else "") + self._out.write(self._separator(event.agent_name, "TOOL USE", color=self._magenta)) self._out.write( f" {self._yellow}⚙{self._reset} [{event.agent_name}] -> {label!r}" f" {self._dim}{preview}{self._reset}\n" @@ -164,6 +168,7 @@ def _handle_complete(self, event: StreamEvent) -> None: def _handle_error(self, event: StreamEvent) -> None: self._break() self._mode = None + self._out.write(self._separator(event.agent_name, "ERROR", color=self._red)) msg = event.data.get("message", "unknown error") exc_type = event.data.get("exception_type") if exc_type and msg.startswith(f"{exc_type}: "): diff --git a/tests/unit/renderers/test_ansi.py b/tests/unit/renderers/test_ansi.py index f0962e9..9d8c731 100644 --- a/tests/unit/renderers/test_ansi.py +++ b/tests/unit/renderers/test_ansi.py @@ -66,8 +66,10 @@ def test_agent_start_breaks_token_stream(self) -> None: def test_agent_start(self) -> None: r, buf = self._renderer() r.render(_event(EventType.AGENT_START)) - assert AGENT in buf.getvalue() - assert "starting" in buf.getvalue() + output = buf.getvalue() + assert AGENT in output + assert "starting" in output + assert "AGENT START" in output def test_tool_start(self) -> None: r, buf = self._renderer() @@ -75,6 +77,7 @@ def test_tool_start(self) -> None: output = buf.getvalue() assert "search" in output assert "⚙" in output + assert "TOOL USE" in output def test_tool_end_success(self) -> None: r, buf = self._renderer() @@ -103,6 +106,14 @@ def test_error(self) -> None: assert "ERROR" in output assert "something broke" in output + def test_error_separator_appears_before_detail(self) -> None: + r, buf = self._renderer() + r.render(_event(EventType.ERROR, message="something broke")) + output = buf.getvalue() + separator_idx = output.index("ERROR") + detail_idx = output.index("something broke") + assert separator_idx < detail_idx + def test_node_start(self) -> None: r, buf = self._renderer() r.render(_event(EventType.NODE_START, node_id="n1"))