fix(bot-screen): AGENT_BROWSER_PROFILE honours ~ and relative paths instead of silently ignoring them

profile_dir() accepted only an absolute AGENT_BROWSER_PROFILE; `~/pin` and `pin`
fell back to the default user-data-dir while the docs said setting the variable
pins your own. The dock's Browser icon and agent-browser both derive their
identity from profile_dir(), so the silent fallback was at least confusing and,
combined with any other consumer of the raw value, a way to end up on two jars.

`~` is expanded and a relative path is anchored at the profile's HERMES_HOME (the
same root runtime.state_dir() uses), so per-profile pins stay per-profile. The
docstring and the bot-screen docs state the resolution rule.

Contributor PR #112708 (@Rook-CodeVolt) proposed documenting "absolute only";
this takes the behavioural fix instead.

Closes #110029
This commit is contained in:
teknium1
2026-09-16 12:55:55 -07:00
committed by Teknium
parent a6240f4974
commit 56613ebf2b
3 changed files with 28 additions and 5 deletions

View File

@@ -6,6 +6,7 @@ from __future__ import annotations
import os
import socket
from pathlib import Path
from tools.bot_desktop import browser, runtime
@@ -31,6 +32,24 @@ def test_user_pinned_profile_wins(tmp_path, monkeypatch):
assert browser.profile_dir() == tmp_path / "mine"
def test_pinned_profile_honours_tilde_and_resolves_relative_paths_against_hermes_home(tmp_path, monkeypatch):
"""Regression for #110029: the docs say setting AGENT_BROWSER_PROFILE pins your own user-data-dir, but only
an absolute value was honoured — `~/pin` and `pin` silently fell back to the default and the human's dock
browser and the agent's browser could end up on different jars. A relative path is anchored where the rest
of this profile's screen state lives (its HERMES_HOME), so two profiles never share one 'pin'."""
home = tmp_path / "hermes-home"
monkeypatch.setenv("HERMES_HOME", str(home))
monkeypatch.setattr(runtime, "get_hermes_home", lambda: home)
monkeypatch.setattr(Path, "home", lambda: tmp_path / "user")
monkeypatch.setenv("HOME", str(tmp_path / "user"))
monkeypatch.setenv("AGENT_BROWSER_PROFILE", "~/pin")
assert browser.profile_dir() == tmp_path / "user" / "pin"
monkeypatch.setenv("AGENT_BROWSER_PROFILE", "pin")
assert browser.profile_dir() == home / "pin"
assert browser.env_for_agent({})["AGENT_BROWSER_PROFILE"] == str(home / "pin"), "agent-browser gets the resolved path"
def test_dock_browser_advertises_a_devtools_port():
"""A human-started instance must be attachable, or the agent can never drive it afterwards."""
assert "--remote-debugging-port=" in browser.dock_argv("/opt/chrome", "/p/dir")[2]

View File

@@ -26,10 +26,12 @@ _SYSTEM_BROWSERS = ("google-chrome", "google-chrome-stable", "chromium", "chromi
def profile_dir() -> Path:
"""User-data-dir the bot's browser uses on this profile's screen (``AGENT_BROWSER_PROFILE`` wins)."""
override = os.environ.get("AGENT_BROWSER_PROFILE", "").strip()
if override and os.path.isabs(override):
return Path(override)
"""User-data-dir the bot's browser uses on this profile's screen. ``AGENT_BROWSER_PROFILE`` pins your own:
``~`` expands, and a relative path is anchored at this profile's HERMES_HOME (where the rest of the screen's
state lives), so ``pin`` means ``<HERMES_HOME>/pin`` and two profiles never share one jar by accident."""
override = os.path.expanduser(os.environ.get("AGENT_BROWSER_PROFILE", "").strip())
if override:
return Path(override) if os.path.isabs(override) else runtime.get_hermes_home() / override
return runtime.state_dir() / "browser-profile"

View File

@@ -155,7 +155,9 @@ controller drops back to watching.
While the screen runs, the bot's browser tool and the dock's **Browser** icon are
the same browser: the Chromium agent-browser drives, with one persistent
user-data-dir per bot (`<HERMES_HOME>/bot-desktop/browser-profile`; set
`AGENT_BROWSER_PROFILE` to pin your own). Click Browser during a takeover and you
`AGENT_BROWSER_PROFILE` to pin your own — `~` expands, and a relative path such
as `pin` resolves against that bot's `HERMES_HOME`, i.e. `<HERMES_HOME>/pin`).
Click Browser during a takeover and you
are in the bot's own windows and cookie jar; what you sign in to is what the bot
uses afterwards and in every later session, until the site expires the login.
Set `browser.headed: true` so the bot's own browsing is visible on the screen too.