Chrome extension
Use Agentak as an MV3 side panel that follows the active tab.
The Agentak repository includes a Chrome MV3 side panel built with the same ChatPanel
and Pi session as the embeddable widget. The panel adds browser capabilities that a chat
inside a normal page does not need.
#Install the unpacked extension
chrome://extensions.The extension is not in the Chrome Web Store yet.
#What the panel adds
#Read the active page
The built-in read_active_tab tool returns the active tab's title, URL, and rendered text.
It is read-only, so it runs without a confirmation. Page text is marked untrusted: the model
and the transcript both warn that it is data, not instructions.
The panel follows tab changes and navigation, so the next call reads the page currently in front of the user rather than the tab where the panel first opened.
An empty panel offers one starter at the foot of its greeting, Summarize this page,
which is that tool in one click. It is the panel's prompts, and it goes with the greeting
once something has been said.
The panel drops this tool when the active page publishes a reader of its own — a WebMCP
tool named read_page, read_current_page, read_page_content, read_this_page,
get_page_content, or get_page_text. A page that reads itself knows its own structure,
and two readers under one list only make the model choose between them.
#Discover WebMCP tools
The panel also lists tools published by the active page on document.modelContext. Calls
run in the tab's main world because a live WebMCP tool cannot be serialized into the
extension. Only metadata, JSON arguments, and result text cross the bridge.
The toolbar badge shows how many tools the page publishes. It does not count
read_active_tab, which exists on nearly every reachable page.
#Resolve links against the tab
A relative link such as /settings in an answer belongs to the page being discussed. The
panel passes the active tab URL as linkBase, so the link resolves against that site
instead of chrome-extension:. A browser-internal page with no safe HTTP base leaves an
unsafe link as text.
#Keep history per site
Provider keys and model choices belong to the whole extension. Conversations do not: the panel keeps a separate history shelf per site. Moving to another origin moves the chat to that site's shelf. A conversation being answered during navigation remains owned by the site where it started.
Tabs without an HTTP origin share a shelf named for the current tab.
#Bundle provider catalogs
Normal Agentak pages load keyed-provider catalogs from esm.sh. MV3 blocks remote modules,
so the extension bundles one lazy catalog chunk per keyed provider and supplies it through
useCatalogSource().
The extension can list Kilo Gateway and OpenCode Zen because its host permissions are not gated by page CORS. Chrome Built-in AI is listed when the Prompt API is available.
#Bundle wllama
On Device (wllama) is listed in the panel as well, for the same reason and with one extra
step. extension/wllama/ supplies the module and the wasm through useWllamaSource(),
which is what puts the row back in the picker.
The extra step is the Worker. wllama joins the emscripten glue and its own worker code
into one string per run and starts it as a blob: URL. MV3 matches a worker script
against worker-src, which falls back to script-src 'self', and blob: is not
'self' — nor can the policy be widened to allow it. So the build writes that worker out
as a file next to the bundle and rewrites wllama's createWorker() to load it, passing
the per-run options on the worker's URL. The package is not patched; the rewrite is a Vite
transform, and the version is pinned exactly so an update is read rather than assumed.
The panel runs single-threaded. Threads mean pthreads, which emscripten also starts from a
blob, and wllama asks for SharedArrayBuffer first — the manifest declares no
cross-origin isolation, so it never gets one.
Model weights still come from Hugging Face on first use and are cached in OPFS, so the first answer on a model waits for the download and later ones do not.
#Storage
The panel uses chrome.storage.local for keys, choices, and conversation shelves. It reads
and writes that area directly, because PiStorage is asynchronous, and mounts on
session.ready so the first render never shows forgotten choices. A write the area refuses
for want of room rejects, which is how built-in history learns to give up an older
conversation.
Closing Chrome's side panel destroys its document. Stored state restores on the next open, and the composer requests focus because the chat is the whole document.
#Permissions
The manifest requests:
sidePanelto provide the browser panelstoragefor choices, keys, and conversationsscriptingto read the active tab and bridge WebMCP- host access to HTTP and HTTPS pages and to follow their selected URLs
Broad host access is intentional. activeTab would cover only the tab where the toolbar
button was clicked, while a side panel remains open as the user changes tabs. The cost is
Chrome's all-sites install warning.
The panel cannot read browser-internal screens such as chrome:// pages. That failure is
returned as a tool error instead of breaking the whole turn.
#Build it from source
From the repository root:
pnpm build:extensionLoad extension/dist unpacked. The same command creates the zip served by this
documentation site.
The MV3 Content Security Policy permits WebAssembly because Agentak's Markdown parser uses wasm. It still disallows remote executable code and Workers built at run time, which is why catalogs are bundled and why wllama's module, wasm, and Worker are all written into the build. The panel is about 8 MB larger than it would otherwise be, most of it the wllama wasm.