Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Background Chrome without focus stealing

- Added persistent dynamic MDB pool provisioning: eight tabs remain the default, operators can request a growth-only target up to 32 with `chrome_workspace_setup`, and capacity pressure auto-targets four more tabs without repeatedly ratcheting while expansion is pending. Provisioning is recorded even when Chrome is in the background; actual tab creation still occurs only when the existing MDB window is naturally focused. Workspace status and exhaustion errors now expose current/target/max/pending capacity and safe lease-pressure details.
- Made `chrome_fill` resolve non-unique selectors to the first visible, enabled, fillable match instead of blindly using a hidden template. Snapshot selectors now return an id or attribute selector only when it is unique, and form submission passes the visible submit control to `requestSubmit` while reporting the selected match and submit strategy for native readback.
- Fixed `chrome_fill` hanging until its 45-second transport timeout in inactive MDB tabs. Framework-settle verification still uses two animation frames when Chrome schedules them, but now falls back to a bounded timer because background tabs may suspend `requestAnimationFrame` indefinitely.
- Added the experimental `chatgpt_conversation_start` MCP tool and static-bearer, direct-loopback `/experimental/chatgpt/conversation` wrapper. The default `runtime` transport leases an inactive signed-in ChatGPT tab and invokes the first-party mounted `submitComposer` store action as a `text_action`, without typing/clicking the UI or extracting credentials/proof material. After the bounded stream/rendered handoff, MDB reloads the exact returned conversation and reads the exact persisted assistant message before returning text; ambiguous handoff fails without a second submission. The former direct private request remains available only as explicit `transport: "raw"` diagnostics. Prompt plaintext is redacted from every audit mode.
Expand All @@ -28,9 +29,9 @@
- Changed the product default to **relaxed approvals**: the signed-in MDB Chrome workspace can use normal HTTP/HTTPS sites and native foreground app control can execute without per-site/per-app terminal approval commands. Added a live **Strict approvals** checkbox to the menu-bar app; when enabled it restores the scoped background-Chrome grant pool and one-use foreground-app approvals.
- Added an optional Manifest V3 Chrome extension plus native-messaging host so ChatGPT can operate approved pages in the operator's real signed-in Chrome profile without routine focus theft. New built-in tools are `chrome_workspace_status`, `chrome_workspace_setup`, `chrome_tabs`, `chrome_open`, `chrome_navigate`, `chrome_snapshot`, `chrome_click`, `chrome_fill`, and `chrome_close`.
- Bound the native host to the selected signed-in Chrome profile/account and fail closed on signed-out or mismatched profiles.
- Added a Chrome-native **`MDB`** tab group with a reusable background-tab pool (eight tabs by default and maximum), per-tab leases, idle collapse, restart reconciliation, and group rediscovery. Routine `chrome_open` leases a pre-created tab rather than creating one; `chrome_close` returns it to the pool.
- Added a Chrome-native **`MDB`** tab group with a reusable background-tab pool (eight tabs by default, later extended to a persistent growth-only target up to 32), per-tab leases, idle collapse, restart reconciliation, and group rediscovery. Routine `chrome_open` leases a pre-created tab rather than creating one; `chrome_close` returns it to the pool.
- Made workspace status/setup local and grantless. Reworked authenticated `chrome-background` approval into a shared additive grant pool: concurrent ChatGPT sessions can approve different domains without replacing one another, URL scopes are unioned while each grant remains unexpired, and active grants survive bridge-child restarts until their original expiry. Legacy fixed-file approvals are imported for backward compatibility. Federated personal-browser providers keep their single-use semantics.
- Refuse workspace creation/expansion unless Chrome is already focused. This is deliberate: measured on Chrome 151/macOS, even `tabs.create({active:false})` can bring Chrome to the foreground. Routine work therefore performs no tab creation after the one-time setup.
- Keep actual workspace tab creation/expansion gated on Chrome already being naturally focused. Provisioning the desired capacity may happen in the background, but measured on Chrome 151/macOS, even `tabs.create({active:false})` can bring Chrome to the foreground, so pending tabs wait for the next natural focus rather than activating Chrome.
- Added background-first desktop GUI detection for native apps such as Slack. Relaxed mode still permits non-Chrome foreground UI when genuinely required; Strict mode requires a single-use, app-scoped, maximum-five-minute grant. The old model-controlled env bypass is gone, while Chrome is always forced through the separate `MDB` background routing rule above.
- Added a friendly cat/headphones/laptop extension icon and Chrome action icon at 16/32/48/128px.
- Deliberately did **not** expose arbitrary page JavaScript, network-header capture, or file uploads. Snapshot password values are redacted, and browser/OS security UI that requires trusted foreground interaction is reported rather than silently activated.
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ Git, package managers, Vercel CLI, database CLIs, AppleScript, browser CLIs, bui
| `chrome_workspace_status` | Inspect the extension-owned `MDB` Chrome group, lease activity, and reusable background-tab pool; no website grant required |
| `chatgpt_extension_status` | Inspect the installed ChatGPT Chrome extension, OpenAI native-host registration, and live read-only page-bridge status without patching the OpenAI extension |
| `chatgpt_conversation_start` | Experimentally start or continue one exact ChatGPT conversation through the signed-in page's first-party runtime action; no UI typing/clicking or credential export |
| `chrome_workspace_setup` | Create or expand the `MDB` pool while Chrome is already foreground; default target is eight reusable tabs |
| `chrome_workspace_setup` | Provision a growth-only `MDB` pool target from 1 to 32 tabs; default is eight, with creation deferred until Chrome is naturally focused |
| `chrome_tabs` | List tabs in the real signed-in Chrome profile without activating Chrome; scoped only when Strict approvals is on |
| `chrome_open` | Lease an idle tab from the persistent `MDB` group and open a URL without creating a new tab |
| `chrome_navigate` | Navigate an approved tab without selecting it |
Expand Down Expand Up @@ -129,11 +129,11 @@ This is intentionally opt-in because authenticated browser control is powerful.

