From 99bbea32ec38fac48bbc0e6f832bb3e6134be206 Mon Sep 17 00:00:00 2001 From: famez Date: Sun, 15 Feb 2026 12:23:30 +0100 Subject: [PATCH] Added ToolResultMessage again, but only visible when clicking over the tool widget. --- pentestagent/interface/tui.py | 52 +++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/pentestagent/interface/tui.py b/pentestagent/interface/tui.py index f79d9f3..384eef2 100644 --- a/pentestagent/interface/tui.py +++ b/pentestagent/interface/tui.py @@ -648,28 +648,50 @@ class ThinkingMessage(Static): class ToolMessage(Static): """Tool execution message""" - # Standard tool icon and color (pa theme) TOOL_ICON = "$" - TOOL_COLOR = "#9a9a9a" # spirit gray + TOOL_COLOR = "#9a9a9a" + + expanded: bool = reactive(False, layout=True) def __init__(self, tool_name: str, args: str = "", **kwargs): super().__init__(**kwargs) self.tool_name = tool_name self.tool_args = args + self._result_widget: ToolResultMessage | None = None def render(self) -> Text: text = Text() text.append(f"{self.TOOL_ICON} ", style=self.TOOL_COLOR) - text.append(f"{self.tool_name}", style=self.TOOL_COLOR) - text.append("\n", style="") + text.append(self.tool_name, style=self.TOOL_COLOR) + text.append("\n") - # Wrap args if self.tool_args: for line in wrap_text_lines(self.tool_args, width=110): text.append(f" {line}\n", style="#6b6b6b") return text + def attach_result(self, result_widget: "ToolResultMessage") -> None: + """Attach a ToolResultMessage widget below this message.""" + + # Prevent double mounting + if self._result_widget is not None: + return + + self._result_widget = result_widget + self._result_widget.display = self.expanded + + # Mount immediately after this widget + self.mount(self._result_widget, after=self) + + + + def on_click(self) -> None: + self.expanded = not self.expanded + if self._result_widget: + self._result_widget.display = self.expanded + + class ToolResultMessage(Static): """Tool result/output message""" @@ -1685,14 +1707,14 @@ class PentestAgentTUI(App): def _add_thinking(self, content: str) -> None: self._add_message(ThinkingMessage(content)) - def _add_tool(self, name: str, action: str = "") -> None: - self._add_message(ToolMessage(name, action)) + def _add_tool(self, name: str, action: str = "") -> ToolMessage: + tool_message = ToolMessage(name, action) + self._add_message(tool_message) + return tool_message - def _add_tool_result(self, name: str, result: str) -> None: + def _add_tool_result(self, tool_message: ToolMessage, name: str, result: str) -> None: """Display tool execution result""" - # Hide tool output - LLM will synthesize it in its response - # This prevents duplication and keeps the chat clean - pass + tool_message.attach_result(ToolResultMessage(name, result)) def _show_system_prompt(self) -> None: """Display the current system prompt""" @@ -3215,6 +3237,8 @@ Be concise. Use the actual data from notes.""" from ..agents.base_agent import AgentState + last_tool_message: ToolMessage + async for response in self.agent.agent_loop(task): if self._should_stop: self._add_system("[!] Stopped by user") @@ -3241,7 +3265,7 @@ Be concise. Use the actual data from notes.""" for call in response.tool_calls: # Show all tools including finish args_str = str(call.arguments) - self._add_tool(call.name, args_str) + last_tool_message = self._add_tool(call.name, args_str) # Show tool results if response.tool_results: @@ -3251,11 +3275,11 @@ Be concise. Use the actual data from notes.""" continue if result.success: - self._add_tool_result( + self._add_tool_result(last_tool_message, result.tool_name, result.result or "Done" ) else: - self._add_tool_result( + self._add_tool_result(last_tool_message, result.tool_name, f"Error: {result.error}" )