Tell your browser what to do.
A macOS chat panel that drives Dia, Arc, Chrome, Brave, Edge or a built-in Chromium: ask about the page,
hand it multi-step tasks, and follow up like you would with a person.
Press ⌘⇧Space anywhere and chat with your browser. Say what you want, it does it in your browser, and you follow up:
you open youtube and play a prince of egypt song
it I opened YouTube and played "When You Believe".
you no, another one
it Now playing "Deliver Us".
you what is this song about?
it It's the film's opening number … (from the video's transcript, 0:40 to 2:15)
The panel has two modes (Tab switches):
- Chat (default): the AI does each message step by step, seeing the page and the whole conversation. "Another one", "no, the second", "now delete them" all work. Chats are kept between launches (⌘4, past chats), and "remember that …" saves a fact for every chat ("what do you remember?", "forget …").
- Direct: instant one-step commands with no back-and-forth ("scroll down", "click sign in", "next tab"), decided in about a second by a model that picks options instead of writing text.
| Tasks | "add the first three results to my queue", "find the cheapest flight to Lisbon on the 14th": the agent looks at the page, acts, and repeats until a final check agrees it's done. Multi-item tasks get a checklist, so nothing is done twice or skipped. |
| Questions | About the page, several tabs at once ("which of my tabs has the cheapest flight?"), or the web. YouTube answers cite the time in the video, PDFs cite the page, and web answers name their sources. |
| Your tabs stay yours | The header shows the tab the agent is working in. When a task would take the tab you're looking at to another site, it opens a background tab instead (Show brings it forward). Go back to where I was undoes the tab changes a request made. |
| Queue & take over | Messages sent while a task runs wait their turn; questions are answered alongside it. Take over pauses the task so you can log in or solve a captcha, then Hand back. |
| Web search | The agent can search the web and read pages or PDFs without opening a tab. It uses Tavily or Brave keys if you add them, then Brave's public results, then Gemini's or Groq's search. |
| Video | "go back 10 seconds and play", "pause", "speed 1.5x". In Direct mode they act on the page's video at once, with no AI; in Chat the agent does it, knowing the conversation. |
| Site notes | Short per-site know-how, learned after tasks or taught ("remember that on workbook bills are under sales"). |
| Voice | Hold ⌥ Option or click the mic and speak. Replies to spoken requests are read aloud. |
| Several AI providers | Groq, Gemini, Z.ai, DeepSeek and OpenRouter. Auto routes each request to the fastest suitable model and moves on when one is rate-limited. |
| Forms | fill the form with … is a task: the agent fills text fields, dropdowns, dates and checkboxes, and submits only if you ask (and only after Allow). |
| Safe by default | Anything that saves, pays, sends or deletes asks Allow / Stop / Allow the rest. Passwords live in the macOS Keychain as {{name}} and never reach a model. |
| Saved requests | Save a finished request and run it again with no AI, changing its values (new-bill Globex, 99). |
Requirements: macOS (built and tested on Apple Silicon, macOS 26), Node.js 22+, and a free API key for at least one model (see Configuration).
git clone https://github.com/ken-code-wq/gofer.git
cd gofer
npm install
npx playwright install chromium
cp .env.example .env # then add your keys — .env is git-ignored and never leaves your Mac
# the desktop app
cd desktop && npm install
./install-mac.sh # builds /Applications/Gofer.app
open "/Applications/Gofer.app"Gofer lives in the menu bar. ⌘⇧Space shows or hides the panel (⋯ → Change shortcut… to change it) and the menu-bar icon has Start at login.
| Key | In the panel |
|---|---|
| ↵ | send, or answer Allow / Confirm when the box is empty |
| Tab | switch between Chat and Direct |
| Esc | cancel a prompt, stop what's running, close a list, then hide the panel |
| @ | pick an open tab |
| ↑ / ↓ | recall earlier messages of this chat |
| 1–9 | pick an option when it asks "Which one?" |
| ⌘N | new chat |
| ⌘1–5 | chat, saved commands, passwords, past chats, site notes |
fn fn |
macOS Dictation, to speak instead of typing |
Prefer the terminal?
./run.shstarts the server and opens a control page at http://localhost:8787 with live decision details and a microphone button (Chrome / Edge).
| Type | What happens |
|---|---|
go to example dot com · open youtube · go to localhost:3000/dashboard |
navigate |
search for alan turing · search youtube for lofi beats |
the site's own search box, a site search, or DuckDuckGo |
click the first result · click new bill |
click — ambiguous targets show numbered choices |
type hello into the search box · click on email and type {{dev-email}} |
type (verbatim) |
scroll down a bit · go back · reload · open a new tab · next tab |
scroll, history, tabs |
open youtube, theo t3.gg's channel |
a chain; a step with no verb means find it (search, then click) |
wait until "Dashboard" appears · check that the url contains /bills |
code-only waits and checks |
goal: … · create a new bill with dummy data · help me … |
a task (multi-step, model-planned); in Chat every message is one |
… ? · what's the total due? · summarize this |
a question about the current page, the web, or general knowledge (the reply says which) |
which of my tabs has the cheapest flight? · @[Tab A] @[Tab B] compare the prices |
a question across all open tabs, or the tabs you mention |
fill the form with … · fill the form and submit for approval |
a task: fill the form (values made up if you give none), then submit if asked |
in @[Tab] … · go to @[Tab] and … |
switch to that tab first |
in the current tab, … · … in the active tab · here, … |
run it in the tab you're on in the browser |
send me a screenshot · screenshot the whole page |
the image in the chat, and a PNG in Downloads (no model) |
… and send me a screenshot |
a task that ends by sending you a screenshot of where it got to |
copy url · copy title |
copy the current page's address or title to the clipboard |
copy the table · export the table as csv |
the page's own table to the clipboard (and a CSV in Downloads), no model |
copy all invoice numbers as a table · copy all vendors with open bills |
the AI pulls those items out of the page as a table |
go back 10 seconds and play · pause · skip ahead 1 minute · speed 1.5x · mute |
control the page's video (Direct mode: at once, no AI; Chat: through the agent) |
who won the match last night? · find the official site and open it |
search the web and read results without opening tabs |
Click the mic in the composer, or hold ⌥ Option while the panel is focused, and speak. Release or press ↵ to send, Esc to cancel. It needs a Groq API key and microphone permission. Replies to spoken requests are read aloud; the speaker icon under any reply reads it. Groq's voice model needs its terms accepted once in the Groq console; until then the Mac's own voice is used.
The agent keeps short notes per site (⌘5, or ⋯ → Site notes), learned after tasks or taught with "remember that on workbook bills are under sales", and uses them whenever it works on that site. Questions read the page the smart way: a YouTube video's transcript (answers cite the time), a PDF's text (answers cite the page), a Google Doc, or an article's main text. "Compare these tabs" or "compare @[A] @[B]" answers with a table.
The panel's header shows the tab the agent is working in, so a tab switch never takes you by surprise. With an attached browser (Dia, Arc, Chrome, Brave, Edge) it follows the tab you switch to; click it to pick another tab. The agent isn't limited to that tab: a request can open a new tab, switch to another one or close one. "In the current tab", "in the active tab", "on the tab I'm on" and similar phrases make sure a command runs in the tab you're looking at.
When a task would send the tab you're looking at to another site, it opens a background tab and works there instead, so what you were reading stays put. While it runs, the header shows that tab with Show to bring it forward, and a finished background task says so, with Show it. After a request that changed your tabs, Go back to where I was puts them back.
The eye button in the header opens a live picture of the agent's tab, so you can watch it work in a background tab without switching to it. It follows the agent from tab to tab; click the tab name to watch any other open tab instead (the agent keeps working where it is). Click the picture or Show to bring that tab to the front, and the arrows to make it bigger. It only streams while the panel is open, a few frames a second, and only when the page changes.
Some sites (Yelp, TripAdvisor, and others behind DataDome or Cloudflare checks) show "You have been
blocked" or a captcha instead of their content when they think a robot is browsing. The agent notices at
once, doesn't retry or scroll the block page, and gets the answer another way: from web search results, or
another site. It skips that site for the next 6 hours unless you ask for it by name ("on Yelp"); the list is
in ~/.gofer/blocked.json. If it's a captcha you can pass, it asks you to take over.
Messages you send while a task runs wait their turn (the box says "Queue another request"); questions about the page are answered alongside the running task. Take over pauses the task after its current step so you can log in, solve a captcha or make a choice yourself; Hand back lets it look at the page again and carry on. The agent asks for this itself when a page needs you.
- Save any message with the bookmark next to it, or add one with New in Saved, then run it by
typing its name (
login-dev). - Values: put
{}(or a named slot like{vendor}) where a value goes. Savedyt=open youtube, search for {}runs asyt lofi beats. Several slots take comma-separated values (bill Acme, 120). - Questions in Chat are answered from the page, several tabs at once ("which of my tabs …"), or general knowledge. Answers say where they came from.
- Save a request: when a request finishes, the bookmark under the reply keeps the exact steps it took. Running it again
uses no AI (fast, nearly free); risky steps still ask. What it typed becomes values you can change:
new-bill Globex, 99ornew-bill amount=99. If the page changes so a step can't be found, run the goal again and save it again. - Spend meter: the footer shows today's estimated AI spend (SubQ Snap + the brain). Click it to see the
week and set a daily limit; once reached, AI calls stop until tomorrow. Brain prices are list-price
estimates (free tiers cost nothing); set
BRAIN_PRICE_IN/BRAIN_PRICE_OUT(USD per million tokens) for a model that isn't priced. - Copy a result or answer with the copy button on its row, or press
Con a selected row. - Notifications: when something that took a while finishes while you're in another app, you get a macOS notification; click it to reopen the bar.
- Passwords tab → stored in your macOS Keychain. Write
{{dev-password}}in a command; the real value is typed into the page at the last moment. Models, history and logs only ever see{{dev-password}}, and password fields are never read back from pages.
Click the browser button in the bar (or the menu-bar icon → Browser) and pick Dia, Arc, Brave, Chrome or Edge. Gofer quits it like ⌘Q (tabs are restored), reopens it with remote debugging and reconnects. Chrome and Edge don't allow that for your main profile, so they get a separate profile instead. Your choice is remembered.
Everything lives in .env (copy .env.example). It is in .gitignore and is only read by the local
server — the UI never sees your keys.
| Variable | Needed for | Notes |
|---|---|---|
SUBQ_API_KEY |
single actions & chains | SubQ Snap — or TYPESAFE_API_KEY for TypeSafe Jev |
GROQ_API_KEY |
chat, goals, questions, forms | Groq: GPT-OSS 120B / 20B and Qwen 3.8 27B (reads screenshots), under a second per step |
GEMINI_API_KEY |
the same | free key from Google AI Studio; reads screenshots, slower |
ZAI_API_KEY, DEEPSEEK_API_KEY, OPENROUTER_API_KEY |
the same (optional) | GLM 5.3 Flash, DeepSeek, any OpenRouter model (OPENROUTER_MODEL) |
BRAIN_PROVIDER + BRAIN_API_KEY |
optional | the older single-provider setting (zai, deepseek, openrouter, groq, custom), tried first |
BRAIN_MODEL, BRAIN_BASE_URL, BRAIN_VISION |
optional | override a preset / point custom at any OpenAI-compatible API |
TAVILY_API_KEY, BRAVE_SEARCH_API_KEY |
optional | better web search for the agent (Tavily, Brave Search API); without them it uses Brave's public results, then Gemini / Groq search |
CDP_URL |
optional | attach to a browser you started with --remote-debugging-port |
PORT |
optional | local server port (default 8787, bound to 127.0.0.1) |
Several providers at once. Every provider you have a key for is used. The model picker in the
composer shows Auto (the default) or the model you pinned. Auto sends each request to the fastest model
that suits it: screenshots to a model that reads them, text to the fastest text model. When a model hits
its rate limit it rests for as long as the provider says, and the next one takes over. Groq's free tier
allows about 8K tokens a minute per model, which is one or two agent steps, so having Groq and Gemini
together keeps chats quick. Open the picker to see every model with its provider's logo, pin one, or paste a key under Add a provider (it's written to .env).
Each reply shows which models handled it.
Rough costs: a single action is ~3–6k input tokens on SubQ Snap ($0.05 / 1M input, output free). A goal step is one call to the "brain" model plus a final check; Gemini Flash-Lite's free tier covers casual use.
- The server listens on 127.0.0.1 only. Anyone who can reach that port can drive your browser.
- Page content (element list, visible text, a screenshot for goals/questions) is sent to the model providers you configure. Free tiers may use prompts to improve their models.
{{secrets}}are resolved from the Keychain only at the moment of typing.- Activity logs stay on this Mac in
~/.gofer/logs/for 7 days: a daily log of what the agent did and one file per task with what the model was shown (page text and element names, never passwords) and what it chose. Delete the folder any time, or setVB_TRACE=0to turn it off. - Risky steps (submit, approve, delete, pay) ask first — treat that as a convenience, not a guarantee, and don't connect a browser profile holding accounts you wouldn't want an automated click near.
See SECURITY.md for reporting vulnerabilities.
⌘⇧Space bar (Electron) ──ws──▶ local Node server ──▶ Playwright ──▶ Chromium / Dia / Arc / Chrome
│
├─ SubQ Snap / Jev: typed questions → act · wait · ask · ignore (~0.5–1 s)
├─ "brain" (Groq / Gemini / Z.ai / DeepSeek / OpenRouter, Auto-routed):
│ chat, tasks (native tool calls), questions, forms, follow-ups
└─ web search & page reading (Tavily / Brave / Gemini / Groq), no tab needed
Single commands are decided by a decision model answering a dozen typed questions at once (intent, target element, "is it complete?", "is it destructive?") with probabilities; code turns those into an action or a "which one?" prompt. Tasks, questions and forms go to a fast generative model with the page's elements (open menus and dialogs first), text and, when it helps, a screenshot. The model picks its next step with native tool calls (click, type, press a key, open a tab, search the web, tick off a checklist item, finish), and code checks whether each step changed the page before the next one. Details: docs/how-it-works.md.
src/ server, controller, decision policy, goals/questions/forms (smart.js), model clients
desktop/ the macOS command bar (Electron): bar.html, main.cjs, install-mac.sh
test/unit/ fast tests with mocked models and browser — no network
npm test # unit tests, no network or keys needed
npm run test:integration # real decision-model calls on captured pages (needs a key)
cd desktop && npm start # run the bar without installing it
./desktop/install-mac.sh # rebuild /Applications/Gofer.app after changing desktop/
npm run shots # redo the README screenshots (the real panel, scripted replies, no AI calls)Contributions are welcome — see CONTRIBUTING.md.
Gofer (formerly Voice Browser) started as a fork of jev-voice-browser by Moritz Kremb — the voice pipeline, decision questions and policy are his. This fork adds the desktop command bar, SubQ Snap support, chains, goals, questions, forms, follow-ups, tab mentions, Keychain secrets, waits/checks, connecting your own browser, the chat panel, several AI providers, web search, voice, site notes, background tabs and the task queue.




