Connecting backends
No accounts, no subscription, no markup — browsa simply brings your own model endpoints into the side panel. Cloud, local, or self-hosted: all welcome.
Two kinds of backends
They split into two kinds — LLM providers are plain chat models, Agent providers are agents that can act on their own:
- LLM providers — plain chat endpoints, covering nearly every service: OpenAI, Anthropic, Ollama, Groq, LiteLLM, aggregation gateways.
- Agent providers — full agent backends with server-side tool execution: they can search, run commands, and operate files — the kind that can actually do things. Four are supported:
- Hermes Agent — a self-hosted agent, built into browsa; just point it at your server.
- OpenCode Agent — the opencode CLI agent with its first-party headless server. Also built in: one command starts the server, browsa connects directly.
- OpenSquilla Agent — OpenSquilla, a local desktop agent (gateway + web UI + desktop app) with a token-efficient microkernel design, model routing, and skills; browsa talks to its gateway WebSocket, with session memory living server-side.
- Agent Bridge — a bridge to local agents (codex, claude, pi, gemini, dsh…): a standalone daemon turns local agents into one unified-protocol backend, and a single card connects to several agents at once.
When you switch to any agent, browsa asks what to do with the current conversation — carry it over as the new agent's first message, or start a new session from scratch; every reply is labeled with the provider / agent that produced it.
Filling in a provider card
Open ⚙ Settings → LLM Providers. An empty LLM 1 card is reserved for you — fill it in and Save. Providers live as tabs on one card; add more anytime with the + tab at the end of the tab bar:
| Field | What to put |
|---|---|
| Alias | A name you choose ("My OpenAI", "本地模型") — shown in the sidebar dropdown so multiple cards stay distinguishable |
| Base URL | Root path only, e.g. https://api.openai.com — protocol paths are appended automatically |
| API Key | Your key, stored locally only (see privacy) |
| Model ID | Required, e.g. gpt-4o; comma-separate several — one card covers an entire gateway's catalog |
| API protocol | What this endpoint speaks: Chat Completions / Responses / Anthropic |
Save, then hit Ping: connectivity is verified and capabilities auto-detected. The first provider to ping successfully becomes active automatically; switch anytime from the dropdown at the top of the panel — multi-model cards expand to one "Alias · model" entry each.
Hermes Agent
Hermes is a self-hosted AI agent with built-in tools (web search, terminal, files, memory). browsa speaks its /v1/runs protocol — which adds live tool progress and approval cards for dangerous actions (approve first, execute after):
- Install:
pip install hermes-agent(or follow the official guide); - Add to
~/.hermes/.env:API_SERVER_ENABLED=trueandAPI_SERVER_KEY=your-secret-key; - Start:
hermes gateway— look forAPI server listening on http://127.0.0.1:8642; - In browsa settings, select the Hermes Agent card, fill Base URL (e.g.
http://127.0.0.1:8642) and API Key, then Ping.
The Hermes card is fixed and always present; it needs only a Base URL and key — the protocol is auto-detected, falling back to plain chat if the server doesn't advertise /v1/runs.
Self-hosting: server-side settings for faster replies
By default Hermes mounts its full toolset for API clients — the definitions of a dozen-plus tool groups (terminal, browser, cron jobs, …) ride along with every request (about 23K tokens measured), and casual page Q&A uses a fraction of them. Thinking mode is also on by default, and that wait lands before the first word. Tighten both in ~/.hermes/config.yaml:
platform_toolsets:
api_server: # affects API clients (browsa) only — Telegram etc. keep their own
- web # search, page extraction
- vision # image understanding
- clarify # follow-up questions
- memory # cross-session memory
agent:
reasoning_overrides: # per-model thinking effort (the global knob is agent.reasoning_effort)
your-model: medium
Run hermes gateway restart to apply. Measured by the author (qwen3.8-flash, an 80K-token context): the slimmed toolset sheds about 14K tokens per request, and the lower thinking effort directly cuts the wait before the first word. Also note that model services generally accelerate consecutive follow-ups within ~5 minutes via prefix caching — asking in a row is much faster than returning after a few minutes. That part is on the model side; nothing to configure.
OpenCode Agent
opencode is an open-source CLI agent that ships a first-party headless server — browsa connects to it directly (multi-turn context lives in the opencode session; browsa just sends your turns). The agent works on whatever directory the server was started in:
- Install:
npm i -g opencode-ai, then follow the first-run login flow; - Start:
opencode serve --port 4096. Pin the port — bareopencode servepicks a random one that changes on every restart; a pinned port is a set-and-forget config in browsa; - In browsa settings, select the OpenCode Agent card, fill Base URL
http://127.0.0.1:4096(the placeholder suggests it), then Ping.
When opencode wants to run a dangerous action (like a shell command), the approval card appears right in the browsa panel. To enable that, mark the permissions as ask-once in ~/.config/opencode/opencode.json:
{
"permission": { "bash": "ask", "edit": "ask" }
}
OpenSquilla Agent
OpenSquilla is a local agent (gateway + web UI + desktop app) with a token-efficient microkernel design, model routing, and skills. browsa talks to its gateway WebSocket (/ws) — the same channel its own web UI uses — so you get the full agent experience: server-side session memory, streaming deltas, thinking output, and server-side cancellation. Conversations show up in the app's sidebar under a browsa:… name, so you can open the desktop app later and pick any of them up right where the panel left off.
Run the gateway either as the desktop app or from the command line — browsa connects to both the same way. The tracks differ in exactly one thing: which config file the gateway reads (step 2 in each track). Editing the wrong file is the most common reason the connection silently fails.
Way A — Desktop app (no terminal)
- Install & launch the OpenSquilla desktop app, v0.5.5 or newer (older builds bundle a gateway whose origin guard doesn't accept extensions). The app starts its own gateway automatically — open the app's settings and note the gateway URL it shows (typically
http://127.0.0.1:18791; it takes the first free port in 18791–18830). - Let the extension in — first locate the config file the desktop app actually reads (it does not read
~/.opensquilla/config.toml; editing the wrong file is the most common reason the connection silently fails). The most accurate source is the app itself: Settings → Advanced → Config file shows the exactconfig.tomlpath on your machine (with a copy button) — use that. Two common defaults: macOS official package~/Library/Application Support/OpenSquilla/opensquilla/config.toml; self-built package~/Library/Application Support/@opensquilla/desktop-electron/opensquilla/config.toml(the two data directories are fully separate). The path contains spaces, so escape them in a terminal: wrap the whole path in quotes, or backslash-escape each space — unescaped, the shell splits the path at the space (cdfails with No such file or directory;vimsilently opens a wrong empty file):open -e "
If the file doesn't exist yet, create it as plain text with:" # opens in macOS TextEdit cd ~/Library/Application\ Support/ # quote-less alternative: backslash before each space (Tab completion does this too) [cors] allowed_origins = ["chrome-extension://kghjmmajnpbkljankbbjbmnhfdocaeho"]
- Fully quit and reopen the app (closing the window is not enough) — the gateway reads this config once at startup. Models / API keys are configured inside the app (its setup window), not via shell env vars. Advanced: the app can also attach to an externally-run CLI gateway via
OPENSQUILLA_DESKTOP_GATEWAY_URL. - Jump to Configure browsa below.
Way B — Command line
- Install & start the gateway (uv provides Python 3.12):
uv tool install --python 3.12 "opensquilla[recommended] @ https://github.com/opensquilla/opensquilla/releases/download/v0.5.5/opensquilla-0.5.5-py3-none-any.whl" opensquilla gateway start # → running: http://127.0.0.1:18791
- Let the extension in — the CLI gateway reads
~/.opensquilla/config.toml. Add the same[cors]block as in Way A. The gateway's model routing is configured here too (see OpenSquilla's own docs; LLM keys typically come from the environment the gateway is started with). - Restart the gateway (
Ctrl+C, thenopensquilla gateway startagain) — the config is read once at startup. - Jump to Configure browsa below.
Configure browsa (same for both): in browsa settings, switch to the OpenSquilla tab — Base URL ws://127.0.0.1:18791/ws (use the URL your gateway actually shows); fill the API Key only if your gateway requires a token. Hit Ping to verify — it performs the real WebSocket handshake, so a green ping proves both connectivity and the origin allowlist.
This is browsa's extension ID (verify at chrome://extensions → browsa → ID — use whatever value is shown there for an unpacked build); the listing is an exact string match (* does nothing). Use ws:// for a local gateway — wss is rejected.
Notes: each browsa conversation maps to one gateway session (reset when you clear browsa's history). Chat history lives on the gateway side — browsa forwards your text plus any page you attached right before asking (the 📎 context rides along on the next message); a huge page (over 60k chars) is uploaded as a page-context.md document the agent reads with its own tools. Pasted screenshots stay in browsa's own history and are not forwarded yet.
Agent Bridge
agent-bridge is a tiny standalone local daemon: it adapts local agents — codex, claude, pi, gemini, dsh, zcode — to one unified local HTTP protocol. A ChatGPT Plus / Claude Pro subscription login works as the model source — no separate model-API purchase needed; the Gemini CLI signs in with your Google account (or GEMINI_API_KEY); pi runs on whatever model providers you configure inside it; dsh runs on a DeepSeek API key, and its sessions also show up in the dsh desktop/Web UI; ZCode rides its official CLI's headless mode and reuses ZCode's own login and model config.
Prerequisites: Node.js 18+ on the machine, plus the agent CLI you want to bridge, logged in (codex: npm i -g @openai/codex then codex login with your ChatGPT subscription account; claude: npm i -g @anthropic-ai/claude-code, run it once to log in, then npm i -g @agentclientprotocol/claude-agent-acp; gemini: npm i -g @google/gemini-cli, run gemini once to sign in with your Google account (or set GEMINI_API_KEY); pi: npm i -g @earendil-works/pi-coding-agent pi-acp, run pi once to pick its model providers (Node 22+ required); dsh: install DeepSeek Harness — the desktop app or npm i -g @deepseek-ai/dsh — and save a DeepSeek API key once: dsh-acp login, or the dsh Web UI → Settings → Models (the openma adapter dsh-acp ships bundled with agent-bridge); zcode: install the ZCode desktop app and sign in once — the bridge auto-finds the CLI bundled with the app, no env vars or path tuning).
- Install the bridge (published on npm):
npm i -g @xiaohuzai/agent-bridge # or try without installing: npx @xiaohuzai/agent-bridge serve
- Prepare a config — every bridge, single or many, is one entry in
agents.json; ports live in the config:cp "$(npm root -g)/@xiaohuzai/agent-bridge/agents.example.json" agents.json chmod 600 agents.json
{ "bridges": [ { "name": "codex", "port": 3948, "apiKey": "", "sandbox": "workspace-write", "approval": "on-request" }, { "name": "claude", "port": 3949, "apiKey": "" }, { "name": "pi", "port": 3950, "apiKey": "" }, { "name": "gemini", "port": 3951, "apiKey": "" }, { "name": "dsh", "port": 3952, "apiKey": "" }, { "name": "zcode", "port": 3954, "apiKey": "", "mode": "edit" } ] } - Start it (from the directory holding
agents.json):agent-bridge serve # reads ./agents.json agent-bridge serve --config /path/to/agents.json # explicit config path # add --bind 0.0.0.0 to expose it beyond this machine (then every entry needs an apiKey)
- In browsa ⚙ Settings, select the Agent Bridge card and click + Add agent to add bridge addresses one per row (one agent per address, e.g.
http://127.0.0.1:3948). Each row also takes an optional alias and that bridge's own API key — that key is the bridge's own access token (apiKeyinagents.json), not a model-service key; leave both empty for purely local use; - Hit Ping — every address is probed; rows with an empty alias are auto-filled from the agent name returned by /health, and everything shows in the status (e.g.
×5/5 healthy (codex, claude, pi, gemini, dsh)). Each row carries its own status dot (gray = untested, green = reachable, red = down) and a⟳button to re-probe just that agent anytime; - Back in the sidebar, the top dropdown lists them as "Agent Bridge · codex", each with its own ping status. Each agent keeps its own independent session thread; when you switch mid-conversation, browsa asks whether to carry the conversation over or start a new session.
- Agent narration before tool calls (e.g. codex announcing a skill) folds into the step history instead of the reply body.
Don't feel like running steps 1–3 by hand? Paste the whole block below to your CLI agent and let it do the install, config, and launch — then just fill the bridge URL it reports into step 4 above:
Set up the agent-bridge daemon on this machine so the browsa browser extension can reach me over local HTTP:
0. First check the scene: you must be running on the same computer that has the browsa browser extension installed (the bridge must share the machine with the browser). If you are in an SSH session, a container, or on another machine, stop and tell me.
1. Install globally (Node >= 18): npm i -g @xiaohuzai/agent-bridge
2. Create agents.json in a fixed working directory (the package's own agents.example.json and README are authoritative for the entry shape), adding an entry for yourself. If you are codex, keep "approval": "on-request" (otherwise approval prompts never reach browsa) and "sandbox": "workspace-write":
{"bridges":[{"name":"codex","port":3948,"apiKey":"","sandbox":"workspace-write","approval":"on-request"}]}
Other CLI agents (e.g. claude, pi) only need name/port/apiKey.
3. Start and verify: run agent-bridge serve (reads ./agents.json by default and stays in the foreground — verify from another command): curl http://127.0.0.1:3948/health should return JSON containing your name.
4. Real-turn smoke test (/health only proves the process is alive, not that the agent can complete a turn): from yet another command, send one minimal turn:
curl -N -s -X POST http://127.0.0.1:3948/turns -H 'Content-Type: application/json' -d '{"text":"Reply with exactly one word: ready"}'
The curl ends by itself once the reply finishes; along the way the stream should show start (with a sessionId), then delta, then done. If an error event appears, send me its message verbatim.
5. When done, report ONLY the final bridge URL (http://127.0.0.1:PORT); I will paste it into browsa's Agent Bridge card and click Ping.
If any step fails, stop and show me the exact error — do not retry the same command in a loop.
Screenshots, pasted images, and PDF figures ride along with your message (≤18 per turn — needs agent-bridge ≥0.8.0; older bridges degrade to the first 8 with a note in the message). Ports live in agents.json, so after changing one update that row's address in browsa to match.
Agents behind the same Agent Bridge do not all behave identically — the differences are only these few, and everything else (install, config, Ping, switching sessions) is shared:
- codex: approval cards need
"approval": "on-request"on that bridge's entry (the defaultneversilently refuses dangerous actions). - pi: built-in tools run without asking (usually no approval card) and it reports no token usage.
- zcode (needs agent-bridge ≥0.12.0): rides the official CLI's headless mode, which differs from the others in four ways:
- No approval cards: nobody can answer a confirmation in a headless turn, and the CLI's own
-pdefault is yolo — so the entry's"mode"is the only permission gate. The defaulteditauto-applies changes;planis read-only Q&A;guardedbehaves like yolo and stops only before dangerous commands such asrm -rf,git reset --hard, or a force push (with no one to approve, headless refuses that command);buildwaits for an approval and stalls — don't use it headless;yoloallows everything. - ZCode decides the model: the first provider in
providerOrder, then the first model in itsmodelOrder, is what runs — the bridge never passes a model and cannot. To change it, edit~/.zcode/v2/provider_config.json(close ZCode first — a running server overwrites the file from memory) and keepmodelOrderandpersonalModelIdsidentical. Account-plan providers (e.g. a logged-in zhipu bigmodel plan) are never auto-selected: with no custom provider visible,zcode -pfails withSelect a model before sending a message. Either add a custom provider, or pick the model in the desktop app / CLI first and continue that session by ID (--resumeskips model resolution and keeps the session's model). Each turn reports the model it actually resolved on anote {kind:"model"}event — trust that one. - Images degrade to text: the CLI only attaches local files (
--attach) and the bridge never writes your images to disk — screenshots and PDF figures ride along as a text notice, not pixels. - One process per turn: a new session boots in ~2–4 s (a PONG turn measured ~12.7 s, a tool turn ~18.5 s) and each spawn carries the CLI's own ~28K-token system prompt; continuing the same session hits the provider prefix cache.
- No approval cards: nobody can answer a confirmation in a headless turn, and the CLI's own
zcode CLI lookup order: the entry's command (when set to a non-default value) > the ZCODE_CLI environment variable > zcode on PATH > auto-discovery of the CLI bundled with the desktop app (Linux ~/.zcode/server/bin/zcode; macOS/Windows the app's resources/glm/zcode.cjs, run on the app runtime). Install the ZCode desktop app and sign in once — zero-config usually just works; if the file exists but doctor can't find it, point any of the first three at it.
Ollama, locally
Running Ollama on your machine? Base URL http://localhost:11434, protocol Chat Completions (Ollama speaks the OpenAI format), Model ID set to a pulled model such as qwen3:32b. Fully offline-capable.
/v1/chat/completions tail); the key is valid; a local model is actually running; your network doesn't block the domain. Errors in the panel come as classified error cards with the raw message expandable and copyable.