Description
When using the browser tool in the CLI, agent-browser daemon processes () accumulate over time and eventually cause the CLI to stop accepting keyboard input (prompt_toolkit becomes unresponsive — input field is visible but nothing can be typed).
Steps to Reproduce
- Start a normal
hermes CLI session
- Use browser tool heavily (navigate, click, snapshot across multiple turns)
- After several tool-heavy turns (~5-6 browser interactions), CLI input freezes
- User is forced to close the terminal tab (killing the process)
- Repeat 3-4 times in a day — each time orphans additional daemons
Root Cause
The browser tool spawns agent-browser daemon processes that are cleaned up via atexit.register(_emergency_cleanup_all_sessions). However:
- When the CLI freezes due to prompt_toolkit corruption, the user must force-kill the terminal
- Force-killing bypasses
atexit handlers, leaving daemons orphaned
- The orphan reaper () runs on clean session startup, but may not catch all cases
- Over a day of usage, 10+ orphaned daemons can accumulate
Evidence
Before cleanup — 11 orphaned agent-browser daemons found, oldest from 4:25 AM:
chlinder 29687 ?? Ss 12:15PM 0:02.37 node .../agent-browser/dist/daemon.js
chlinder 11603 ?? Ss 5:52AM 0:02.67 node .../agent-browser/dist/daemon.js
chlinder 11109 ?? Ss 5:41AM 0:02.54 node .../agent-browser/dist/daemon.js
... (8 more)
Environment
- OS: macOS (11-year-old Mac)
- Hermes version: latest (git install)
- Model: xiaomi/mimo-v2-pro via Nous Portal
- Platform: CLI (terminal)
Suggested Fixes
-
Signal-based cleanup: Revisit the SIGINT/SIGTERM handler approach. The comment in browser_tool.py mentions this conflicts with prompt_toolkit, but a handler that only calls _emergency_cleanup_all_sessions() (without sys.exit()) and then re-raises the signal might avoid the corruption.
-
Per-daemon heartbeat: Each daemon writes a heartbeat file with its owner PID. A startup sweep already exists but could be more aggressive.
-
Daemon limit: Cap the number of concurrent agent-browser daemons per user (e.g., 3). Oldest gets killed when limit is exceeded.
-
Session-level cleanup on browser_tool init: Run the orphan reaper every time the browser tool is first used in a session, not just at process startup.
Description
When using the browser tool in the CLI, agent-browser daemon processes () accumulate over time and eventually cause the CLI to stop accepting keyboard input (prompt_toolkit becomes unresponsive — input field is visible but nothing can be typed).
Steps to Reproduce
hermesCLI sessionRoot Cause
The browser tool spawns agent-browser daemon processes that are cleaned up via
atexit.register(_emergency_cleanup_all_sessions). However:atexithandlers, leaving daemons orphanedEvidence
Before cleanup — 11 orphaned agent-browser daemons found, oldest from 4:25 AM:
Environment
Suggested Fixes
Signal-based cleanup: Revisit the SIGINT/SIGTERM handler approach. The comment in
browser_tool.pymentions this conflicts with prompt_toolkit, but a handler that only calls_emergency_cleanup_all_sessions()(withoutsys.exit()) and then re-raises the signal might avoid the corruption.Per-daemon heartbeat: Each daemon writes a heartbeat file with its owner PID. A startup sweep already exists but could be more aggressive.
Daemon limit: Cap the number of concurrent agent-browser daemons per user (e.g., 3). Oldest gets killed when limit is exceeded.
Session-level cleanup on browser_tool init: Run the orphan reaper every time the browser tool is first used in a session, not just at process startup.