Skip to content
Draft
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
170 changes: 170 additions & 0 deletions docs/design/2026-07-18-standalone-chrome-extension.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
# Standalone Qwen Code Chrome Extension

## Status

Implemented as a draft comparison branch, 2026-07-18.

## Goal

Provide an install-and-configure browser agent that does not require a running
Qwen process, while reusing Qwen Code's production Web Shell and browser tool
engine. Keep the daemon-based extension as the complete coding-agent path.

## Reverse-engineering findings

The inspected Claude 1.0.66 package contains two callers of one browser-tool
engine:

1. a standalone side-panel runtime that creates its model client in the
extension and runs the tool loop there; and
2. optional Native Messaging bridges used by Claude Code and Claude Desktop.

Anthropic's support documentation likewise presents ordinary Claude in Chrome
as an install-and-sign-in side panel, while the Claude Code documentation
describes a separate native integration for local coding context.

`noemica-io/open-claude-in-chrome` reconstructs the native chain (Claude Code,
MCP, TCP bridge, native host, extension), not the standalone model runtime.

This confirms that Qwen's pure-web version is technically feasible, but also
that local coding features are not made browser-native merely by sharing the
same UI.

The supplied `claude_1.0.66.zip` was used only as behavioral evidence. No code
was copied from it. Its SHA-256 is
`2d085a455621f07abb649feded74c85e31b0e6ff937823e679a81475dbf95cac`, and it
contains an injected remote-configuration layer. It must not be installed in a
real profile or given credentials.

## Architecture

```text
Qwen Web Shell
└─ DaemonWorkspaceProvider
└─ in-process StandaloneDaemonTransport
├─ session storage and daemon-shaped event replay
├─ ModelStudio OpenAI-compatible agent loop
├─ Web Shell permission requests
└─ existing BrowserTools
└─ existing ChromeDebuggerSession
└─ active Chrome tab
```

Only one small shared-UI change was required: `WebShellWithProviders` now
accepts the `DaemonTransport` injection point already supported by
`DaemonWorkspaceProvider`. The extension supplies an in-memory implementation
instead of rebuilding chat, transcript, tool, permission, session, or status
components.

The service worker remains responsible only for toolbar/side-panel behavior.
The model loop and debugger session live in the side panel so MV3 service-worker
suspension cannot interrupt a turn or invalidate snapshot element references.

## Reused capabilities

| Capability | Standalone implementation |
| -------------------- | ------------------------------------------------------------- |
| Chat UI and Markdown | Production Qwen Web Shell |
| Sessions and history | Daemon-shaped session API backed by bounded Chrome storage |
| Tool cards | Existing daemon transcript events and Web UI renderers |
| Permission UX | Existing Web Shell permission request/resolution flow |
| Model selector | Existing provider/model UI backed by standalone settings |
| Stop | Existing composer control aborts fetch/tool execution |
| Browser tools | All 20 existing CDP-backed extension tools |
| Skills display | Bundled browser skill exposed through the workspace APIs |
| Settings | Local form plus one-click, allowlisted `settings.json` import |

The production bundle is about 3.2 MB compressed. Most of its uncompressed size
is the existing Web Shell Markdown, syntax-highlighting, and diagram stack.

## Tool and permission model

Read-only snapshot, screenshot, wait, console inspection, and network
inspection execute without a second prompt after the user starts a turn.

Navigation, clicks, form entry, keyboard input, scrolling, script execution,
diagnostic clearing, and page-context HTTP requests issue a normal Web Shell
permission request. The user can allow or reject each action. A decision is
discarded if the active page changes before execution.

Page content is treated as untrusted. The model prompt forbids treating page
text as higher-priority instructions and forbids requesting or entering
passwords, payment data, tokens, and other secrets.

## Settings import

A pure Chrome extension cannot silently read `~/.qwen/settings.json`. Local
paths are outside the extension sandbox, and allowing silent filesystem access
would erase the security distinction from the native/daemon mode.

The standalone UI therefore offers a native file picker. Parsing occurs inside
the extension and imports only:

- `model.name`;
- a supported ModelStudio base URL;
- `BAILIAN_TOKEN_PLAN_API_KEY`, `DASHSCOPE_API_KEY`, or the supported auth API
key field.

MCP configuration, hooks, unrelated environment variables, and unrelated
secrets are ignored. The key remains session-only unless the user explicitly
selects persistent Chrome storage.

## Capability boundary

| Area | Standalone pure web | Daemon-based extension |
| ----------------------------- | ----------------------------------- | --------------------------- |
| Install and chat | No local process | Requires Qwen runtime |
| Browser reading/control | Full 20-tool browser engine | Full browser engine |
| Web Shell UI | Yes | Yes |
| Session history | Chrome-local, bounded | Daemon-managed |
| Repository/files | No | Yes |
| Shell/Git/processes | No | Yes |
| `QWEN.md` and project context | No | Yes |
| Skills | Bundled browser-only skills | Local and project skills |
| Hooks | No arbitrary local hooks | Full Qwen hook runtime |
| MCP | No local stdio servers | Full configured MCP support |
| Credentials/config | Picker or manual entry | Reads Qwen configuration |
| Background/schedules | Not implemented | Daemon/runtime dependent |
| Hosted account sign-in | Requires a separate backend product | Existing CLI auth paths |

Local skills and hooks are executable programs or filesystem configuration, not
just UI metadata. Reusing their Web Shell panels without a trusted execution
host would create controls that cannot work. A future standalone release may
bundle audited, browser-only skill prompts, but arbitrary local execution must
remain in daemon/native-host mode.

## Deliberate remaining gaps

- Model responses are currently displayed after each model step rather than
token-streamed. Tool progress, permissions, stopping, and final responses are
live daemon events.
- Session history is bounded rather than model-summarized.
- Claude-style workflow recording, multi-tab groups, scheduled background
tasks, notifications, upload tooling, and GIF capture are separate browser
product features, not provided by Qwen Code's current browser tool engine.
- Account sign-in and hosted safety classifiers require backend services and
cannot be recreated in an extension-only PR.

These gaps do not block the standalone architecture. They define follow-up
product work rather than reasons to duplicate the Qwen Code UI or run local
code unsafely.

## Security and release constraints

- Only `http:` and `https:` pages may be automated.
- Only four explicit ModelStudio HTTPS hosts and the
`/compatible-mode/v1` base path are accepted.
- API keys are never put into page context, URLs, tool output, or logs.
- Model errors and persisted tool content are bounded.
- Chrome storage holds at most 20 sessions, 100 messages per session, and 500
replay events per session.
- `chrome.debugger` remains a powerful permission; release integrity and a
narrow update channel are mandatory.

## Verification

- 92 Chrome-extension unit tests cover the agent loop, settings allowlist,
credential persistence, all browser-tool families, transport event flow, and
permission denial.
- Chrome-extension type checking and production packaging pass.
- The packaged artifact scanner passes.
8 changes: 8 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

144 changes: 67 additions & 77 deletions packages/chrome-extension/README.md
Original file line number Diff line number Diff line change
@@ -1,104 +1,94 @@
# @qwen-code/chrome-bridge
# Qwen Browser Agent — standalone

A Chrome extension that brings Qwen Code into the browser as a thin client of a
local [`qwen serve`](../../docs/users/qwen-serve.md) daemon — no Native
Messaging host to install.
This branch adds a browser-only companion to the daemon-based Qwen Code Chrome
extension. It reuses Qwen Code's Web Shell, daemon event protocol, permission
UI, and Chrome debugger tools while running the model/tool loop entirely inside
Chrome. It does not require `qwen serve`, Native Messaging, or an external MCP
process.

It does two things:
It is a browser agent, not a browser-hosted replacement for the Qwen Code CLI.
Local filesystem, shell, Git, repository context, local MCP servers, hooks, and
project skills remain exclusive to the daemon-based extension.

- **Side panel** — handles daemon discovery and pairing, then frames the
daemon's Web Shell (chat + tools).
- **Service worker** — hosts Qwen's browser MCP tools and executes them through
`chrome.debugger`. Tool calls travel over the daemon's reverse MCP WebSocket.

## Build
## Build and load

```bash
npm run build # -> dist/extension (static assets + bundled service worker)
npm run build
```

Then load it: `chrome://extensions`enable Developer mode **Load unpacked**
→ pick `dist/extension`.
Open `chrome://extensions`, enable Developer mode, choose **Load unpacked**, and
select `dist/extension`.

## Run
The first launch accepts:

The extension cannot spawn a local process, so start the daemon separately:
- a Qwen `settings.json` selected with **Import settings.json**; or
- an Alibaba ModelStudio endpoint, model name, and API key entered manually.

```bash
qwen serve
```
Chrome extensions cannot silently read arbitrary local files. The file picker
is therefore the closest browser-only equivalent to reading the local Qwen
configuration. The selected file is parsed locally, and only the active model,
supported endpoint, and supported API key are imported. MCP definitions and
unrelated environment variables are ignored.

The official extension id is pinned by `qwen serve`, so no browser-related
environment variables or `--allow-origin` flag are required. Custom or forked
extension builds must still pass their own origin explicitly:
The API key is stored in `chrome.storage.session` by default. Selecting
**Remember the API key after Chrome exits** moves it to
`chrome.storage.local`, which is persistent but not a hardware-backed secret
store.

```bash
qwen serve --allow-origin chrome-extension://<custom-extension-id>
```

