Imported from Arvid-pku/MultAI (
AGENTS.md). Install upstream withnpx skills add Arvid-pku/MultAI. Copyright stays with the author.
AGENTS.md — MultAI
Project overview
MultAI is a Chrome extension (Manifest V3) that embeds multiple AI chat providers (ChatGPT, Claude, Gemini, Grok, Meta AI, DeepSeek, Qwen) as iframes inside a single "cockpit" tab. Users type one prompt and broadcast it to all active providers simultaneously. There is no backend server — each iframe runs the real provider site under the user's own login, and all data stays in chrome.storage.local.
Tech stack
- Plain JavaScript (ES modules for extension pages, IIFE for content scripts). No TypeScript.
- Plain CSS + Google Fonts. No preprocessors or CSS frameworks.
- No build step. Load the repo folder directly as an unpacked Chrome extension.
- No npm/yarn dependencies. No
package.json, nonode_modules. - No CI/CD, linter, or formatter configured in the repo.
Architecture
┌─────────────────────────────────────────────────────┐
│ Extension pages (chrome-extension://...) │
│ ┌──────────────┐ ┌────────────┐ ┌─────────────┐ │
│ │ cockpit.js │ │ options.js │ │ service- │ │
│ │ (main UI) │ │ (settings) │ │ worker.js │ │
│ └──────┬───────┘ └─────┬──────┘ └──────┬───────┘ │
│ │ │ │ │
│ │ chrome.storage.local │ DNR │
│ │ ◄─────────────┘ rules ─┘ │
│ │ │
│ │ postMessage (multai:*) │
│ ▼ │
│ ┌──────────────────────────────────┐ │
│ │ Provider iframes │ │
│ │ anti-framebust.js (MAIN world) │ │
│ │ _runtime.js + content.js │ │
│ └──────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
Embedding flow: Static + dynamic declarativeNetRequest rules strip framing headers (X-Frame-Options, CSP, COOP, COEP, CORP, Permissions-Policy). anti-framebust.js runs in MAIN world at document_start to neutralize JS-based frame-busting.
Communication: Cockpit ↔ provider content scripts talk via window.postMessage with multai:*-prefixed message types. sendToPane() in src/shared/messaging.js posts a message and awaits a reply matched by _replyTo.
Readiness: After an iframe loads, cockpit sends multai:wake on an interval until the content script replies multai:ready.
Directory layout
manifest.json Extension manifest (MV3)
rules/iframe-headers.json Static DNR rules for header stripping
assets/ Icons (icon.svg; PNGs referenced but may be missing)
src/
background/
service-worker.js DNR setup, action click handler, tab management
cockpit/
cockpit.html/css/js Main UI: pane grid, broadcast, compare, bench, library
options/
options.html/css/js Settings page: crew toggles, plan display, storage reset
shared/
providers.js Provider registry (ids, labels, URLs, origins)
messaging.js MSG constants, sendToPane() helper
attachments.js File size/name utilities
anti-framebust.js MAIN-world frame-busting countermeasure
design-tokens.css Shared CSS variables
providers/
_runtime.js Shared content-script framework (register(), DOM helpers)
chatgpt/content.js ChatGPT automation (standalone, does not use _runtime)
claude/content.js Claude automation (uses _runtime.register)
gemini/content.js Gemini automation
grok/content.js Grok automation
meta/content.js Meta AI automation
deepseek/content.js DeepSeek automation
qwen/content.js Qwen automation
Key modules
| Module | Responsibility |
|---|---|
src/shared/providers.js |
Single source of truth for provider metadata: id, label, default URL, temporary-chat URL, postMessage origin. Exports DEFAULT_CREW. |
src/shared/messaging.js |
Message protocol constants (MSG.WAKE, MSG.BROADCAST, etc.) and sendToPane() with timeout support. |
src/cockpit/cockpit.js |
Main UI controller. Manages pane lifecycle, layout (grid/tabs), broadcast dispatch, Compare drawer, prompt library, bench, keyboard shortcuts. State persists via chrome.storage.local under multai.* keys. |
src/providers/_runtime.js |
MAIN-world shared framework. Provides setPrompt, submit, attachFiles, readLast, and register(config) which wires up the postMessage listener for all standard operations. |
src/providers/*/content.js |
Per-provider selectors and overrides. All providers call __multaiRuntime.register({...}). |
src/background/service-worker.js |
Installs dynamic DNR rules, opens/focuses cockpit on action click, handles multai:open-in-tab. |
Judge feature
Each pane header has a Judge button that collects the last assistant reply from other panes and composes a meta-prompt asking the selected model to evaluate them.
skipSubmitflag: TheBROADCASTpayload supports an optionalskipSubmit: trueboolean. When set, provider content scripts fill the chat input but do not auto-submit, giving the user a chance to review or edit the prompt. The judge flow uses this flag; normal broadcast does not.- Judge picker popup: Clicking the Judge button opens a popup dialog (
#judge-picker) listing all other ready panes with checkboxes (all checked by default). The user selects which models' responses to include, then clicks "Judge" to proceed. The popup HTML lives incockpit.html; styles are incockpit.cssunder.judge-picker*.
Conventions
- Message prefix: All cross-context message types use the
multai:prefix (e.g.multai:wake,multai:broadcast,multai:ready). - Storage keys: All
chrome.storage.localkeys are prefixed withmultai.(e.g.multai.state,multai.plans,multai.library). - Console logging: Use
[multai]or[multai-<provider>]prefixes for allconsole.log/console.warnoutput. - Provider content scripts: Place selectors at the top of each
content.jsfor easy patching when provider sites change their DOM. - Error handling: Wrap brittle DOM queries and
postMessagepaths in try/catch. - Privacy: No data leaves the browser. Never introduce external analytics, telemetry, or proxy servers.
Adding a new provider
- Add the provider entry to
src/shared/providers.js(id, label, url, origin, optionally temporaryUrl). - Create
src/providers/<id>/content.jsimplementing the provider contract. Use__multaiRuntime.register({...})from_runtime.js— implement at minimumselectors,probe(),broadcast(),readLast(). - Register content script injection in
manifest.jsonundercontent_scripts(match the provider's URL pattern; inject_runtime.jsthencontent.jsin MAIN world). - Add the provider's domain to
host_permissionsand the DNR header-stripping rules in bothrules/iframe-headers.jsonand the dynamic rules inservice-worker.js. - Add the provider's origin to
content_security_policy.extension_pagesframe-src. - Add an
anti-framebust.jscontent script entry if the provider uses JS-based frame-busting.
Running locally
- Open Chrome →
chrome://extensions→ enable Developer mode. - Click Load unpacked → select this repository folder.
- Click the MultAI toolbar icon to open the cockpit.
- Log into each AI provider in its respective pane.
No build, install, or compile step is required.
Important constraints
- No build system. All JS must be valid for Chrome's V8 without transpilation. Do not introduce JSX, TypeScript, or syntax requiring a build step unless the project explicitly migrates.
- Manifest V3 restrictions. No
eval(), no remote code loading. Service worker is event-driven (no persistent background page). - Content script worlds.
anti-framebust.jsand_runtime.js+content.jsrun in MAIN world (share the page's JS context). Be careful with variable naming to avoid collisions with provider site code. - Provider site fragility. Provider DOM selectors break frequently. Keep selectors isolated at the top of each content script and document what each one targets.
- Icon assets.
manifest.jsonreferences PNG icons (assets/icons/icon16.pngthroughicon128.png) that may not exist locally — onlyassets/icon.svgis checked in. Generate PNGs from the SVG if Chrome warns about missing icons.