# v0.7.0 — One browser, many agents: tenancy, background tabs, bulk context

## TL;DR
Several agents and a person can now drive the same Firefox at once without fighting over it. Every agent gets an identity and owns the tabs it opens; every tool that acts on a page does so without pulling that page to the front; and every reply carries the console output, network traffic, page tree and screenshot belonging to the call that produced it. A workflow that used to be five calls is one.

## Why
The browser was always shared - that is the point of running it in a container with a VNC session attached - but sharing it was hostile. Tool calls landed on whichever tab happened to be focused, so two agents working in parallel would steal each other's page mid-action, and a person reading something at the VNC session would watch it vanish. On top of that, every tool answered exactly what it was asked, so finding out what a click had actually done took four more round trips.

## Highlights

### Tenancy: agents own their tabs
| | Before | After |
|---|---|---|
| Identity | None - all callers indistinguishable | Every reply carries `agent: agt_xxxxxx`; pass it back to stay yourself |
| Targeting | Whatever tab is focused | `tab:` id, defaulting to your own most recent tab |
| Visibility | Your tabs only | All tabs, marked by owner - like `ps aux`, you see the whole box |
| Enforcement | n/a | Soft: another agent's tab is reachable, and warns |

Tabs a person opens at the VNC session stay unowned, and an agent that drifts onto one is told so. Ownership is claimed by whoever opened the tab, at the moment it opens.

### Nothing steals the view
Every tab-scoped operation - navigate, evaluate, snapshot, click, hover, fill, drag, upload, screenshot, open, close - now runs against a named tab in the background. The on-screen tab does not move. `select_page` is the sole exception, because asking to look at something is the one case where moving the view is the request.

Each path falls back to the classic focus-then-act behaviour when the browser cannot do it in the background, so nothing refuses to work; it just stops being quiet about it.

### Bulk context: one call, the whole picture
Every page-affecting tool now bundles what happened while it ran:

```
result: "ok"
🧰 2 console (1 error) · 1 request (1 failed)
console:
  [error] boom: something failed
network:
  404 GET https://example.com/missing-endpoint
page tree (id=7, interactive elements, 12 of 240 lines):
  uid=7_3 button "Submit"
```

Console lines and requests are sliced to the window the tool ran in and filtered to the tab it acted on, so the reply describes *this* action rather than everything the browser has done since it started. The uids in the tree are live - they can go straight to `click_by_uid`.

Verbosity is one enum, defaulting to `inline`:

| `context` | What comes back |
|---|---|
| `off` | Nothing |
| `counts` | One summary line |
| `inline` *(default)* | That line, plus errors and warnings, failed and XHR requests, and the interactive parts of the page |
| `full` | Everything captured in the window, whole tree, attributes |

Per-channel booleans (`screenshot`, `dom`, `console`, `network`) override the level for one call. Screenshots default on only for tools that change the page.

### Snapshots survive being looked at
A tab used to hold exactly one snapshot, so anything that snapshotted it invalidated the uids an agent was holding - including the context bundle riding along on the agent's own calls. Tabs now keep the two previous generations, so a plan made from one snapshot survives being carried out. Navigation still expires them, because then the elements really are gone.

### Housekeeping
- **Asset janitor.** Recording frames, screenshots and network payloads under `~/.firefox-devtools-mcp/` are swept on a TTL (`MCP_ASSET_TTL_MIN`, default 60, `0` disables). Paths a caller named with `saveTo` are never touched.
- **Human takeover.** `docker/ff-takeover.sh` restarts the container with the automation flags off, so sites that refuse to sign in a remote-controlled browser can be signed into by hand. Flags back on afterwards; the profile and its sessions persist.
- **Container DNS.** The browser container resolves through the host's MagicDNS (`100.100.100.100`) with a public fallback, so `*.lan` and `*.ts.net` names work from inside it without a sidecar.
- **Dev stack.** `docker/docker-compose.dev.yaml` plus `docker/dev-reload.sh` build and swap the server in about four seconds against a separate container, leaving the production one alone.

## Design notes
- **Soft gating, never hard.** An agent can see and act on any tab, including a person's. Refusing would break the shared-browser premise; warning keeps it honest.
- **Identity is minted, not declared.** The first call without an `agent` gets one issued and echoed back. Nothing has to be configured, and an agent that ignores the echo simply gets a new identity each time.
- **The registry is in memory.** A restart re-mints identities and drops claims. Tabs survive; ownership does not.
- **Context never fails a call.** Every channel is best-effort: a page tree that would not render is logged and dropped, not raised.
- **The bundle travels twice.** Clients that render `structuredContent` ignore text blocks entirely, so the same report is emitted in both.

## Files changed
- `src/tenancy.ts`, `src/tools/tenancy-tools.ts` — identity registry, tab claims, envelope, `claim_tab` / `release_tab` / `list_agents`
- `src/context-bundle.ts` — context schema, window anchor, channel collection
- `src/firefox/bidi-ops.ts`, `src/firefox/bidi-input.ts` — tab-targeted navigate, evaluate, capture, and the full input set
- `src/utils/tab-routing.ts` — background-first routing with classified fallback
- `src/firefox/pages.ts` — background open and close
- `src/firefox/snapshot/manager.ts` — per-tab snapshots with retained generations
- `src/utils/asset-janitor.ts` — TTL sweep of generated assets
- `src/index.ts` — one decoration point: identity, tab resolution, envelope, bundle
- `docker/docker-compose.dev.yaml`, `docker/dev-reload.sh`, `docker/Dockerfile.mcp`, `docker/ff-takeover.sh`, `docker/smoke.sh` — dev stack and takeover

No breaking changes: every new argument is optional and every default matches or improves on the old behaviour. Bridge package untouched.
