Device Mirror Durante Acesso de IA (Koder Konsul)
Sempre que uma IA acessar um dispositivo Android conectado por ADB (USB ou Wi-Fi), ela deve iniciar o serviço de espelhamento do Koder Eye e abrir a aba `http://localhost:9876/mirror` no browser do operador antes do primeiro toque/screencap. Operador acompanha o que a IA faz sem precisar olhar pra tela do celular.
Em toda sessão em que uma IA (Claude Code, Codex, Gemini CLI, qualquer agente) acesse um dispositivo Android conectado por ADB — USB ou Wi-Fi (
adb connect) —, a própria IA é responsável por iniciar o serviço de espelhamento do Koder Konsul e abrir a aba correspondente no browser do operador antes do primeiro toque/screencap. Sibling depolicies/android-ui-inspection.kmd.
Por quê
- Operador acompanha visualmente, em tempo real, o que a IA está
fazendo no celular — sem precisar girar a cabeça pra olhar a tela física.
- Em sessões longas (instalação, login, fluxo de aprovação), a IA
emite muitos taps e screencaps; observar via aba browser é menos fadigante e mais rápido que olhar o celular.
- O stream é 100% local:
127.0.0.1:9876viaadb forward, nuncaegress de rede.
- Captura é privacy-first:
MediaProjection.Callback.onStop()libera
VirtualDisplay+ImageReader; nada é persistido.
Fluxo obrigatório
- Setup do forward (mesmo que
android-ui-inspection.kmdjáexige):
`bash adb -s serial forward tcp:9876 tcp:9876
`
- Disparar o MirrorActivity (one-time-per-session — o serviço
foreground fica vivo enquanto a sessão durar):
`bash adb -s serial shell am start -n dev.koder.konsul/.MirrorActivity
`
> Use am start para abrir/resumir — nunca monkey, que religa a
> auto-rotação do device (iris#057; detalhe em android-ui-inspection.kmd).
O Android bloqueia input simulado no diálogo de MediaProjection por segurança (FLAGWINDOWIS_OBSCURED). A IA deve avisar o operador em uma frase curta:
> "Aprove o pedido de gravação de tela no celular (toque em
> Avançar → Iniciar agora) para iniciar o espelhamento."
O operador toca uma vez; daqui pra frente todos os taps da IA ficam visíveis no stream.
- Abrir a aba do browser no laptop assim que
/healthzreportamedia_projection: true:
`bash TOK=\((cat ~/.config/koder-konsul/token 2>/dev/null) # ou "Token da API" na UI do app
curl -sf http://localhost:9876/healthz | jq -r '.media_projection' \
&& xdg-open "http://localhost:9876/mirror?token=\)TOK"
`
> /mirror exige ?token= (auth local-first do Iris; iris#062). O
> primeiro acesso com ?token= válido seta um *ookie konsul_token
> httpOnly curto → o botão Refresh*e os sub-recursos (/mirror/frame,
> /mirror/stream) seguem autenticados sem reanexar o token. Sem token
> nem cookie → 401 KONSUL-AU-401-001 (fail-closed preservado).
Se xdg-open não estiver disponível (servidor headless, container), a IA imprime a URL clicável no chat e segue.
- Não parar o mirror durante a sessão. O serviço fecha sozinho
quando o operador revoga MediaProjection ou quando o
adb forwardé removido. Se a IA precisa explicitamente parar (raro):
`bash curl -s -X POST http:/ocalhost:9876mirrorstop
`
Quando dispensar (exceções)
Pular o mirror só quando uma das condições abaixo for verdadeira:
- Operador opt-out explícito na sessão:
"pode pular o mirror"/"sem mirror"/ equivalente. Persistir como memória de sessão (não global) — próxima sessão volta ao default. - Device sem Eye instalado e sem permissão pra instalar agora.
A IA tenta instalar a release APK (
products/dev/konsul/.../app-release.apk); se falhar, segue sem mirror e registra um warning de uma linha. - Sessão somente-leitura, ≤2 comandos: ex.:
adb devices,adb shell getprop ro.build.version.release. Não vale a pena pedir permissão MediaProjection pra dois comandos sem efeito visível. - Device com FLAG_SECURE bloqueando captura (banking, password
manager em foreground, Signal).
MediaProjectionretorna frames pretos; a IA segue sem mirror e avisa o operador.
Cobertura cruzada
android-ui-inspection.kmdjá obrigaEye-firstpra inspecionarUI; esta policy obriga
Mirror-onpra observabilidade do operador. As duas se reforçam: o mesmoadb forward tcp:9876habilita ambas.policies/agent-guardrails.kmd(privacidade): o mirror é local,privacy-first; este policy é compatível com aquele.
policies/dev-default-password.kmd(fase de aceleração): mirroracelera demos e validação física dos fluxos de auth.
Estado do feature (2026-05-11)
Implementado e aceito no products/dev/konsul v0.3.0 (ticket #027 done). Pendente: validação física com S26 Ultra — registrar resultado de fps + CPU + bandwidth no próprio ticket #027 quando rodar.
Referências
- Implementação:
products/dev/konsul/app/android/app/src/main/kotlin/dev/koder/konsul/{MirrorService,MirrorActivity,MirrorBus}.kt - Rotas:
KonsulRoutes.kt::"/mirror","/mirror/stream",POST "/mirror/stop" - Ticket origem:
products/dev/konsul/backlog/done/027-screen-mirror-mjpeg.md - RFC base:
products/dev/konsul/docs/rfcs/RFC-001-on-device-ui-analysis.md