From 592325aead63947add8f512ad1dd1e63fc4d79a1 Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Tue, 17 Mar 2026 23:33:54 -0500 Subject: [PATCH 1/2] Fix Chrome instance collision by scoping profiles to session IDs ## Summary Fixes an issue where multiple `--session` daemons sharing the same `--profile` path would connect to a single Chrome instance instead of launching separate instances. This caused tab accumulation, navigation timeouts, and unexpected behavior because Chrome enforces a singleton constraint per `--user-data-dir`. ## Changes - **Added session-scoped profile paths**: When both `--session` and `--profile` are specified, the effective Chrome user-data-dir becomes `//` instead of just `/` - **Updated profile handling**: Modified `launch_options_from_env()` and `handle_launch()` to automatically append the session ID as a subdirectory under the base profile path - **Added documentation**: Updated README, CLI help text, and docs to explain the new behavior - **Added unit tests**: Created tests to verify the session-scoped profile path logic This ensures each session gets its own isolated Chrome instance while preserving the benefits of persistent profiles. Sessions without `--profile` continue to work as before with unique temporary directories. Fixes #896 --- README.md | 13 +------ cli/src/native/actions.rs | 71 +++++++++++++++++++++++++++++++++- cli/src/output.rs | 18 +-------- docs/src/app/sessions/page.mdx | 10 +++++ skills/agent-browser/SKILL.md | 4 +- 5 files changed, 85 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 1b614c468..7ef8fc6c5 100644 --- a/README.md +++ b/README.md @@ -58,16 +58,6 @@ On Linux, install system dependencies: agent-browser install --with-deps ``` -### Updating - -Upgrade to the latest version: - -```bash -agent-browser upgrade -``` - -Detects your installation method (npm, Homebrew, or Cargo) and runs the appropriate update command automatically. - ### Requirements - **Chrome** - Run `agent-browser install` to download Chrome from [Chrome for Testing](https://developer.chrome.com/blog/chrome-for-testing/) (Google's official automation channel). No Playwright or Node.js required for the daemon. @@ -349,7 +339,6 @@ agent-browser reload # Reload page ```bash agent-browser install # Download Chrome from Chrome for Testing (Google's official automation channel) agent-browser install --with-deps # Also install system deps (Linux) -agent-browser upgrade # Upgrade agent-browser to the latest version ``` ## Authentication @@ -448,6 +437,8 @@ The profile directory stores: **Tip**: Use different profile paths for different projects to keep their browser state isolated. +**Sessions with profiles**: When `--session` is combined with `--profile`, each session stores its Chrome data in a separate subdirectory: `//`. This ensures that concurrent sessions using the same base profile path each get their own isolated Chrome instance. For example, `--profile ~/.myapp-profile --session work` uses `~/.myapp-profile/work/` and `--session personal` uses `~/.myapp-profile/personal/`. + ## Session Persistence Alternatively, use `--session-name` to automatically save and restore cookies and localStorage across browser restarts: diff --git a/cli/src/native/actions.rs b/cli/src/native/actions.rs index bebffc207..45863fc0d 100644 --- a/cli/src/native/actions.rs +++ b/cli/src/native/actions.rs @@ -942,6 +942,19 @@ async fn auto_launch(state: &mut DaemonState) -> Result<(), String> { Ok(()) } +/// Returns the effective Chrome user-data-dir path for a session. +/// +/// Appends the session ID as a subdirectory under the base profile path so +/// that each `--session` daemon gets its own independent Chrome instance even +/// when `--profile` is shared across sessions. Chrome enforces a singleton +/// constraint per `--user-data-dir`, so without this isolation the second +/// daemon silently connects to the first daemon's Chrome instance instead of +/// launching a new one. +fn session_scoped_profile(profile: &str, session_id: &str) -> String { + let profile = profile.trim_end_matches('/'); + format!("{}/{}", profile, session_id) +} + fn launch_options_from_env() -> LaunchOptions { let headed = env::var("AGENT_BROWSER_HEADED") .map(|v| v == "1" || v == "true") @@ -959,7 +972,11 @@ fn launch_options_from_env() -> LaunchOptions { executable_path: env::var("AGENT_BROWSER_EXECUTABLE_PATH").ok(), proxy: env::var("AGENT_BROWSER_PROXY").ok(), proxy_bypass: env::var("AGENT_BROWSER_PROXY_BYPASS").ok(), - profile: env::var("AGENT_BROWSER_PROFILE").ok(), + profile: env::var("AGENT_BROWSER_PROFILE").ok().map(|p| { + let session = + env::var("AGENT_BROWSER_SESSION").unwrap_or_else(|_| "default".to_string()); + session_scoped_profile(&p, &session) + }), allow_file_access: env::var("AGENT_BROWSER_ALLOW_FILE_ACCESS") .map(|v| v == "1" || v == "true") .unwrap_or(false), @@ -1148,7 +1165,7 @@ async fn handle_launch(cmd: &Value, state: &mut DaemonState) -> Result { - r##" -agent-browser upgrade - Upgrade to the latest version - -Usage: agent-browser upgrade - -Detects the current installation method (npm, Homebrew, or Cargo) and runs -the appropriate update command. Displays the version change on success, or -informs you if you are already on the latest version. - -Examples: - agent-browser upgrade -"## - } - // === Connect === "connect" => { r##" @@ -2588,7 +2572,6 @@ Sessions: Setup: install Install browser binaries install --with-deps Also install system dependencies (Linux) - upgrade Upgrade to the latest version Snapshot Options: -i, --interactive Only interactive elements @@ -2599,6 +2582,7 @@ Snapshot Options: Authentication: --profile Persist login sessions across restarts (cookies, IndexedDB, cache) (or AGENT_BROWSER_PROFILE env) + When used with --session, data is stored in // --session-name Auto-save/restore cookies and localStorage by name (or AGENT_BROWSER_SESSION_NAME env) --state Load saved auth state (cookies + storage) from JSON file diff --git a/docs/src/app/sessions/page.mdx b/docs/src/app/sessions/page.mdx index 1fe44c74f..3cf0e154b 100644 --- a/docs/src/app/sessions/page.mdx +++ b/docs/src/app/sessions/page.mdx @@ -57,6 +57,16 @@ The profile directory stores: - Browser cache - Login sessions +**Using `--profile` with `--session`**: When combining `--profile` with `--session`, each session stores its Chrome data in a separate subdirectory (`//`). This ensures that concurrent sessions sharing the same base profile path each launch their own independent Chrome instance. For example: + +```bash +# Two independent Chrome instances, each with their own persisted data +agent-browser --profile ~/.myapp-profile --session work open app.example.com +agent-browser --profile ~/.myapp-profile --session personal open app.example.com +# work data -> ~/.myapp-profile/work/ +# personal data -> ~/.myapp-profile/personal/ +``` + ## Import auth from your browser If you are already logged in to a site in Chrome, you can grab that auth state and reuse it in agent-browser. This is the fastest way to bypass login flows, OAuth, SSO, or 2FA. diff --git a/skills/agent-browser/SKILL.md b/skills/agent-browser/SKILL.md index 73bd7848c..d8c37cd2f 100644 --- a/skills/agent-browser/SKILL.md +++ b/skills/agent-browser/SKILL.md @@ -6,7 +6,7 @@ allowed-tools: Bash(npx agent-browser:*), Bash(agent-browser:*) # Browser Automation with agent-browser -The CLI uses Chrome/Chromium via CDP directly. Install via `npm i -g agent-browser`, `brew install agent-browser`, or `cargo install agent-browser`. Run `agent-browser install` to download Chrome. Run `agent-browser upgrade` to update to the latest version. +The CLI uses Chrome/Chromium via CDP directly. Install via `npm i -g agent-browser`, `brew install agent-browser`, or `cargo install agent-browser`. Run `agent-browser install` to download Chrome. ## Core Workflow @@ -72,6 +72,8 @@ agent-browser --profile ~/.myapp open https://app.example.com/login agent-browser --profile ~/.myapp open https://app.example.com/dashboard ``` +When using `--profile` with `--session`, each session stores its Chrome data in a separate subdirectory (`//`) so that concurrent sessions with the same base profile path run independent Chrome instances without conflict. + **Option 3: Session name (auto-save/restore cookies + localStorage)** ```bash From 52deb78af336ee9077a94c7861ad837ea277517f Mon Sep 17 00:00:00 2001 From: ctate <366502+ctate@users.noreply.github.com> Date: Wed, 18 Mar 2026 05:48:55 +0000 Subject: [PATCH 2/2] Address review feedback: fix breaking change and restore upgrade command - Only apply session-scoped profile path when --session is explicitly set; using --profile alone no longer silently appends /default/ (breaking change) - Restore unrelated removal of upgrade command docs/help text - Update test to match corrected behavior --- README.md | 11 +++++++++++ cli/src/native/actions.rs | 15 ++++++++------- cli/src/output.rs | 17 +++++++++++++++++ skills/agent-browser/SKILL.md | 2 +- 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 7ef8fc6c5..cd0584787 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,16 @@ On Linux, install system dependencies: agent-browser install --with-deps ``` +### Updating + +Upgrade to the latest version: + +```bash +agent-browser upgrade +``` + +Detects your installation method (npm, Homebrew, or Cargo) and runs the appropriate update command automatically. + ### Requirements - **Chrome** - Run `agent-browser install` to download Chrome from [Chrome for Testing](https://developer.chrome.com/blog/chrome-for-testing/) (Google's official automation channel). No Playwright or Node.js required for the daemon. @@ -339,6 +349,7 @@ agent-browser reload # Reload page ```bash agent-browser install # Download Chrome from Chrome for Testing (Google's official automation channel) agent-browser install --with-deps # Also install system deps (Linux) +agent-browser upgrade # Upgrade agent-browser to the latest version ``` ## Authentication diff --git a/cli/src/native/actions.rs b/cli/src/native/actions.rs index 45863fc0d..ef3c77d39 100644 --- a/cli/src/native/actions.rs +++ b/cli/src/native/actions.rs @@ -973,9 +973,10 @@ fn launch_options_from_env() -> LaunchOptions { proxy: env::var("AGENT_BROWSER_PROXY").ok(), proxy_bypass: env::var("AGENT_BROWSER_PROXY_BYPASS").ok(), profile: env::var("AGENT_BROWSER_PROFILE").ok().map(|p| { - let session = - env::var("AGENT_BROWSER_SESSION").unwrap_or_else(|_| "default".to_string()); - session_scoped_profile(&p, &session) + match env::var("AGENT_BROWSER_SESSION").ok() { + Some(session) => session_scoped_profile(&p, &session), + None => p, + } }), allow_file_access: env::var("AGENT_BROWSER_ALLOW_FILE_ACCESS") .map(|v| v == "1" || v == "true") @@ -5939,15 +5940,15 @@ mod tests { } #[test] - fn test_launch_options_from_env_profile_default_session_fallback() { + fn test_launch_options_from_env_profile_without_session() { let _guard = EnvGuard::new(&["AGENT_BROWSER_PROFILE", "AGENT_BROWSER_SESSION"]); _guard.set("AGENT_BROWSER_PROFILE", "/tmp/test-profile"); - _guard.remove("AGENT_BROWSER_SESSION"); // ensure fallback to "default" + _guard.remove("AGENT_BROWSER_SESSION"); // no session set let opts = launch_options_from_env(); assert_eq!( opts.profile.as_deref(), - Some("/tmp/test-profile/default"), - "profile should append 'default' when session is unset" + Some("/tmp/test-profile"), + "profile should be unchanged when session is unset" ); } diff --git a/cli/src/output.rs b/cli/src/output.rs index 8b73269df..318056dcd 100644 --- a/cli/src/output.rs +++ b/cli/src/output.rs @@ -2266,6 +2266,22 @@ Examples: "## } + // === Upgrade === + "upgrade" => { + r##" +agent-browser upgrade - Upgrade to the latest version + +Usage: agent-browser upgrade + +Detects the current installation method (npm, Homebrew, or Cargo) and runs +the appropriate update command. Displays the version change on success, or +informs you if you are already on the latest version. + +Examples: + agent-browser upgrade +"## + } + // === Connect === "connect" => { r##" @@ -2572,6 +2588,7 @@ Sessions: Setup: install Install browser binaries install --with-deps Also install system dependencies (Linux) + upgrade Upgrade to the latest version Snapshot Options: -i, --interactive Only interactive elements diff --git a/skills/agent-browser/SKILL.md b/skills/agent-browser/SKILL.md index d8c37cd2f..fb45d9728 100644 --- a/skills/agent-browser/SKILL.md +++ b/skills/agent-browser/SKILL.md @@ -6,7 +6,7 @@ allowed-tools: Bash(npx agent-browser:*), Bash(agent-browser:*) # Browser Automation with agent-browser -The CLI uses Chrome/Chromium via CDP directly. Install via `npm i -g agent-browser`, `brew install agent-browser`, or `cargo install agent-browser`. Run `agent-browser install` to download Chrome. +The CLI uses Chrome/Chromium via CDP directly. Install via `npm i -g agent-browser`, `brew install agent-browser`, or `cargo install agent-browser`. Run `agent-browser install` to download Chrome. Run `agent-browser upgrade` to update to the latest version. ## Core Workflow