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.
This commit is contained in:
teknium1 2026-02-21 00:27:35 -08:00
parent b33ed9176f
commit 5b3f708fcb

View file

@ -645,28 +645,41 @@ def _get_browserbase_config() -> Dict[str, str]:
_stale_daemons_cleaned = False _stale_daemons_cleaned = False
def _kill_stale_agent_browser_daemons(): 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 global _stale_daemons_cleaned
if _stale_daemons_cleaned: if _stale_daemons_cleaned:
return return
_stale_daemons_cleaned = True _stale_daemons_cleaned = True
try: patterns = [
result = subprocess.run( "agent-browser.*daemon",
["pgrep", "-f", "agent-browser.*daemon"], "agent-browser/.*dist/daemon",
capture_output=True, text=True, timeout=5 ]
) killed_pids = set()
pids = result.stdout.strip().split()
if pids and pids[0]: 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: for pid in pids:
try: if pid and pid not in killed_pids:
os.kill(int(pid), signal.SIGTERM) try:
except (ProcessLookupError, ValueError, PermissionError): os.kill(int(pid), signal.SIGTERM)
pass killed_pids.add(pid)
if not os.getenv("HERMES_QUIET"): except (ProcessLookupError, ValueError, PermissionError):
print(f"[browser_tool] Cleaned up {len(pids)} stale daemon process(es)", file=sys.stderr) pass
except Exception: except Exception:
pass 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: def _find_agent_browser() -> str:
@ -771,10 +784,22 @@ def _run_browser_command(
env=browser_env, 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 # Parse JSON output
if result.stdout.strip(): if result.stdout.strip():
try: 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: except json.JSONDecodeError:
# If not valid JSON, return as raw output # If not valid JSON, return as raw output
return { return {