pi-browser-use 0.9.5 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -39,24 +39,30 @@ export declare function runBootstrap(options: {
39
39
  profileDir?: string;
40
40
  executablePath?: string;
41
41
  chromeArgs?: string[];
42
+ signal?: AbortSignal;
42
43
  launch?: (options: {
43
44
  userDataDir: string;
44
45
  profileDirectory?: string;
45
46
  executablePath?: string;
46
47
  chromeArgs?: string[];
48
+ signal?: AbortSignal;
47
49
  }) => Promise<number | null>;
48
50
  }, events?: SetupFlowEvents): Promise<number | null>;
49
51
  export interface ReauthOptions {
50
- backend: PersistentBackend;
52
+ backend: Pick<PersistentBackend, 'profileDir' | 'restart'>;
51
53
  /** Page that needs auth (used for messaging + Variant B navigation hint). */
52
54
  url: string;
53
55
  variant?: ReauthVariant;
56
+ signal?: AbortSignal;
57
+ executablePath?: string;
58
+ chromeArgs?: string[];
54
59
  /** Plain-variant launcher (no CDP). Defaults to launchSetupBrowser. */
55
60
  launchPlain?: (options: {
56
61
  userDataDir: string;
57
62
  profileDirectory?: string;
58
63
  executablePath?: string;
59
64
  chromeArgs?: string[];
65
+ signal?: AbortSignal;
60
66
  }) => Promise<number | null>;
61
67
  /** Restart the backend headed/headless (Variant B + resume). */
62
68
  restartBackend?: (headed: boolean) => Promise<unknown>;
@@ -44,6 +44,7 @@ export function reauthInstructions(url, variant) {
44
44
  * window exit code after marking the profile initialized.
45
45
  */
46
46
  export async function runBootstrap(options, events) {
47
+ options.signal?.throwIfAborted();
47
48
  const profileDir = options.profileDir ?? DEFAULT_PROFILE_DIR;
48
49
  events?.onSetupNeeded?.(SETUP_INSTRUCTIONS);
49
50
  // Same named identity automation uses: sign in here, automate there.
@@ -54,7 +55,11 @@ export async function runBootstrap(options, events) {
54
55
  profileDirectory: PI_PROFILE_NAME,
55
56
  executablePath: options.executablePath,
56
57
  chromeArgs: options.chromeArgs,
58
+ signal: options.signal,
57
59
  });
60
+ options.signal?.throwIfAborted();
61
+ if (code !== 0)
62
+ throw new Error(`Browser setup did not complete successfully (exit ${code}).`);
58
63
  markBootstrapped(profileDir);
59
64
  events?.onSetupComplete?.(profileDir);
60
65
  return code;
@@ -67,13 +72,23 @@ export async function runBootstrap(options, events) {
67
72
  * and waits for close. Returns the user-facing instruction to relay.
68
73
  */
69
74
  export async function runReauth(options) {
75
+ options.signal?.throwIfAborted();
70
76
  const variant = options.variant ?? 'instrumented';
71
77
  const profileDir = options.backend.profileDir?.() ?? DEFAULT_PROFILE_DIR;
72
78
  const message = reauthInstructions(options.url, variant);
73
79
  options.events?.onReauthNeeded?.(message);
74
80
  if (variant === 'plain') {
75
81
  const launch = options.launchPlain ?? launchSetupBrowser;
76
- await launch({ userDataDir: profileDir, profileDirectory: PI_PROFILE_NAME });
82
+ const code = await launch({
83
+ userDataDir: profileDir,
84
+ profileDirectory: PI_PROFILE_NAME,
85
+ executablePath: options.executablePath,
86
+ chromeArgs: options.chromeArgs,
87
+ signal: options.signal,
88
+ });
89
+ options.signal?.throwIfAborted();
90
+ if (code !== 0)
91
+ throw new Error(`Browser verification did not complete successfully (exit ${code}).`);
77
92
  }
78
93
  else {
79
94
  if (options.restartBackend) {
@@ -83,6 +98,7 @@ export async function runReauth(options) {
83
98
  await options.backend.restart(true);
84
99
  }
85
100
  }
101
+ options.signal?.throwIfAborted();
86
102
  options.events?.onReauthComplete?.(profileDir);
87
103
  return message;
88
104
  }
@@ -110,8 +110,12 @@ export class TabBridge {
110
110
  const pollMs = options?.pollMs ?? 100;
111
111
  const deadline = Date.now() + timeoutMs;
112
112
  while (Date.now() < deadline) {
113
- if (options?.signal?.aborted)
113
+ if (options?.signal?.aborted) {
114
+ // Do not leave a cancelled request for the extension to consume; it
115
+ // would create an unmanaged tab after the caller has given up.
116
+ this.pending.delete(token);
114
117
  throw new Error('Tab wait aborted.');
118
+ }
115
119
  const done = this.completed.get(token);
116
120
  if (done) {
117
121
  this.completed.delete(token);
@@ -0,0 +1,131 @@
1
+ # Agent Plugins 1.0
2
+
3
+ One browser runtime, two host adapters: native Pi and a portable stdio MCP server.
4
+
5
+ ## Installation and distribution
6
+
7
+ Native Pi remains unchanged: `pi install npm:pi-browser-use`. It loads `dist/index.js`,
8
+ uses Pi's trusted settings, and retains `~/.pi/browser-profile` and `~/.pi/browser-artifacts`.
9
+ Do not enable both adapters in the same Pi session; that would register duplicate tools.
10
+
11
+ For a client supporting **Agent Plugins 1.0 and local stdio MCP**, install this npm
12
+ package into a user-controlled directory, then use that client's local-plugin
13
+ installation flow to select the **installed package root**, containing `plugin.json`,
14
+ `mcp.json`, `skills/`, and `dist/`. Node and the package's production dependencies must
15
+ be installed; a source-only Git clone is not a ready-to-run plugin.
16
+
17
+ For example, from an empty directory:
18
+
19
+ ```sh
20
+ npm install --omit=dev pi-browser-use
21
+ # Select this directory in the client's local-plugin loader:
22
+ # <absolute-install-directory>/node_modules/pi-browser-use
23
+ ```
24
+
25
+ To test an unpublished branch, use `npm ci && npm run build` in the checkout and
26
+ load that directory. The manifest starts `node ${PLUGIN_ROOT}/dist/mcp-server.js`;
27
+ there is no install-time shell hook, network package download, or hidden Pi dependency
28
+ in that command. Clients without local-plugin loading can register the same script
29
+ as an ordinary stdio MCP server and load the bundled skills separately.
30
+
31
+ Chrome/Chromium must be installed. Tool discovery and `browser_status` do not launch
32
+ Chrome, open a login window, or require a signed-in profile. The first browser action
33
+ starts the configured backend. A plugin-capable client that only supports remote MCP
34
+ cannot run this local browser server.
35
+
36
+ ## Configuration and private state
37
+
38
+ The manifest passes the client's `${PLUGIN_DATA}` as `PI_BROWSER_USE_DATA_DIR`.
39
+ The server reads only `<data>/config.json` (a direct `BrowserUseConfig` object):
40
+
41
+ ```json
42
+ {
43
+ "mode": "persistent",
44
+ "headed": false,
45
+ "tabBridgePort": 31973
46
+ }
47
+ ```
48
+
49
+ Missing configuration uses persistent headless defaults. Invalid JSON, unknown
50
+ fields, and invalid field types fail closed. Pi user settings, project `.pi` files,
51
+ and Pi model credentials are never read by the portable adapter. Environment
52
+ interpolation is not performed inside this configuration file.
53
+
54
+ The data layout is:
55
+
56
+ ```text
57
+ PLUGIN_DATA/
58
+ config.json
59
+ browser-profile/ # named pi-browser-use identity, cookies and site sessions
60
+ browser-profile.* # existing lock, metadata, preferences, ownership/advert files
61
+ artifacts/ # default screenshot and HTML destination
62
+ ```
63
+
64
+ The plugin root can be read-only after dependencies and build output are installed.
65
+ `browser_status` reports the actual profile and artifact paths; skills must use those
66
+ paths rather than assume `~/.pi`. Each client's plugin data is separate by default:
67
+ signing into one client does not authenticate another client's browser.
68
+
69
+ For standalone MCP, set `PI_BROWSER_USE_DATA_DIR` explicitly. Without it the server
70
+ uses `PLUGIN_DATA`, then `~/.local/share/pi-browser-use` (never Pi's data directory).
71
+ `PI_BROWSER_USE_CONFIG` optionally selects a different **absolute** configuration
72
+ file; an explicitly selected missing file is an error.
73
+
74
+ The existing launch, category, URL restriction, network-redaction, and bridge options
75
+ from `BrowserUseConfig` are supported. `userDataDir` and `executablePath` must be
76
+ absolute paths (a leading `~/` is supported). An explicit `userDataDir` can share a
77
+ **dedicated automation identity** across clients; coordinate profile ownership and
78
+ never point it at the user's daily Chrome directory. The chosen identity is retained
79
+ when switching through fresh or existing mode and back to persistent. `wsHeaders`
80
+ belongs to a host-managed secret-bearing configuration file, not the plugin bundle.
81
+ The core does not disable Chrome's sandbox.
82
+
83
+ ## Behavior and host-specific capabilities
84
+
85
+ All curated upstream `browser_*` tools and the management tools (`save_artifact`,
86
+ `doctor`, `switch_mode`, `setup`, `status`, `reauth`, `open_background_tab`) share the
87
+ same runtime. The MCP adapter is not a raw chrome-devtools-mcp configuration: tool
88
+ filtering, network-header redaction, overlay recovery, annotated artifacts, ownership
89
+ checks, per-origin visibility preferences, and background-focus defaults remain in
90
+ place. MCP inputs are validated against each advertised tool's JSON schema.
91
+
92
+ Calls are serialized within a runtime so a mode switch cannot race an active page
93
+ operation. Cancellation propagates to upstream MCP, startup and human setup; stdin
94
+ EOF, MCP disconnect and SIGINT/SIGTERM close the session. Shutdown terminates only a
95
+ browser owned by that runtime, never a borrowed peer/user browser. Abrupt SIGKILL
96
+ still relies on the existing orphan-recovery mechanism. Profile locks cannot be
97
+ reclaimed from a live owner merely because their timestamps are old.
98
+
99
+ Existing mode remains explicit and human-authorized. Background grouped tabs require
100
+ the bundled Chrome extension in `extension/`, just as in native Pi. The loopback tab
101
+ bridge is not a hosted service. Multiple simultaneous Existing-mode runtimes need
102
+ coordinated bridge ports/extension configuration; this migration does not implement
103
+ a shared bridge broker.
104
+
105
+ **Vision:** Native Pi retains optional `visionModel` / `browser_analyze_screenshot`
106
+ through its own model registry. Portable hosts receive standard MCP image content
107
+ from `browser_take_screenshot` and use their own vision capability; the portable
108
+ adapter deliberately does not expose the Pi-registry analysis tool or make implicit
109
+ model/sampling calls. `visionModel` is rejected in portable configuration.
110
+
111
+ **Authentication:** `browser_setup` opens an ordinary headed window on the same
112
+ managed profile, with no automation attached. A human completes login and closes it.
113
+ Use `browser_reauth` for later verification, including `variant: plain` for a provider
114
+ that rejects instrumented sign-in. Human setup can exceed a client's default tool
115
+ request timeout; increase that timeout in the host before starting. Cancellation or
116
+ a nonzero browser exit does not mark setup/verification successful. Never copy daily
117
+ Chrome profiles, cookies, passwords or Pi credentials into a plugin installation.
118
+
119
+ ## Validation and release
120
+
121
+ `npm test` exercises the shared runtime, native Pi adapter, MCP schema/routing/error
122
+ boundary, cancellation and auth lifecycle. `npm run test:smoke` packs the npm artifact,
123
+ installs its production dependency closure in a temporary directory **without Pi**,
124
+ and exercises a real stdio process, all tool schemas, status, EOF and SIGTERM.
125
+ `npm run test:browser` adds a real Chrome run against a loopback-only fixture, covering
126
+ navigation, screenshots, artifacts, profile reuse and fresh-mode isolation.
127
+
128
+ Release Please synchronizes `plugin.json.version` with the npm version. The package
129
+ includes both manifests and all skills, while excluding source maps, tests and CI
130
+ helpers. This is a packaging/API compatibility claim, not a claim that every named
131
+ agent client or real-world authentication provider has been tested.
@@ -5,7 +5,7 @@ This project measures performance in two layers:
5
5
  - `npm run perf:audit` measures cold plugin import time and memory, the published package, and the production dependency closure. It is fast, emits JSON, and does not launch Chrome.
6
6
  - `npm run bench -- --iterations 10 --json` launches the complete headless Chrome + MCP stack and measures startup, process-tree RSS, and representative tool calls against a fixed local fixture.
7
7
 
8
- `npm test` runs `perf:check` after the unit suite, so the stable artifact, dependency, and normalized cold-import budgets run in CI. The audit reports raw time but compares it with a same-run `typebox` import to account for runner CPU variance. Browser timing and process-tree memory are reported rather than enforced because shared-runner and Chrome variance would make a wall-clock budget flaky.
8
+ Code Foundry runs `npm test` and `npm run perf:check` in separate unit and performance jobs, so the stable artifact, dependency, and normalized cold-import budgets remain required in CI without coupling them to the unit command. The audit reports raw time but compares it with a same-run `typebox` import to account for runner CPU variance. Browser timing and process-tree memory are reported rather than enforced because shared-runner and Chrome variance would make a wall-clock budget flaky.
9
9
 
10
10
  ## M0 baseline and result
11
11
 
@@ -33,7 +33,7 @@ Budgets live in [`performance-budgets.json`](../performance-budgets.json). They
33
33
 
34
34
  | Metric | Budget |
35
35
  | --------------------------------- | ------------: |
36
- | Cold import / `typebox` p50 ratio | 2.0 |
36
+ | Cold import / `typebox` p50 ratio | 3.0 |
37
37
  | Cold import maximum RSS delta | 48 MiB |
38
38
  | npm tarball | 90,000 bytes |
39
39
  | npm unpacked package | 300,000 bytes |
@@ -50,6 +50,7 @@ npm run format:check
50
50
  npm run lint
51
51
  npm run typecheck
52
52
  npm test
53
+ npm run perf:check
53
54
  npm run perf:audit
54
55
  npm run bench -- --iterations 10 --json
55
56
  npm run bench -- --startup-only --json
package/mcp.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json",
3
+ "mcpServers": {
4
+ "browser": {
5
+ "type": "stdio",
6
+ "command": "node",
7
+ "args": ["${PLUGIN_ROOT}/dist/mcp-server.js"],
8
+ "cwd": "${PLUGIN_ROOT}",
9
+ "env": {
10
+ "PI_BROWSER_USE_DATA_DIR": "${PLUGIN_DATA}"
11
+ }
12
+ }
13
+ }
14
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-browser-use",
3
- "version": "0.9.5",
4
- "description": "Opinionated browser-use for the Pi coding agent, powered by chrome-devtools-mcp (not Playwright). Fresh headless by default, authenticated persistent profile opt-in, CLI-first policy bundled.",
3
+ "version": "0.10.0",
4
+ "description": "Opinionated browser automation via chrome-devtools-mcp: native Pi extension and portable Agent Plugins 1.0 skills + MCP server.",
5
5
  "keywords": [
6
6
  "automation",
7
7
  "browser",
@@ -27,7 +27,10 @@
27
27
  "docs/performance.md",
28
28
  "extension",
29
29
  "skills",
30
- "README.md"
30
+ "README.md",
31
+ "plugin.json",
32
+ "mcp.json",
33
+ "docs/agent-plugins.md"
31
34
  ],
32
35
  "type": "module",
33
36
  "sideEffects": false,
@@ -51,10 +54,14 @@
51
54
  "typecheck": "tsc -p tsconfig.json --noEmit",
52
55
  "prepublishOnly": "npm run build",
53
56
  "bench": "node scripts/bench.mjs",
54
- "test": "node --test test/*.test.mjs && npm run perf:check",
57
+ "test": "node --test test/*.test.mjs",
58
+ "preperf:audit": "npm run build",
55
59
  "perf:audit": "node scripts/performance.mjs",
60
+ "preperf:check": "npm run build",
56
61
  "perf:check": "node scripts/performance.mjs --check",
57
- "pretest": "npm run build"
62
+ "pretest": "npm run build",
63
+ "test:smoke": "npm run build && node scripts/plugin-smoke.mjs",
64
+ "test:browser": "npm run build && node scripts/plugin-smoke.mjs --browser"
58
65
  },
59
66
  "dependencies": {
60
67
  "@modelcontextprotocol/sdk": "^1.29.0",
package/plugin.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
+ "name": "pi-browser-use",
4
+ "version": "0.10.0",
5
+ "description": "Managed persistent, fresh, and existing Chrome sessions with focus-safe browser tools and CLI-first skills.",
6
+ "author": {
7
+ "name": "0xPlayerOne"
8
+ },
9
+ "repository": "https://github.com/0xPlayerOne/pi-browser-use",
10
+ "license": "MIT",
11
+ "keywords": ["browser", "chrome", "mcp", "agent-skills", "pi"]
12
+ }
@@ -1,58 +1,61 @@
1
1
  ---
2
2
  name: auth-bootstrap
3
- description: "Log the persistent browser profile in for the first time. Use when the persistent profile hits a login wall, after switching to a fresh machine, or when SSO, 2FA, or passkeys need a human."
3
+ description: "Initialize or reauthenticate the managed persistent browser profile. Use on a new machine or plugin instance, at a login wall, or when SSO, 2FA, passkeys or provider verification require a human. Works with native Pi and portable MCP hosts."
4
4
  ---
5
5
 
6
6
  # Auth Bootstrap
7
7
 
8
- The persistent profile starts empty. Log in once per site; cookies persist across sessions after that. The agent must never see your passwords — you type, it waits.
8
+ Each managed profile starts empty. Native Pi and portable clients have separate
9
+ profiles by default. Call `browser_status` to identify this instance before doing
10
+ anything: a signed-in daily Chrome or another agent client proves nothing about it.
11
+ Never read, capture or paste passwords, one-time codes or session cookies into chat.
9
12
 
10
- ## Recommended flow: headed once
13
+ ## First run: plain headed setup
11
14
 
12
- ```text
13
- browser_switch_mode({ "mode": "persistent", "headed": true })
14
- ```
15
-
16
- 1. A visible Chrome window opens on the persistent profile.
17
- 2. **You** navigate and log in normally — including SSO, 2FA, and passkeys.
18
- 3. Tell the agent you're done. It verifies (account page loads logged-in) and you close the window or it switches back to fresh.
15
+ Select persistent mode when needed, then call `browser_setup`. Explain **before**
16
+ calling it that an ordinary headed window opens on the managed profile, the human
17
+ signs into the needed sites, and they close that window when finished. No browser
18
+ automation is attached to this setup window. Do not automate its credential fields.
19
19
 
20
- Headless cannot do this step: 2FA apps, security keys, and most SSO device checks need a human and often a visible window.
20
+ Setup waits for the window to close. Use a host tool timeout sufficient for a human
21
+ login; cancellation or browser failure is not success. Successful setup initializes
22
+ the profile, but does not prove that every target site is authenticated.
21
23
 
22
- ## Power option: clone your daily profile
24
+ ## Expired login or rejected instrumented sign-in
23
25
 
24
- Close **all** Chrome windows first (running Chrome locks the profile), then:
26
+ In persistent mode, request the human handoff:
25
27
 
26
- ```bash
27
- cp -R ~/Library/Application\ Support/Google/Chrome/Default ~/.pi/browser-profile
28
+ ```text
29
+ browser_reauth({ "url": "https://example.com/" })
28
30
  ```
29
31
 
30
- Same macOS user, so Keychain-bound cookies and passwords decrypt fine. Prefer the headed-once flow unless you have dozens of logins — clones carry sync state, extensions, and version skew that cause strange breakage. Never copy while Chrome runs; you'll corrupt both ends.
31
-
32
- ## Google SSO says "browser or app may not be secure"
32
+ When the provider rejects an instrumented browser, use the plain variant on the
33
+ **same managed identity**, not the user's daily profile:
33
34
 
34
- Expected: Google rejects sign-in from automation-driven Chrome
35
- (`--enable-automation`, debugging pipe, fresh profile with no history).
36
- Don't fight it — use one of these instead:
37
-
38
- 1. **Email + password** on the login form (no Google involved).
39
- 2. **Your daily browser**, which Google already trusts (real history, no
40
- automation flags). Point the session at it temporarily with
41
- `mode: existing` and drive the flow there, then switch back.
42
- 3. Complete SSO there once; the persistent profile keeps the resulting
43
- Cloudflare session cookies either way.
35
+ ```text
36
+ browser_reauth({ "url": "https://example.com/", "variant": "plain" })
37
+ ```
44
38
 
45
- ## What not to do
39
+ Only the human completes SSO, 2FA, CAPTCHA, passkeys and device checks. Stop at the
40
+ challenge; never loop attempts. A live peer-owned backend cannot be restarted or
41
+ reauthenticated by this session: coordinate with its owner instead.
46
42
 
47
- - **Paste passwords or TOTP codes into chat.** The agent never needs them — type directly in the headed window.
48
- - **Export/import individual cookies** for HttpOnly session cookies. The tooling (keychain decryption, cookie-store surgery) is fragile; a two-minute headed login beats an hour of debugging.
49
- - **Reuse the persistent profile for hostile links.** Unknown URLs go through `fresh` — no credentials present, nothing to steal.
43
+ ## Resume and verify
50
44
 
51
- ## Verify
45
+ After human verification, return persistent automation to headless:
52
46
 
53
47
  ```text
54
- browser_navigate_page({ "pageId": <id>, "url": "https://github.com/settings/profile" })
55
- browser_take_snapshot({ "pageId": <id> })
48
+ browser_switch_mode({ "mode": "persistent" })
49
+ browser_list_pages({})
56
50
  ```
57
51
 
58
- Your username on the page means the profile is live. If a site challenges headless later (bot checks sometimes do), redo that site with `headed: true` — same profile, same cookies.
52
+ Navigate an explicitly identified page to a benign, task-relevant account page and
53
+ verify its authenticated DOM. For Gmail, load `gmail-auth`; generic Chrome sign-in
54
+ is not proof of Gmail authentication. If only headless execution fails on this
55
+ profile, use persistent headed-background with `rememberSite: true` for that origin,
56
+ not a global downgrade.
57
+
58
+ Never clone daily Chrome profiles, export/import cookies, or point `userDataDir` at
59
+ the user's daily browser directory. Existing mode is a separate identity and requires
60
+ the user's explicit choice. Its cookies do not migrate back to persistent mode.
61
+ Use fresh mode only for anonymous checks and clean-room reproductions.
@@ -1,10 +1,14 @@
1
1
  ---
2
2
  name: browser-policy
3
- description: "Browser-use policy for Pi agents. Use before any browser_* tool call. Prefer CLIs and APIs over browser automation, default to fresh headless sessions, escalate to the authenticated profile only on login walls, and never steal user focus."
3
+ description: "Browser-use policy for agents. Use before any browser_* tool call. Prefer CLIs and APIs over browser automation, default to a dedicated persistent headless profile, use fresh mode for anonymous checks, and never steal user focus."
4
4
  ---
5
5
 
6
6
  # Browser Policy
7
7
 
8
+ These policies apply to native Pi and portable Agent Plugins/MCP hosts. Call
9
+ `browser_status` for this instance's profile and artifact paths; never infer
10
+ them from another client. Authentication does not transfer between profiles.
11
+
8
12
  `browser_*` tools (this `pi-browser-use` package, powered by `chrome-devtools-mcp` — not Playwright) drive a real Chrome. They are the tool of last resort, not the first.
9
13
 
10
14
  ## Decision order
@@ -15,14 +19,14 @@ description: "Browser-use policy for Pi agents. Use before any browser_* tool ca
15
19
 
16
20
  ## Session modes
17
21
 
18
- - **Default: persistent headless** (`mode: persistent`). Pi's own browser on its dedicated profile (`~/.pi/browser-profile`): self-launched Chrome, no window, never steals focus, no consent popups. Log in once via `browser_setup`; cookies persist. Use for everything unless there's a reason not to. Pass `headed: true` to watch, and warn the user before any headed launch.
22
+ - **Default: persistent headless** (`mode: persistent`). The plugin's own browser on its dedicated profile (the profile reported by `browser_status`): self-launched Chrome, no window, never steals focus, no consent popups. Log in once via `browser_setup`; cookies persist. Use for everything unless there's a reason not to. Pass `headed: true` to watch, and warn the user before any headed launch.
19
23
  - **Clean room** (`mode: fresh`). Ephemeral profile, thrown away each session. Use for anonymous checks, hostile links, and "does it render logged-out?" verifications — never for anything needing identity.
20
- - **Existing Chrome** (`mode: existing`) attaches to the user's running Chrome — intrusive (drives the daily browser, sees all tabs). Avoid unless the user explicitly asks. First attach shows Chrome's "Allow remote debugging?" consent popup (once per session — click Allow). Pi tabs must be opened with `browser_open_background_tab` (extension-brokered into the collapsed `pi-browser-use` group), never raw `browser_new_page`. Closing the last Pi tab dissolves the group automatically; `browser_close_page` refuses tabs Pi didn't open unless `force: true` was explicitly requested.
24
+ - **Existing Chrome** (`mode: existing`) attaches to the user's running Chrome — intrusive (drives the daily browser, sees all tabs). Avoid unless the user explicitly asks. First attach shows Chrome's "Allow remote debugging?" consent popup (once per session — click Allow). Agent tabs must be opened with `browser_open_background_tab` (extension-brokered into the collapsed `pi-browser-use` group), never raw `browser_new_page`. Closing the last agent tab dissolves the group automatically; `browser_close_page` refuses tabs this session didn't open unless `force: true` was explicitly requested.
21
25
  - **Switch, don't restart**: `browser_switch_mode` moves between persistent, fresh, and existing mid-session. Start persistent; drop to fresh for clean-room checks; touch existing only when the user explicitly asks.
22
26
  - **Hard blocks escalate themselves**: login walls in fresh sessions suggest the switch call; login walls and bot challenges in authenticated sessions rebuild headed and prompt the human. Once per call, never looping, never in attached sessions — and a headed popup from a block is the one case where stealing focus is the job, not a bug.
23
- - **Visual analysis** (`browser_analyze_screenshot`, only when `visionModel` is configured) is for canvas/WebGL scenes and coordinate clicks the tree cannot describe — not a substitute for reading the snapshot first.
27
+ - **Visual analysis** (`browser_analyze_screenshot`, native Pi only when `visionModel` is configured; other hosts analyze the MCP image from `browser_take_screenshot`) is for canvas/WebGL scenes and coordinate clicks the tree cannot describe — not a substitute for reading the snapshot first.
24
28
 
25
- `--chrome-arg` flags only apply when `chrome-devtools-mcp` launches Chrome itself — never with `autoConnect`/`browserUrl`. On macOS `--start-minimized` is ignored; only `headless: true` truly hides the window.
29
+ Chrome flags apply only to a managed launch (direct or upstream), never to an already-running browser attached with `autoConnect`/`browserUrl`. On macOS `--start-minimized` is ignored; only `headless: true` truly hides the window.
26
30
 
27
31
  ## Bot walls and logins
28
32
 
@@ -45,7 +49,7 @@ Turnstile, device checks, SSO/2FA cannot be automated away. On hitting one: stop
45
49
 
46
50
  ## Parallel agents (shared browser)
47
51
 
48
- Many agents share one Pi-owned Chrome. Separation is by tabs, not windows:
52
+ Many agents share one plugin-owned Chrome. Separation is by tabs, not windows:
49
53
 
50
54
  - Always pass an explicit `pageId` (from your own `browser_list_pages`) to every page-scoped call. Never assume the selected page is yours.
51
55
  - Open your own tabs (`browser_new_page` background, or `browser_open_background_tab` in existing mode). They are claimed to your session automatically.
@@ -55,16 +59,16 @@ Many agents share one Pi-owned Chrome. Separation is by tabs, not windows:
55
59
 
56
60
  ## Browser mode rules
57
61
 
58
- 1. Prefer Persistent (the default) for everything: Pi's browser, invisible, no popups.
62
+ 1. Prefer Persistent (the default) for everything: the plugin's browser, invisible, no popups.
59
63
  2. Use Fresh only for anonymous/stateless browsing: hostile links, logged-out checks, clean-room reproductions.
60
- 3. Persistent uses Pi's dedicated browser profile — never the user's daily Chrome data.
61
- 4. If Persistent has never been initialized, launch the Pi Browser setup flow (headed once, human signs in, close the window).
64
+ 3. Persistent uses the plugin's dedicated browser profile — never the user's daily Chrome data.
65
+ 4. If Persistent has never been initialized, launch the managed browser setup flow (headed once, human signs in, close the window).
62
66
  5. Never attempt to automate credentials, CAPTCHA, 2FA, passkeys, or security challenges that require the user.
63
67
  6. When authentication is required, request the headed authentication flow.
64
68
  7. After authentication, prefer restarting Persistent headless.
65
69
  8. If a site fails specifically because it is headless, retry using Persistent headed-background (per-origin; never downgrade every site).
66
- 9. In headed-background mode, never request foreground focus unless the user explicitly asked to watch or Pi is handing over auth.
70
+ 9. In headed-background mode, never request foreground focus unless the user explicitly asked to watch or the agent is handing over auth.
67
71
  10. Use Existing only when the user explicitly chose it or Persistent cannot provide the required existing browser/session state.
68
- 11. In Existing mode, all new Pi tabs must be created through the Pi extension and placed in the collapsed `pi-browser-use` group.
69
- 12. Never activate Pi-created Existing-mode tabs by default.
70
- 13. Never close or modify unrelated user tabs; on session end close only Pi-owned tabs.
72
+ 11. In Existing mode, all new agent tabs must be created through the bundled Chrome extension and placed in the collapsed `pi-browser-use` group.
73
+ 12. Never activate agent-created Existing-mode tabs by default.
74
+ 13. Never close or modify unrelated user tabs; close only this session's explicitly identified tabs; do not assume session shutdown closes tabs in a borrowed browser.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: gmail-auth
3
- description: "Verify Gmail authentication in the persistent Pi browser profile. Use when a task needs the Gmail inbox, after browser_setup, or when Google shows a login or verification challenge."
3
+ description: "Verify Gmail authentication in the persistent managed browser profile. Use when a task needs the Gmail inbox, after browser_setup, or when Google shows a login or verification challenge."
4
4
  ---
5
5
 
6
6
  # Gmail Auth
@@ -10,11 +10,14 @@ Authentication state lives in this skill, not in a generic browser heuristic: on
10
10
  ## Verify first
11
11
 
12
12
  ```text
13
- browser_open_background_tab({ "url": "https://mail.google.com/" })
13
+ browser_new_page({ "url": "https://mail.google.com/", "background": true })
14
14
  browser_take_snapshot({ "pageId": <id> })
15
15
  ```
16
16
 
17
- Or in persistent mode, navigate directly — the persistent profile is Pi's browser, no tab group needed.
17
+ Use the page ID returned by `browser_list_pages`. In explicitly selected Existing
18
+ mode, use `browser_open_background_tab` instead so the bundled Chrome extension
19
+ creates a grouped inactive tab. Check `browser_status` for the active profile; a
20
+ login in another client or daily Chrome does not authenticate this instance.
18
21
 
19
22
  ## Authenticated
20
23