Then in Chrome open `chrome://extensions`, enable **Developer mode**, choose **Load unpacked**, and select this repository's `chrome-extension/` directory. The expected extension id is `pcebfblnmcappinbenkmddjdapaoajgm`.

The extension keeps a Chrome-native tab group named **`MDB`**. By default it targets eight extension-owned idle tabs. They are created only while Chrome is already foreground, then leased and reused for routine work. The group is collapsed when idle and expands while one or more tabs are leased. This mirrors the managed-group approach used by browser-agent extensions while avoiding a macOS/Chrome quirk measured on this project: even `chrome.tabs.create({ active:false })` can bring Chrome to the foreground.
The extension keeps a Chrome-native tab group named **`MDB`**. By default it targets eight extension-owned idle tabs, with a hard maximum of 32. They are created only while the existing MDB Chrome window is already naturally foreground, then leased and reused for routine work. The group is collapsed when idle and expands while one or more tabs are leased. This preserves the no-focus-steal boundary around a macOS/Chrome quirk measured on this project: even `chrome.tabs.create({ active:false })` can bring Chrome to the foreground.

The pool now self-heals and self-expands. If Chrome or the extension restarts, or an older four-tab pool is still present, the extension grows the managed pool to the default eight tabs the next time you **naturally focus Chrome**. It never activates Chrome just to repair or expand itself. You can also force setup while Chrome is already foreground by calling `chrome_workspace_setup` (default pool size: 8).
The pool self-heals and has a persistent growth-only capacity target. `chrome_workspace_setup(pool_size=16)` records a 16-tab target immediately. If the MDB Chrome window is already focused, the missing tabs are created and grouped at once; otherwise status reports the pending count and the extension expands on the next **natural Chrome focus**. A lower later request never closes existing tabs. When every current tab is leased and no expansion is already pending, pressure raises the target by four, up to 32, and attempts immediate creation only when Chrome is already focused. MDB never activates Chrome to satisfy either manual or automatic provisioning.

`chrome_workspace_status` is grantless because it only reads extension-owned local workspace state. It now includes lease age/idle metadata, the 10-minute idle-reclaim timeout, and the 20-second lease-wait budget. `chrome_workspace_setup` is also grantless because it creates only extension-owned idle pages; it refuses to create or expand the pool unless Chrome is already focused rather than stealing focus itself. Legacy/internal `tabs.open` callers are routed to the same `workspace.open` lease path, so they cannot create loose tabs outside `MDB`. When all tabs are busy, `chrome_open` waits briefly for a release instead of failing immediately; abandoned leases are reclaimed after 10 minutes without browser activity, while every navigate/snapshot/click/fill renews an active lease.
`chrome_workspace_status` is grantless because it only reads extension-owned local workspace state. It reports current and target pool sizes, the 32-tab maximum, four-tab automatic growth step, pending capacity, lease age/idle metadata, the 10-minute idle-reclaim timeout, and the 20-second lease-wait budget. `chrome_workspace_setup` is also grantless: provisioning is always accepted locally, while actual creation remains deferred when Chrome is not focused. Legacy/internal `tabs.open` callers are routed to the same `workspace.open` lease path, so they cannot create loose tabs outside `MDB`. When all tabs are busy, `chrome_open` first provisions or creates capacity where safe, then waits briefly for a release; abandoned leases are reclaimed after 10 minutes without browser activity, while every navigate/snapshot/click/fill renews an active lease.

