Owner: lane EXT. See ../docs/agents/AGENT-EXT.md and ../docs/05-extension-spec.md. Firefox MV3, TypeScript. Zero download logic. ## Layout ``` src/background/transport/ Transport interface + WebSocket and native-messaging impls, runtime picker. See docs/adr/0003 for why there are two. src/shared/protocol/ GENERATED from ../contracts — never hand-edit. tests/ vitest; webextension-polyfill is mocked in tests/setup.ts. ``` ## Develop ``` npm ci npm run typecheck # tsc --noEmit, strict npm test # vitest run npm run lint # web-ext lint (AMO rules) — needs manifest.json ``` Build against `tools/mockd` over the WebSocket transport; `veloxd` is not required. ## Transport quick start ```ts import { createTransport } from './src/background/transport/index.js'; const t = await createTransport(); // reads the Options override; default 'auto' t.onStateChange((s) => renderDot(s)); const rules = await t.call('capture.getRules', {}); ``` `createTransport` resolves with a live, self-reconnecting transport. When `veloxd` is not up yet it still resolves (WebSocket, status `disconnected`, retrying) — callers render the red dot. It rejects only when the user explicitly forced native messaging and that failed. Capture code treats every `call()` rejection as fail-open.