diff --git a/docs/tools/browser.md b/docs/tools/browser.md index a5c0e1a59..2189c2e75 100644 --- a/docs/tools/browser.md +++ b/docs/tools/browser.md @@ -55,7 +55,7 @@ | `viewport` | `{ width: number; height: number; scale?: number }` | No | Requested viewport. For headless launch this becomes the initial viewport; for a page it is applied with `page.setViewport()`. `scale` maps to Puppeteer `deviceScaleFactor`. | | `wait_until` | `"load" \| "domcontentloaded" \| "networkidle0" \| "networkidle2"` | No | Navigation wait condition. Defaults to `"load"` where omitted, including `open` navigation and later `tab.goto(...)`. | | `dialogs` | `"accept" \| "dismiss"` | No | Installs a page `dialog` handler that auto-accepts or auto-dismisses dialogs. Omitted means no handler. | -| `app` | `{ path?: string; cdp_url?: string; args?: string[]; target?: string }` | No | Selects browser kind. With no `app`, the cmux backend is used when a cmux socket is available (`CMUX_SOCKET_PATH`, gated by the `browser.cmux` setting / `PI_BROWSER_CMUX` override); otherwise the session `browser.headless` setting applies. `app.path` is resolved against the session cwd and used as the executable path for spawn/attach reuse. `app.cdp_url` connects to an existing CDP endpoint. `args` are appended only when spawning `app.path`. `target` is only used for attached/spawned-app page selection. | +| `app` | `{ path?: string; cdp_url?: string; args?: string[]; target?: string }` | No | Selects browser kind. With no `app`, a configured `browser.cdpUrl` setting attaches to that endpoint; otherwise the cmux backend is used when a cmux socket is available (`CMUX_SOCKET_PATH`, gated by the `browser.cmux` setting / `PI_BROWSER_CMUX` override); otherwise the session `browser.headless` setting applies. `app.path` is resolved against the session cwd and used as the executable path for spawn/attach reuse. `app.cdp_url` connects to an existing CDP endpoint. `args` are appended only when spawning `app.path`. `target` is only used for attached/spawned-app page selection. | ### `action: "close"` @@ -92,6 +92,7 @@ The tool returns one result per call; no streaming partial output is emitted fro 2. `open` resolves browser kind with `resolveBrowserKind()`: - `app.cdp_url` → `{ kind: "connected" }` after trimming trailing slashes. - `app.path` → `{ kind: "spawned" }` after resolving against session cwd. + - otherwise, a non-empty `browser.cdpUrl` setting → `{ kind: "connected" }` after trimming whitespace and trailing slashes. - otherwise, `resolveCmuxKind()` → `{ kind: "cmux", socketPath, password?, surface? }` when `CMUX_SOCKET_PATH` is set and cmux is enabled (`browser.cmux` setting, overridable by `PI_BROWSER_CMUX`). - otherwise → `{ kind: "headless", headless: session.settings.get("browser.headless") }`. 3. `open` rejects reusing the same tab name across different browser kinds (`sameBrowserKind()`); callers must close first. @@ -163,7 +164,7 @@ The tool returns one result per call; no streaming partial output is emitted fro - **Browser kind** - **Headless**: launches local Chromium with Puppeteer, applies stealth patches, and creates a fresh page per tab. - **Spawned app (`app.path`)**: reuses an existing CDP-enabled process for that executable when possible; otherwise kills same-path processes, spawns the executable with remote debugging enabled, then attaches. No stealth patches are injected. - - **Connected browser (`app.cdp_url`)**: attaches to an already-running CDP endpoint. No process ownership; close only disconnects. + - **Connected browser (`app.cdp_url`, or the `browser.cdpUrl` setting when the call carries no `app`)**: attaches to an already-running CDP endpoint. No process ownership; close only disconnects. - **Cmux surface (`browser.cmux`)**: with no `app` and a cmux socket available (`CMUX_SOCKET_PATH`, enabled by the `browser.cmux` setting / `PI_BROWSER_CMUX` override), drives a cmux WKWebView surface over a unix-socket JSON-RPC client instead of Puppeteer. No Bun worker and no stealth patches; `open` opens a split (owning that surface), `run` executes via `runCmuxCode()`, and `close` issues `surface.close` for surfaces it owns (leaving the workspace's last surface open). - **Target selection for attached/spawned browsers** - With `app.target`, `pickElectronTarget()` returns the first page whose URL or title contains the case-insensitive substring. diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 6859f52d6..bf45259b1 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -42,6 +42,7 @@ - Added `fastModeEnabled` and `fastModeActive` to RPC `get_state` responses. - Fixed RPC fast-mode state reporting after direct Anthropic rejects `speed: "fast"`, while allowing explicit re-enable requests to retry priority service. - Added opt-in subagent access to `checkpoint`, `rewind`, `learn`, and `manage_skill` when explicitly listed in an agent definition's `tools:` frontmatter. Listing one of `checkpoint`/`rewind` auto-includes the other. Settings (`checkpoint.enabled`, `autolearn.enabled`) remain master toggles. +- Added a `browser.cdpUrl` setting that points browser automation at an already-running CDP endpoint by default, so `app.cdp_url` no longer has to be repeated on every call. Explicit `app` options still take precedence. ## [17.1.8] - 2026-07-28 diff --git a/packages/coding-agent/src/config/settings-schema.ts b/packages/coding-agent/src/config/settings-schema.ts index cf0156dd8..da5dc164a 100644 --- a/packages/coding-agent/src/config/settings-schema.ts +++ b/packages/coding-agent/src/config/settings-schema.ts @@ -4054,6 +4054,18 @@ export const SETTINGS_SCHEMA = { }, }, + "browser.cdpUrl": { + type: "string", + default: undefined, + ui: { + tab: "tools", + group: "Grep & Browser", + label: "Browser CDP URL", + description: + "Default HTTP CDP discovery endpoint (for example http://127.0.0.1:9222) to attach to instead of launching a browser. Explicit app.cdp_url or app.path on the tool call take precedence.", + }, + }, + "browser.headless": { type: "boolean", default: true, diff --git a/packages/coding-agent/src/tools/browser.ts b/packages/coding-agent/src/tools/browser.ts index 92f358fde..eda99d7ef 100644 --- a/packages/coding-agent/src/tools/browser.ts +++ b/packages/coding-agent/src/tools/browser.ts @@ -95,6 +95,11 @@ function resolveBrowserKind(params: BrowserParams, session: ToolSession): Browse const exe = resolveToCwd(app.path, session.cwd); return { kind: "spawned", path: exe }; } + // A configured endpoint is a default, not an override: explicit app options win. + const configuredCdpUrl = (session.settings.get("browser.cdpUrl") as string | undefined)?.trim(); + if (configuredCdpUrl) { + return { kind: "connected", cdpUrl: configuredCdpUrl.replace(/\/+$/, "") }; + } const cmuxKind = resolveCmuxKind({ settingEnabled: session.settings.get("browser.cmux") as boolean | undefined, });