Skip to content

Agent browser: agents drive ⌗ tabs through mpx browser - #70

Draft
jhen0409 wants to merge 1 commit into
mainfrom
agent-browser
Draft

jhen0409 wants to merge 1 commit into
mainfrom
agent-browser

Conversation

@jhen0409

@jhen0409 jhen0409 commented Oct 8, 2026

Copy link
Copy Markdown
Member

Summary

Host Settings ▸ Agent browser (per host, synced, default off) lets agents on that host open and operate web pages beside its terminal with mpx browser. CLI side: multiplex-term/mpx-cli#2, which also ships the multiplex skill and the Claude Code plugin.

  • Bridge. The app runs mpx bridge on its own SSH connection (SSHConnection.openLineChannel, a PTY-less login shell) and speaks JSON lines with it (AgentBrowserWire, pure).
    • Wire integers are bounded (wireInteger).
    • Requests that went stale during a suspension are dropped. The bridge clock is learned only from pongs that answer the ping in flight.
    • The link pings and redials on a network change or a host edit.
  • Agent tabs (ViewportController.Agent). Agents can drive only the tabs they opened.
    • Helpers run in an isolated WKContentWorld. There is still no message handler, so the page gets no bridge into the app.
    • Each tab uses the host's own data store, so the user's cookies are never visible.
    • Tabs carry an ⌗ AGENT label.
    • A tab docks beside the agent's tmux session, preferring a window that is on screen. It takes focus only through AgentFocusGate: never while the user is typing, at most once per 30 s per host.
  • Route: through the host (AgentBrowserProxy).
    • The tabs' data store gets a loopback CONNECT proxy (random credential, no failover). Every connection is an SSH direct-tcpip opened by the host, so the device's LAN is never on the path, and DNS-to-private-address tricks have nothing to reach.
    • A real device never proxies localhost, so host loopback ports are forwarded on the device.
      • Forwards admit a connection only if its first request carries an HttpOnly cookie, set per proxy through the tab's own store.
      • mpx browser forward <port> --open opts a port out of the cookie check. Re-checking the port closes the connections admitted while it was open.
      • Forwards close with the host's last agent tab.
    • A loopback content rule list blocks every other device port. WebRTC is removed from agent tabs.
    • Tunnels are backpressured both ways (AgentProxySplice).
    • DEBUG MULTIPLEX_AGENT_ROUTE=device keeps the old device route, for measurement.
  • Hardening from a Codex security review.
    • Quotas: 8 requests in flight per host, 6 tabs per host, one screenshot at a time, capped answers.
    • Console capture: levels are allowlisted and the buffer has a character budget.
    • Content rules fail closed.
    • Revocation cancels in-flight requests and closes the host's tabs.
  • Records: docs/agents/links-and-viewers.md ("Agent tabs"), architecture.md, e2e-headless.md. Strings are translated (zh-Hant, ja), and the TestFlight note is added.

Testing

  • Unit tests: visionOS 1466 ✅, iPad 1473 ✅. SwiftLint: 0 violations.
  • New tests cover the wire, the bridge clock, the address and loopback rules, proxy-head parsing, forward-cookie admission, AgentFocusGate, docking order, splice backpressure, and the composed AGENT label.
  • iPhone Air, against this Mac as the host:
    • A loopback-only dev server loads.
    • Forwarded WebSocket (HMR) works.
    • 60 parallel requests take about 0.2 s; a 50 MB download completes without stalling.
    • A listening device port that isn't forwarded is blocked.
    • --open behaves: checked → refused, open → reached, re-checked → refused.
    • Screenshots are capped at 1280 px.
    • A suspended request is not replayed after resume.
  • Simulators (visionOS / iPad): background-scheduling measurements and the bridge lifecycle. Notes are in local-plan/agent-browser.md (not committed).

Known limits

  • Bulk transfer tops out around 7 MB/s over Wi-Fi. swift-nio-ssh's per-channel window equals its 128 KiB maximum packet size; raising it naively made fetches hang.
  • A cross-port fetch without credentials is refused unless that port is --open.
  • *.localhost is not admitted; https://localhost works only on an open port.
  • Requires mpx 0.2 (the mpx-cli PR).

Host Settings ▸ Agent browser (per host, synced, default off) lets agents
on that host open and operate web pages beside its terminal via
`mpx browser` (mpx-cli PR). The app runs `mpx bridge` over its own SSH
connection and serves requests against AGENT viewport tabs only.

- Tabs: helpers in an isolated WKContentWorld (still no message handler —
  the page gets no bridge), the host's own data store, docked beside the
  agent's tmux session on a window that is on screen; focus taken only
  through AgentFocusGate (never mid-typing, once per 30 s per host).
- Route: pages load THROUGH THE HOST — a loopback CONNECT proxy for the
  tabs' data store, every connection an SSH direct-tcpip from the host, so
  the device's LAN is never on the path. A real device never proxies
  loopback, so host localhost ports are forwarded on the device, admitted
  by an HttpOnly per-proxy cookie (`forward --open` opts a port out) and
  closed with the host's last agent tab; a loopback rule list blocks every
  other device port. WebRTC is removed from agent tabs.
- Robustness: bounded wire integers and buffers, per-host quotas, stale
  requests dropped after suspension (bridge clock from trusted pongs),
  revocation cancels in-flight work and closes the host's tabs, the link
  redials on network change, backpressured tunnels.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant