From 5b3f708fcb440cf3d81da92952dc11fa9855e321 Mon Sep 17 00:00:00 2001 From: teknium1 Date: Sat, 21 Feb 2026 00:27:35 -0800 Subject: [PATCH] feat: enhance stale daemon cleanup and improve error logging in browser tool - Updated the stale daemon cleanup function to support multiple patterns for identifying orphaned agent-browser processes, improving reliability across different versions. - Added logging for stderr output during browser command execution to aid in diagnostics, particularly for capturing warnings from the agent-browser. - Implemented a warning for empty snapshots returned from the agent-browser, indicating potential issues with stale daemons or CDP connections. --- tools/browser_tool.py | 59 ++++++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/tools/browser_tool.py b/tools/browser_tool.py index e0048f3a2..516032335 100644 --- a/tools/browser_tool.py +++ b/tools/browser_tool.py @@ -645,28 +645,41 @@ def _get_browserbase_config() -> Dict[str, str]: _stale_daemons_cleaned = False def _kill_stale_agent_browser_daemons(): - """Kill any orphaned agent-browser daemon processes from previous runs.""" + """Kill any orphaned agent-browser daemon processes from previous runs. + + Uses multiple patterns to catch daemons from different agent-browser versions, + since the daemon process name/args can vary between releases. + """ global _stale_daemons_cleaned if _stale_daemons_cleaned: return _stale_daemons_cleaned = True - try: - result = subprocess.run( - ["pgrep", "-f", "agent-browser.*daemon"], - capture_output=True, text=True, timeout=5 - ) - pids = result.stdout.strip().split() - if pids and pids[0]: + patterns = [ + "agent-browser.*daemon", + "agent-browser/.*dist/daemon", + ] + killed_pids = set() + + for pattern in patterns: + try: + result = subprocess.run( + ["pgrep", "-f", pattern], + capture_output=True, text=True, timeout=5 + ) + pids = result.stdout.strip().split() for pid in pids: - try: - os.kill(int(pid), signal.SIGTERM) - except (ProcessLookupError, ValueError, PermissionError): - pass - if not os.getenv("HERMES_QUIET"): - print(f"[browser_tool] Cleaned up {len(pids)} stale daemon process(es)", file=sys.stderr) - except Exception: - pass + if pid and pid not in killed_pids: + try: + os.kill(int(pid), signal.SIGTERM) + killed_pids.add(pid) + except (ProcessLookupError, ValueError, PermissionError): + pass + except Exception: + pass + + if killed_pids and not os.getenv("HERMES_QUIET"): + print(f"[browser_tool] Cleaned up {len(killed_pids)} stale daemon process(es)", file=sys.stderr) def _find_agent_browser() -> str: @@ -771,10 +784,22 @@ def _run_browser_command( env=browser_env, ) + # Log stderr for diagnostics (agent-browser may emit warnings there) + if result.stderr and result.stderr.strip() and not os.getenv("HERMES_QUIET"): + print(f"[browser_tool] stderr from '{command}': {result.stderr.strip()[:200]}", file=sys.stderr) + # Parse JSON output if result.stdout.strip(): try: - return json.loads(result.stdout.strip()) + parsed = json.loads(result.stdout.strip()) + # Warn if snapshot came back empty (common sign of daemon/CDP issues) + if command == "snapshot" and parsed.get("success"): + snap_data = parsed.get("data", {}) + if not snap_data.get("snapshot") and not snap_data.get("refs"): + print(f"[browser_tool] WARNING: snapshot returned empty content. " + f"Possible stale daemon or CDP connection issue. " + f"returncode={result.returncode}", file=sys.stderr) + return parsed except json.JSONDecodeError: # If not valid JSON, return as raw output return {