#!/usr/bin/env python3 """ Interactive Live Browser Testing Suite for miniswardbower. Opens a real headed browser window on your display so you can watch the 5-agent team navigate, fill forms, execute searches, and verify state in real time. Keeps the browser open for manual inspection and verification by hand. """ import argparse import asyncio import os import sys import time from pathlib import Path # Ensure canopy and miniswardbower are in python path sys.path.insert(0, "/home/psikosen/canopy") sys.path.insert(0, "/home/psikosen/canopy/miniswardbower") from miniswardbower.agents.team_runner import MultiAgentBrowserTeam from miniswardbower.core.config import SystemConfig from miniswardbower.evals.mock_web_env import MockWebEnvironment async def run_interactive_test( workflow: str = "form", custom_url: str = None, custom_objective: str = None, speed: str = "demo", pause_at_end: bool = True, ): # Ensure display is available if "DISPLAY" not in os.environ: os.environ["DISPLAY"] = ":1" print("=" * 72) print("🌐 miniswardbower Interactive Live Browser Tester") print(f" Display: {os.environ.get('DISPLAY')} | Speed Mode: {speed}") print("=" * 72) mock_env = None target_url = custom_url objective = custom_objective if not custom_url: mock_env = MockWebEnvironment(port=8998) mock_env.start() time.sleep(0.4) if workflow == "form": target_url = mock_env.form_url objective = f"Navigate to {target_url} and fill the authorized representative form with client info and submit" else: target_url = mock_env.search_url objective = f"Navigate to {target_url} and search for 'mechanical keyboard'" print(f"\n[1] Starting Local Test Environment...") print(f" Target URL: {target_url}") print(f" Objective: {objective}") config = SystemConfig() config.browser.headless = False # Headed: opens real browser window on screen! config.enable_speculative_batching = True team = MultiAgentBrowserTeam(config) print("\n[2] Launching Headed Chromium on your screen...") print(" Watch the live browser window for:") print(" • Set-of-Marks tags (@e1, @e2, ...) over interactive elements") print(" • Top status HUD showing real-time agent coordination") print(" • Green action glow when inputs are typed and buttons clicked\n") start_time = time.perf_counter() try: results = await team.run_task( objective=objective, start_url=target_url, max_steps=15, ) elapsed = time.perf_counter() - start_time print("-" * 72) print(f"šŸŽÆ TASK EXECUTION COMPLETED in {elapsed:.2f}s!") print(f" Success Verified: {results['success']}") print(f" Total Actions Executed: {results['total_actions']}") print(f" Estimated Tokens Saved: {results['estimated_tokens_saved']}") print(f" Average Step Latency: {results['avg_step_ms']}ms") print("-" * 72) print("\nšŸ“œ Episodic Narrative Log:") for log in results["narrative_log"]: print(f" • Step {log['step']}: {log['action']} -> {log['outcome']} [{log['status']}]") if pause_at_end: print("\n" + "=" * 72) print("šŸ‘ļø BROWSER IS READY FOR MANUAL INSPECTION!") print(" You can now click around, inspect the inputs, or test by hand.") print(" Press [ENTER] in this terminal when you are done to close the browser.") print("=" * 72) # Run input in a thread so as not to block the event loop await asyncio.to_thread(input, "") finally: if mock_env: mock_env.stop() def main(): parser = argparse.ArgumentParser(description="miniswardbower Interactive Live Browser Tester") parser.add_argument( "--workflow", choices=["form", "search"], default="form", help="Preconfigured workflow to test: 'form' (form-filling) or 'search' (product search)", ) parser.add_argument("--url", type=str, default=None, help="Custom URL to test") parser.add_argument("--objective", type=str, default=None, help="Custom objective for the agent team") parser.add_argument( "--speed", choices=["fast", "demo"], default="demo", help="'fast' for maximum throughput, 'demo' for human-observable pacing", ) parser.add_argument( "--no-pause", action="store_true", help="Do not pause at end; close browser automatically upon task completion", ) args = parser.parse_args() asyncio.run( run_interactive_test( workflow=args.workflow, custom_url=args.url, custom_objective=args.objective, speed=args.speed, pause_at_end=not args.no_pause, ) ) if __name__ == "__main__": main()