From 4d7d9d971556d0ecf069ef7fceb5c3a24d28a803 Mon Sep 17 00:00:00 2001 From: teknium1 Date: Sun, 8 Mar 2026 19:54:32 -0700 Subject: [PATCH] fix: add diagnostic logging to browser tool for errors.log MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All failure paths in _run_browser_command now log at WARNING level, which means they automatically land in ~/.hermes/logs/errors.log (the persistent error log captures WARNING+). What's now logged: - agent-browser CLI not found (warning) - Session creation failure with task ID (warning) - Command entry with socket_dir path and length (debug) - Non-zero return code with stderr (warning) - Non-JSON output from agent-browser (warning — version mismatch/crash) - Command timeout with task ID and socket path (warning) - Unexpected exceptions with full traceback (warning + exc_info) - browser_vision: which model is used and screenshot size (debug) - browser_vision: LLM analysis failure with full traceback (warning) Also fixed: _get_vision_model() was called twice in browser_vision — now called once and reused. --- tools/browser_tool.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/tools/browser_tool.py b/tools/browser_tool.py index 3985e7614..d238d1435 100644 --- a/tools/browser_tool.py +++ b/tools/browser_tool.py @@ -802,6 +802,7 @@ def _run_browser_command( try: browser_cmd = _find_agent_browser() except FileNotFoundError as e: + logger.warning("agent-browser CLI not found: %s", e) return {"success": False, "error": str(e)} from tools.interrupt import is_interrupted @@ -812,6 +813,7 @@ def _run_browser_command( try: session_info = _get_session_info(task_id) except Exception as e: + logger.warning("Failed to create browser session for task=%s: %s", task_id, e) return {"success": False, "error": f"Failed to create browser session: {str(e)}"} # Build the command with the appropriate backend flag. @@ -841,6 +843,8 @@ def _run_browser_command( f"agent-browser-{session_info['session_name']}" ) os.makedirs(task_socket_dir, mode=0o700, exist_ok=True) + logger.debug("browser cmd=%s task=%s socket_dir=%s (%d chars)", + command, task_id, task_socket_dir, len(task_socket_dir)) browser_env = {**os.environ} # Ensure PATH includes standard dirs (systemd services may have minimal PATH) @@ -882,22 +886,29 @@ def _run_browser_command( "returncode=%s", result.returncode) return parsed except json.JSONDecodeError: - # If not valid JSON, return as raw output + # Non-JSON output indicates agent-browser crash or version mismatch + raw = result.stdout.strip()[:500] + logger.warning("browser '%s' returned non-JSON output (rc=%s): %s", + command, result.returncode, raw[:200]) return { "success": True, - "data": {"raw": result.stdout.strip()} + "data": {"raw": raw} } # Check for errors if result.returncode != 0: error_msg = result.stderr.strip() if result.stderr else f"Command failed with code {result.returncode}" + logger.warning("browser '%s' failed (rc=%s): %s", command, result.returncode, error_msg[:300]) return {"success": False, "error": error_msg} return {"success": True, "data": {}} except subprocess.TimeoutExpired: + logger.warning("browser '%s' timed out after %ds (task=%s, socket_dir=%s)", + command, timeout, task_id, task_socket_dir) return {"success": False, "error": f"Command timed out after {timeout} seconds"} except Exception as e: + logger.warning("browser '%s' exception: %s", command, e, exc_info=True) return {"success": False, "error": str(e)} @@ -1426,8 +1437,11 @@ def browser_vision(question: str, task_id: Optional[str] = None) -> str: # Use the sync auxiliary vision client directly from agent.auxiliary_client import auxiliary_max_tokens_param + vision_model = _get_vision_model() + logger.debug("browser_vision: analysing screenshot (%d bytes) with model=%s", + len(image_data), vision_model) response = _aux_vision_client.chat.completions.create( - model=_get_vision_model(), + model=vision_model, messages=[ { "role": "user", @@ -1453,6 +1467,7 @@ def browser_vision(question: str, task_id: Optional[str] = None) -> str: # in the LLM vision analysis, not the capture. Deleting a valid # screenshot loses evidence the user might need. The 24-hour cleanup # in _cleanup_old_screenshots prevents unbounded disk growth. + logger.warning("browser_vision failed: %s", e, exc_info=True) error_info = {"success": False, "error": f"Error during vision analysis: {str(e)}"} if screenshot_path.exists(): error_info["screenshot_path"] = str(screenshot_path)