diff --git a/src/cai/tools/reconnaissance/generic_linux_command.py b/src/cai/tools/reconnaissance/generic_linux_command.py index eb0624d7..552e7b3f 100644 --- a/src/cai/tools/reconnaissance/generic_linux_command.py +++ b/src/cai/tools/reconnaissance/generic_linux_command.py @@ -133,11 +133,15 @@ async def generic_linux_command(command: str = "", else: timeout = 100 - # Tools always stream EXCEPT in parallel mode + # Tools always stream EXCEPT in parallel mode or when CAI_STREAM=False # In parallel mode, multiple agents run concurrently with Runner.run() # and streaming would create confusing overlapping outputs stream = True # Default to streaming + # Check if CAI_STREAM is explicitly set to False + if os.getenv("CAI_STREAM", "true").lower() == "false": + stream = False + # Simple heuristic: If CAI_PARALLEL > 1 AND we have a P agent ID, disable streaming # This is more reliable than trying to count active agents try: diff --git a/src/cai/util.py b/src/cai/util.py index 28276842..ddf92545 100644 --- a/src/cai/util.py +++ b/src/cai/util.py @@ -2901,7 +2901,11 @@ def cli_print_tool_output( # Use simple output _print_simple_tool_output(tool_name, args, output, execution_info, token_info) return - else: + + # Initialize is_first_display for later use + is_first_display = False + + if not streaming: # For non-streaming outputs, check if we've already seen this command streaming_enabled = os.getenv("CAI_STREAM", "false").lower() == "true" @@ -2928,11 +2932,11 @@ def cli_print_tool_output( if not output: return + # Check if this is first time display before adding to displayed commands + is_first_display = command_key not in cli_print_tool_output._displayed_commands + # Add to displayed commands since we're going to show it cli_print_tool_output._displayed_commands.add(command_key) - - # Track display time for better duplicate detection - cli_print_tool_output._command_display_times[command_key] = time.time() # For non-streaming updates with call_id, check if already seen # This _seen_calls logic is an additional layer for non-streaming calls that might have call_ids @@ -3067,12 +3071,62 @@ def cli_print_tool_output( title_align="left", ) + # When CAI_STREAM=false and this is the first display (not a duplicate), + # show a small command execution panel first + if not streaming_enabled and not streaming and is_first_display: + # Get agent name for the panel + agent_name = "" + if token_info and token_info.get("agent_name"): + agent_name = token_info.get("agent_name") + else: + agent_name = "Agent" + + # Extract the command from args + command_text = "" + if isinstance(display_args, dict): + if "command" in display_args: + command_text = display_args.get("command", "") + if "args" in display_args and display_args["args"]: + command_text += f" {display_args['args']}" + elif "full_command" in display_args: + command_text = display_args.get("full_command", "") + else: + # Fallback to string representation + command_text = str(display_args) + else: + command_text = str(display_args) + + # Create a small panel showing just the command being executed + command_panel = Panel( + f"[bold cyan]{command_text}[/bold cyan]", + title=f"[bold blue]{agent_name} - Executing Command[/bold blue]", + border_style="blue", + padding=(0, 1), + box=ROUNDED, + title_align="left", + width=None, # Auto width based on content + expand=False # Don't expand to full width + ) + + # Print the command panel + console.print(command_panel) + console.print() # Add spacing between panels + # Display the panel console.print(panel) + + # Track display time AFTER the panel is rendered + # This ensures accurate timing for duplicate detection + if not streaming and command_key: + cli_print_tool_output._command_display_times[command_key] = time.time() except (ImportError, Exception): # Fall back to simple output format without rich _print_simple_tool_output(tool_name, args, output, execution_info, token_info) + + # Also track display time for simple output + if not streaming and command_key: + cli_print_tool_output._command_display_times[command_key] = time.time() # Helper function to format tool arguments