Kruze Companion — a camada Koder do Kruze como extensão multi-browser
Status
accepted — ratificada pelo owner em 2026-05-30. Efetivado na ratificação: surface extension + targets chromiumgeckosafari emendados em specs/variants/taxonomy.kmd §3.1/§3.2/§4/§5; variante registrada em registries/component-names.md + target-readiness.md; surface products/horizontal/kruze/extension/ scaffoldada com backlog das ondas (incl. contratos OpenAPI §8.1). As recomendações de §9 (D-open-123) ficam como diretriz de implementação.
1. Motivação
O Koder Kruze é o browser próprio da Stack (CEF/Flutter). Além do motor de browser, ele carrega uma camada Koder que o diferencia de um Chromium qualquer (README ## Koder Integrations):
- Koder ID — login na conta Koder Stack.
- Kode — painel do assistente, com MCP (
KMCP-001/002: o Kode operao browser e usa ferramentas).
- Koder Observability — telemetria, error-reporter, breadcrumbs
(
065/094/100/112/117) e o Debug Mode v2 (164.x: captura de networkwebsocketstoragesourcemaptimeline/visual-diff). - Koder Pass — autofill, passkeys, vault (
187/188/218). - Koder Capture — screenshotgravação (`153189`).
A maioria dos usuários, porém, não vai trocar de browser. A tese desta RFC: levar a camada Koder do Kruze para Chrome, Edge, Brave, Firefox e Safari via uma extensão — chamada Kruze Companion — para que qualquer browser ganhe Koder ID + Kode + Observability + Pass + Capture, dentro do que as APIs de extensão permitem.
2. Princípio orientador: extensão ≠ motor de browser
O Kruze tem duas metades. A primeira — abas, address bar, downloads, histórico, privacidade, reader mode, OSR/CEF, hibernação (a maioria dos ~220 tickets do backlog) — não deve ser portada: o browser host *já é* o motor. Reimplementar abas numa extensão é trabalho jogado fora.
A segunda metade — as Koder Integrations — é exatamente o que o usuário de Chrome não tem. O Kruze Companion é essa segunda metade, e só ela, injetada em qualquer browser. Esse recorte é o coração da proposta: a extensão entrega valor Koder, não um clone de browser.
3. Decisão D1 — nova surface canônica extension
specs/variants/taxonomy.kmd §3.1 fecha as surfaces em mobile, desktop,
tv, web, cli, tui, backend, engine. Uma extensão de browser não cabe em web:
web(targetbrowser) é uma página que roda dentro de uma aba —PWA/SaaS. Seu ambiente é o documento.
- Uma extensão roda na chrome do browser host: service worker em
background, content scripts injetados em páginas de terceiros, side panel/sidebar, APIs privilegiadas (
tabs,scripting,debugger,declarativeNetRequest). Seu ambiente é o browser, não a página.
São modelos de execução, empacotamento (manifest MV3 vs build web) e distribuição (Chrome Web Store / AMO / App Store vs deploy de site) distintos. §4 da taxonomia exige RFC explícita para adicionar surface (análogo a RFC-006 §3.4) — esta é essa RFC.
Proposta de emenda (efetivar em taxonomy.kmd + monorepo-RFC-006 na ratificação):
| Eixo | Adição |
|---|---|
| Surface | extension — L4 origin extension/; "WebExtension MV3 injetada na chrome de um browser host" |
| Target | chromium (active), gecko (active), safari (inactive — no Mac hardware, mesmo motivo de ios/macos) |
| Combinação válida | extension × {chromium, gecko, safari} · form factor: laptop, desktop |
| Naming | kruze-extension-chromium, kruze-extension-gecko, kruze-extension-safari; multi-engine no mesmo código → kruze-extension |
Nota: o eixo "target" para a surface
extensionmapeia ao runtime de extensão / loja (chromium, gecko, safari), não ao SO — análogo a comoengineusa o pseudo-targetuniversal. Cada engine tem manifest, set de APIs, pipeline de release e loja distintos, logo merece variante própria para fins de paridade e release.
4. Decisão D2 — surface do sector kruze, não produto novo
O Kruze Companion é modelado como a surface extension do sector products/horizontal/kruze — não como um produto irmão.
Consequência decisiva (e a resposta à pergunta de processo que motivou esta RFC): a paridade Kruze ↔ Companion vira a matriz nativa de variantes do Kruze. O /k-parity kruze já constrói feature × variante a partir das variantes canônicas (precedente real: ticket 148 do Kruze, spawned por /k-parity kruze, auditou desktop × mobile). Com extension sendo variante do mesmo sector, a coluna da extensão entra sozinha na matriz — sem ninguém precisar lembrar de conferir. Se fosse produto separado, a paridade seria cross-component (mais frágil).
"Kruze Companion" é o display name da variante; o nome canônico de variante segue §5 da taxonomia (kruze-extension-*). Registro formal em registries/component-names.md + conformidade com specs/naming/forms.kmd na ratificação (gatilho "referenciar/nomear componente").
5. Decisão D3 — fronteira de reuso
Diretriz do owner (2026-05-30): *reaproveitar código sempre que possível, desde que isso não limite funcionalidades em nenhum dos dois lados.* É o espírito de policies/reuse-first.kmd aplicado — não regra nova.
Restrição dura: Kruze é FlutterDart; a extensão é JSTS (service worker e content scripts rodam no motor do browser host — não há Flutter ali). Logo o reuso não é de UI. A fronteira:
| Reusável de fato | Não reusável (diverge legitimamente) |
|---|---|
Contratos — endpoints/schema do gateway do Kode, formato dos eventos de RUM que o apm espera, fluxo OAuth do Koder ID, protocolo MCP |
UI (Dart ↔ JS) |
| Clients tipados desses contratos | Lógica de motor de browser (a extensão não precisa — o host já é o motor) |
A chromium-extension/ do Koder Pass — já é JS/TS de WebExtension; reusável como código, não só como contrato |
Recursos de plataforma onde a extensão é mais restrita que o Kruze (ver §6) |
Princípio: compartilha-se só o subconjunto comum. Nunca um denominador-comum de UI que force a extensão a não fazer X porque o Kruze não faz, nem vice-versa. Isso satisfaz a diretriz "não limitar nenhum dos dois".
Corolário (paridade de contrato como erro de build): se os contratos vivem num pacote compartilhado e tipado, quando a Observability ou o Kode adicionam um campo/endpoint, o build da extensão acusa "contrato novo não consumido". Paridade de contrato deixa de ser checklist e vira erro de compilação — cobrindo o caso "a mudança veio de outro componente, não do Kruze" que o /k-parity por-módulo sozinho não pega.
6. Matriz de viabilidade (WebExtension MV3)
🟢 viável · 🟡 parcial/com ressalva · 🔴 inviável sem app-wrapper
| Camada Koder | Chromium | Gecko (FF) | Safari | Mecanismo |
|---|---|---|---|---|
| Koder ID login | 🟢 | 🟢 | 🟢 | identity/popup OAuth (oauth-flow.kmd) |
| Kode chat (sidebar + contexto da aba) | 🟢 | 🟢 | 🟡 | sidePanel/sidebar_action + content script |
| Kode opera a página (MCP browser) | 🟡 | 🟡 | 🔴 | tabs+scripting: navegarlerclicar; sem controle do chrome do host |
Observability — RUM (erros/perf → apm) |
🟢 | 🟢 | 🟢 | content script: window.onerror, PerformanceObserver |
| Observability — Debug Mode v2 (networkWSstorage/sourcemap) | 🟢 | 🟡 | 🔴 | chrome.debugger (CDP); FF parcial; Safari sem CDP |
| Koder Pass (autofillpasskeysvault) | 🟢 | 🟢 | 🟡 | base já existe em services/foundation/pass/app/chromium-extension/ |
| Koder Capture (screenshot/record) | 🟡 | 🟡 | 🔴 | captureVisibleTab ok; full-page/screen-share limitado |
Dois fatos pautam o roadmap:
chrome.debugger(CDP) é o trunfo que porta boa parte do DebugMode v2 — Chromium-first; Gecko parcial; Safari não tem.
- Safari é caro: WebExtension convertida + app-wrapper Xcode + conta
Apple Developer (já temos, ver
infrastructure/accounts.md), e hoje sem Mac hardware (target safari = inactive). Fase final, não v1.
7. Mecanismo de paridade (não depende de memória)
Três peças, todas reusando maquinário existente:
extensioncomo variante canônica (D1) → entra sozinha na matrizdo
/k-parity kruze./k-parity+/k-manifestrodam no/k-housekeep(diário) epré-release; cada gap vira ticket (decisão de design por feature, como o ticket
148).- Registry
registries/kruze-companion-parity.mdnos moldes dekoder-id-auth-coverage.md(que bloqueia release se a linha não estiver verde). Matrizfeature × {kruze-desktop, kruze-mobile, ext-chromium, ext-gecko, ext-safari}com 4 estados: 🟢 tem · ⚪ falta · 🟡 parcial · 🔴 impossível-na-plataforma. O 🔴 é decisão registrada — sem ele, o audit gritaria pra sempre sobre features que uma extensão nunca terá (ex.: hibernação de aba).
Mais o corolário de D3: paridade de contrato é erro de build.
8. Escopo do v1 e faseamento
Decisão do owner (2026-05-30): v1 = suite completa (as 5 camadas). Por stack-principles.kmd §2 (Quality > Speed) entregamos a suite completa em um único produto, faseada por viabilidade — não um MVP com limitações a remendar depois:
- Onda 1 (núcleo de presença): Koder ID login → Kode na sidebar +
RUM de observability. Tudo 🟢, prova as três capacidades-âncora.
- Onda 2 (Pass + Observability profunda): Pass autofill (reusa a
chromium-extension/do Pass) + Debug Mode v2 viachrome.debugger(Chromium-first). - Onda 3 (Kode agente + Capture): MCP browser + captura, no teto do
que cada engine permite.
- Onda 4 (Safari): quando houver Mac hardware (flip de
target safari → activeemregistries/target-readiness.md).
Cada onda abre tickets no backlog da surface extension do Kruze; a matriz de paridade (§7) governa o que entra e o que é 🔴-por-plataforma.
8.1 — Pré-requisito de alta alavancagem: contratos OpenAPI-first
Antes de qualquer código da extensão, formalizar os três contratos- âncora como OpenAPI (fonte de verdade no dono do serviço):
- Gateway do Kode (
services/ai/ai/gateway) — chatstreamMCP. - RUMcrash do
apm(`infraobserve/apm`) — schema de eventos. - OAuth do Koder ID (
services/foundation/id) — já parcialmente emspecs/auth/oauth-flow.kmd.
É o item de maior alavancagem: destrava o reuso cross-language (D3), torna a paridade de contrato um erro de build dos dois lados, e beneficia todos os consumidores Koder, não só a extensão. Sem ele, a extensão nasce como mais um client artesanal que diverge. Ordem global: contratos OpenAPI → Onda 1 → registry de paridade → ondas 2–4.
9. Decisões abertas → recomendações (2026-05-30)
Recomendações fundamentadas; ainda aguardam ratificação do owner.
- *-open-1 (consolidação do Pass) → RECOMENDADO: um codebase, dois
produtos de loja. O Companion é o superset; o Koder Pass standalone vira um build "Pass-only" do mesmo codebase (feature flag), não uma extensão separada. Duas SKUs nas lojas, zero duplicação — o "reusar sem limitar nenhum dos dois" de D3. Cuidado dura:*detecção mútua — com os dois instalados, um desativa seu módulo de autofill (senão dois autofills brigam no mesmo campo).
- *-open-2 (onde mora o contrato) → RECOMENDADO: nem
engines/sdk/novo, nem dentro do
kruze/— o contrato vive com o dono do serviço. A formulação original (escolher entre pacote novo vs dentro do kruze) estava errada. O padrão cross-language vem dodesign-RFC-006(koder_kitDart +koder_web_kitJS consomem uma fonte de tokens). Aplicar o mesmo trilho a contratos de API: OpenAPI no dono (gatewayapmid); o client Dart vai emkoder_kit(→ Kruze); o client TS vai em@koder/sdk(engines/sdk/js) — o SDK JS real da Stack — não*emkoder_web_kit(que é o kit de UI/CSS).- Correção de reuso (2026-05-30, walk-codebase):
@koder/sdk*ácontém boa parte dos clients que a extensão precisa:
KoderClient+Span(transporte + tracing),agent-stream(decoder do Koder Agent Stream Protocol, specspecs/ai/agent-stream.kmd),kode-conversation(@koder/kode-conversation— Web Component de transcript do Kode) einstrumentation/(auto-instrumentação de telemetria). A extensão consome*@koder/sdk+@koder/kode-conversation; não gera client do zero. Pré-requisito: §8.1 (os 3 serviços não expõem OpenAPI hoje).
- Correção de reuso (2026-05-30, walk-codebase):
- *-open-3 (loja e identidade visual) → RECOMENDADO: menor privilégio +
permissões on-demand; AMO primeiro.*
- Ícones via
kicon generate(specs/icons/products.kmd+generation-targets.kmd, tamanhos 1648128) — nunca PNG à mão. - Nome: "Kruze Companion" = display; bare/slug em
registries/component-names.mdporspecs/naming/forms.kmd. - Permissões MV3: núcleo mínimo (
storage,activeTab,identity);<all_urls>echrome.debuggersó viaoptional_permissions, pedidas quando o usuário ativa RUM amplo / Debug Mode (melhor aprovação + privacidade). - Lojas: temos Apple Developer (Safari) e Google Play, mas *hrome Web
Store e AMO são contas de publisher separadas que ainda não temos*(
infrastructure/accounts.mdsó lista Google Play). Começar pela AMO (Firefox; grátis, review mais leve); provisionar Chrome Web Store (US$5 one-time) em paralelo; registrar ambas emaccounts.md.
- Ícones via
10. Impacto na Stack — efetivado na ratificação (2026-05-30)
- ✅
specs/variants/taxonomy.kmdemendado (§3.1 surfaceextension,§3.2 targets chromiumgeckosafari, §4 combinação, §5 naming) + referência em
monorepo-RFC-006. - ✅
registries/target-readiness.md: chromium/geckoactive, safariinactive(no Mac hardware).component-names.mdnão recebe linha nova — "Kruze Companion" é a surfaceextensiondo componentekruze(variante, não componente); namingkruze-extension-*já coberto pela taxonomia §5. "Kruze Companion" é só o display name de loja da variante (análogo apass-desktopnão ter linha própria). - ✅
registries/kruze-companion-parity.md(matriz 4-estados) criado. - ✅ Surface
products/horizontal/kruze/extension/scaffoldada + backlogdas ondas (incl. contratos OpenAPI §8.1).
- Pacote de contratos: sem pacote próprio (D-open-2 corrigida) —
OpenAPI no dono do serviço; client TS via
@koder/sdk(engines/sdk/js) (já tem agent-stream + kode-conversation + instrumentation), client Dart viakoder_kit.koder_web_kit= UI/CSS, não clients de API.