Paste the pairing code printed by `qwen serve`. The credential remains in
Chrome storage across extension reloads, but a restarted daemon requires a new
pairing code because the daemon keeps trust state in memory. Once pairing
succeeds, the panel opens the chat UI and browser tools register immediately.
If Chrome storage is cleared while the daemon is still running, restart the
daemon to generate fresh pairing material.
## Reused Qwen Code experience

The first-use exchange sends only an HMAC challenge proof; the pairing code and
derived credential secret never cross HTTP. The extension verifies the daemon's
proof before storing that credential, then uses a separate challenge-response
before sending it over `/acp`. Pairing endpoints intentionally precede bearer
authentication so an unknown process never receives a stored bearer token. The
pairing code is time-limited and failed attempts are bounded.
- the complete Web Shell chat surface, Markdown rendering, tool cards, sidebar,
model selector, stop control, status bar, and responsive layout;
- the daemon SDK's session, replay, provider, tool, skill, and permission event
shapes through an in-process browser transport;
- persisted browser-chat sessions, with bounded history and event replay;
- the existing `BrowserTools` and `ChromeDebuggerSession` implementation;
- the existing Web Shell permission drawer for state-changing or sensitive
tools.

## Browser Automation Tools
## Browser tools

Browser debugging tools are implemented in and bundled with this Chrome
extension. The main `@qwen-code/qwen-code` npm package does not contain an
external Chrome DevTools MCP server. The first-release catalog covers page
snapshot/navigation/input, screenshots, JavaScript evaluation, console output,
and network request/response inspection.
The model receives all 20 existing extension tools:

Tools act on the active tab. `evaluate_script` and `send_request` execute in the
page context and can access that page's authenticated session, so use a dedicated
browser profile or tab for untrusted sites and keep normal tool approval enabled.
- accessibility snapshot and screenshot;
- navigation, reload, back, and forward;
- click, fill, multi-field form fill, keyboard, scroll, and wait;
- JavaScript evaluation;
- console list, detail, and clear;
- network request list, detail/body, and clear;
- page-context HTTP requests.

An explicitly configured `QWEN_CDP_MCP_COMMAND` remains a deprecated
compatibility path targeted for removal in PR2. When present, the extension does
not register its native tool catalog and instead keeps the CDP tunnel available
to that adapter.
Snapshot, screenshot, wait, and read-only console/network inspection run
without an extra prompt. Navigation, page mutation, JavaScript, clearing
diagnostics, and HTTP requests require explicit approval in the Web Shell.
Approval is invalidated if the page changes while the decision is pending.

Relevant `/capabilities` tags:
The tools operate through `chrome.debugger`, so Chrome displays its debugger
banner while a tab is attached.

- `allow_origin` means the extension may frame and call the daemon.
- `cdp_tunnel_over_ws` means the daemon exposes the reverse CDP tunnel.
- `client_mcp_over_ws` means extension-hosted tools can register over `/acp`.
- `browser_automation_mcp` means the legacy external adapter is configured.
## Pure-web boundary

## Onboarding states
The standalone path cannot safely reuse functionality that depends on the local
Qwen process:

The side panel probes `GET /health` and `GET /capabilities` and shows one of:
- filesystem, shell, Git, repository context, and `QWEN.md`;
- local skill discovery or execution;
- shell-based hooks and policies;
- stdio MCP servers and local subprocesses;
- CLI credentials or silent local configuration access;
- daemon background jobs and schedules.

| State | Meaning | Shown |
| -------------------- | ---------------------------------------- | -------------------------------- |
| `down` | no daemon reachable | "Start qwen serve" + command |
| `needs-upgrade` | daemon lacks secure extension pairing | Qwen Code update command |
| `needs-restart` | Chrome lost the active daemon credential | daemon restart guidance |
| `needs-allow-origin` | daemon up but `--allow-origin` not set | "Allow this extension" + command |
| `needs-pairing` | daemon reachable, credential not trusted | pairing-code form |
| `ready` | daemon reachable and paired | the Web Shell (chat) |
The standalone transport advertises a built-in browser skill because its
instructions and tools are bundled in the extension. Adding more bundled,
reviewed browser-only skills is possible. Executing arbitrary local skills or
hooks would require the daemon/native-host mode.

## Packaging for the Chrome Web Store
## Verify and package

```bash
npm run package # -> chrome-extension.zip (manifest at the zip root)
npm test
npm run typecheck
npm run package
```

Upload the zip to the Chrome Web Store Developer Dashboard. The `debugger`
permission will draw manual review; explain that it is used only after a paired
local Qwen Code daemon requests a browser debugging action. Host permissions
are limited to the loopback daemon.

Release the matching Qwen Code CLI before publishing the extension update. The
pairing handshake intentionally does not downgrade for older daemons; the side
panel detects them and shows an update command instead of sending browser tools
to an unauthenticated local process.
The packaged artifact is `chrome-extension.zip`.
Loading