**Relaxed access is the default.** Normal HTTP/HTTPS work, including localhost and non-default ports, through the signed-in `MDB` Chrome profile does not require a terminal approval command or per-site allowlist. This is intentional: Mac Developer Bridge already exposes unrestricted shell/file authority as the logged-in macOS user, and the useful default is for browser execution to match that operator-chosen trust level while remaining background-first.

Expand Down
8 changes: 4 additions & 4 deletions bridge.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -763,16 +763,16 @@ const TOOLS = [
{
name: "chrome_workspace_setup",
title: "Set up MDB Chrome workspace",
description: "Create or expand the extension-owned MDB Chrome tab group and its reusable background-tab pool. This is a one-time/local setup action and does not access authenticated websites. On macOS, setup refuses to create tabs unless a normal Chrome window is already focused, because Chrome may otherwise steal focus even for active:false tab creation.",
description: "Provision the desired capacity for the extension-owned MDB Chrome tab group and reusable background-tab pool. The target is persisted even when Chrome is not focused. Missing tabs are created immediately only while the existing MDB Chrome window is already naturally focused; otherwise expansion remains pending until the next natural focus, so MDB never steals focus.",
inputSchema: {
type: "object",
properties: {
pool_size: {
type: "integer",
minimum: 1,
maximum: 8,
maximum: 32,
default: 8,
description: "Number of reusable extension-owned tabs to keep in the MDB group. Eight is the default so concurrent automations do not exhaust a four-tab pool.",
description: "Growth-only desired capacity for the reusable extension-owned MDB pool. Eight is the default and 32 is the hard maximum. Lower requests never close existing tabs.",
},
},
additionalProperties: false,
Expand Down Expand Up @@ -1546,7 +1546,7 @@ const BACKGROUND_CHROME_GRANT_DIR = process.env.MAC_DEV_BRIDGE_BACKGROUND_CHROME
const BACKGROUND_CHROME_MAX_TTL_MS = 15 * 60 * 1000;
const BACKGROUND_CHROME_MAX_GRANT_FILES = 256;
const BACKGROUND_CHROME_DEFAULT_POOL_SIZE = 8;
const BACKGROUND_CHROME_MAX_POOL_SIZE = 8;
const BACKGROUND_CHROME_MAX_POOL_SIZE = 32;
const CHATGPT_CHROME_EXTENSION_ID = "hehggadaopoacecdllhhajmbjkdcmajg";
const CHATGPT_NATIVE_HOST_NAME = "com.openai.codexextension";

Expand Down
2 changes: 1 addition & 1 deletion chrome-extension/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "Mac Developer Bridge Background Browser",
"version": "0.2.9",
"version": "0.2.10",
"description": "Lets Mac Developer Bridge operate approved Chrome tabs without bringing Chrome to the foreground.",
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq9QjCNUI8xWx8wNSCITv4TFRy2pYhd77cDUTmYFJqduaYwhplrkvbo1BB9wTn7VBKLe8ohNp4fnZz9T03il69oRPsYGRb5aHAdSVaYdCrOiGs7GN00wvwUf6d2lQeuslDd53SpTnXZdtGRMVRBeK4X4NyEksQHzxKpaeaRlT0R9qREp+TdiLfEh5Z6UImZ0ZeBVn7efazrNBYMARBjas3A/AbXUyH654TVMOAqePUpgj129g8ZGQjcPqOFKGqhAyyCVhpbJBU+1Kt7sX6wbIIREtjmryW7sqMba7TnubqCv2qTPLZNeHQVQaQpl75cYdL26lh8PXwSTHeQKX+3KwHwIDAQAB",
"permissions": [
Expand Down
Loading