Duplex is a browser shared by humans and AI, with the core idea of "one browser, humans see the rendered page, AI reads the DOM and source code, same tab, same live session, simultaneously."
Purpose
Duplex lets AI and humans collaborate in the same real browser session. Unlike browser-use / Playwright MCP, which lets AI run through a flow for you, Duplex lets AI use the browser together with you—same tab, same session, and when both sides operate on the page at the same time, humans take priority.
Main capabilities
- Real browser: tabs, address bar with search (Baidu / Bing / Google), forward / back / refresh, loading state, themes, wallpaper start page, customizable keyboard shortcuts, and a full bookmark manager (folders, editing, sorting, search).
- Human-priority collaboration: AI works on the bound tab and continues reading while you interact, but pauses modifications to that page and hands pages back one by one.
- 24 MCP tools, based on Playwright: snapshot compresses any page into a compact DOM outline with [eN] references; clicking, typing, dragging, and reading are performed through Playwright locators (stable across DOM changes, supports iframes and open shadow DOM, waits are cancelable). The remaining tools cover tabs, navigation, uploads, console logs, JS evaluation, and page annotation.
- Zero-configuration bridge: the stdio MCP bridge (mcp-bridge) automatically launches the browser on the first tool call, compatible with opencode, Claude Code, or any MCP client.
- Live session mirroring: when AI works through opencode, its replies, reasoning, and tool call cards stream into the side panel in real time; typing in the panel can inject messages into the same session.
- AI action visualization: translucent cursor, element highlighting, and status bar ("AI is clicking "…" — F2 to take over") drawn in a Shadow-DOM overlay.
- Emergency stop: press the configured emergency stop key (default F2 / Ctrl+Shift+K) or click the status bar to take over immediately: running tool calls are aborted, external processes started from the panel are terminated, pending confirmations are rejected, and queued messages are discarded. The paused state persists after restart.
- Page annotation: press Ctrl+Shift+A (or the toolbar ✎ button, or use annotation_mode) to draw boxes / circles / arrows / dots on any page and attach questions. Annotations are compiled into a structured text brief (DOM outline + visible text + selectors + geometry information) sent to AI, with delivery receipts, and expired annotations are rejected.
- Built-in agent (optional): connect to any OpenAI-compatible API (DeepSeek, Kimi, Qwen, GLM, Ollama, etc.) and let the browser drive itself. Provider management supports one-click import from opencode.
- Conversation history and session recovery: built-in agent sessions are saved locally and can be reopened at any time; browser tabs and active pages are restored after restart.
How to use
Installation: download the latest installer from Releases (Duplex Setup x.y.z.exe for Windows, .dmg for macOS (Apple Silicon / Intel), or .AppImage for Linux), run it, and launch Duplex. A portable zip is also available (extract and run Duplex.exe).
Building from source: if the Electron binary download fails, you can use $env:ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/"; node node_modules/electron/install.js to retry. If running a development build alongside an installed app, set DUPLEX_DATA_DIR to another directory to avoid sharing settings and local endpoints.
Connecting opencode (recommended):
- Install the mirror plugin: copy integrations/opencode/plugins/cobrowse-mirror.ts to the global opencode plugins directory (~/.config/opencode/plugins/), or the project's .opencode/plugins/.
- Register the MCP server in the opencode configuration (opencode.json), at project or global level—see opencode.example.json.
- In opencode, have the AI browse: "open example.com and describe the page". The browser starts automatically on the first tool call.
- In the Duplex side panel, select the opencode tab and choose the session to follow (or keep "Auto").
Using the built-in model (no opencode needed): Panel → Built-in tab → Model settings → Add an OpenAI-compatible provider (base URL, API key, model name), or click "Import from opencode" to reuse an existing configuration. For local models, point the base URL to Ollama, for example http://localhost:11434/v1。
Use cases
- When AI and a human need to collaborate on web tasks in the same browser session, such as searching, playing videos, filling out forms, browsing information, and summarizing.
- Let AI drive a real browser through opencode, Claude Code, or any MCP client, while the human can observe and take over at any time.
- Use page annotations to ask the AI questions, so that even a text-only AI can "see" what is being pointed at.
- The built-in agent can connect to a variety of OpenAI-compatible APIs, suitable for scenarios where you want the browser to drive itself.
Limitations
- Security boundary: Duplex lets AI drive your real browser session, including login state, cookies, and local data. Erroneous AI actions may touch real accounts and data (sending messages, submitting forms, modifying or deleting content), which may cause serious consequences. Do not let the agent run unattended in an environment where sensitive accounts are logged in. The emergency stop hotkey is the last human brake and cannot replace your judgment about what the AI can touch.
- When you interact on the page, AI input is synthesized DOM events (isTrusted=false); websites that rely on real keyboard/mouse events, contenteditable, or custom controls may require manual coordination, and side effects that have already occurred cannot be undone.
- Cancellation granularity: in-progress Playwright calls are subject to 200–250 ms polling limits—operations that have already completed (clicks, submissions, writes) cannot be rolled back.
- evaluate cleans up timers / RAF / fetch within its scope, but cannot undo callbacks registered elsewhere (for example, via document.defaultView); advanced usage is being further constrained.
- Element references are snapshot-scoped: a new snapshot is required after navigation or major DOM changes (stale references fail with a clear message rather than clicking the wrong element).
- Direct MCP clients share the default caller identity; an explicit task start / end / handoff protocol is planned for 0.3.0.
- opencode message deduplication depends on the plugin; if the disconnection time exceeds the lease, accepted but unacknowledged messages may be delivered twice—exact deduplication requires upstream message IDs.
- Custom CLIs without a structured startup identity are accepted only when a unique new record appears; ambiguous cases are rejected rather than guessed.
- Built-in multimodal image context, site-specific custom controls, and cross-platform interaction have not yet been fully verified.
- The built-in agent isConvenience options: local / smaller models are noticeably less reliable than the full opencode setup on long tool-use chains.
- CLI tools that open pages via the operating system (Claude Code, Codex, etc.) follow the system default browser. To route them to Duplex, select Duplex once in the OS settings—⋯ → Set as default browser… opens that page (never forced). CLIs and commands launched from Duplex also get the BROWSER=duplex-open shim, covering tools that respect $BROWSER.
Architecture notes
The Electron main process serves a small local HTTP API on 127.0.0.1, protected by a per-launch bearer token (endpoint information is written to ~/.cobrowse/endpoint.json). dist-bridge/index.cjs is a stdio MCP server that proxies tool calls to that API and automatically starts it when the app is not running. Page automation is Playwright through the browser's own Chromium: the app reads DevToolsActivePort, matches targetId to each tab, and drives locators, iframes, open shadow DOM, cancellable waits, and script evaluation—that is, the same page you are looking at, not a separate automation instance. The collaboration layer binds each task to its tab, serializes same-page writes, monitors real human input (wheel / touch / keyboard / focus), and pauses AI modifications per page; the status bar reflects this state and provides one-click resume per page. External scripts can also push messages to the session: POST /api/chat { "text": "..." }.