Sala de control para agentes de IA
Agents Control Center
Una sola bandeja y un plano de control para muchas sesiones de Claude Code, en varias cuentas y en las máquinas que quieras, también desde un teléfono.
- Rol
- Único autor
- Período
- oct. 2026
- Estado
- En uso diario
- Código
- Privado, hecho en Enara Health
- commits en 11 días
- 149
- versiones, de 0.1 a 0.76
- 108
- pruebas
- 565
- dependencias de ejecución
- 10
- TypeScript
- Bun
- React
- SQLite
- tmux
- Noise
- WebAuthn
- Web Push
- MCP
- whisper.cpp
01 Contexto
Por qué existe
Uso varias suscripciones de Claude en paralelo, en más de una máquina. Cada sesión pide permisos y espera respuestas en su propia pestaña de terminal, así que las solicitudes se acumulan donde nadie está mirando. Los límites del plan solo se ven en la línea de estado de cada sesión, y una sesión que llega a uno simplemente se queda detenida.
Hay dos costos más que es fácil pasar por alto. Una conversación que se retoma después de que vence su caché de prompts cuesta cerca del doble de su tamaño en reescribirse. Y el control remoto integrado muestra una conversación a la vez, no las preguntas que esperan en todas ellas. Quería un solo lugar para ver cada sesión, responder lo que pregunta y mantener el trabajo andando cuando se agota una cuenta o una máquina, sin escribir cada respuesta a mano.
02 En uso
Qué hace
Todo lo de aquí funciona entre sesiones, cuentas y máquinas, algo que una sola sesión de Claude Code no puede ver. Claude Code es el agente que maneja hoy; el servidor llega a los agentes a través de adaptadores, así que se pueden sumar otros sin tocar la interfaz.
-
Una bandeja para todas las sesiones
Las solicitudes de permiso y las preguntas de cada sesión, cuenta y máquina llegan a una sola lista, con el comando completo y una evaluación de riesgo. Responde con una tecla numérica, con una respuesta rápida o desde el teléfono. Los force push, los merges y las migraciones piden una segunda confirmación.
-
Las máquinas que quieras
Una máquina principal y tantos nodos como vincules. Cada uno se suma pegando un comando, se reconecta solo después de una suspensión o un corte de red, y muestra su CPU y su memoria. Las sesiones nuevas van a la máquina que tiene capacidad.
-
Sesiones que se mueven, con su trabajo
Una sesión sigue en otra cuenta cuando se le acaba el plan, o en otra máquina cuando falta memoria. Su trabajo sin commit viaja con ella, en privado, y para moverla no se hace ningún commit ni push.
-
Recuperación y detenciones automáticas
Un guardián de memoria opcional pausa la sesión inactiva más pesada cuando queda poca memoria. Las sesiones que llevan mucho rato inactivas se cierran después de escribir una nota de traspaso y se reanudan desde ella por unos pocos miles de tokens. Las sesiones cerradas se reabren con la misma conversación, y las desactualizadas se reinician en la versión más nueva de Claude Code sin perderla.
-
Secretos que nunca entran a una conversación
El pegado seguro guarda las credenciales cifradas y solo las escribe en archivos env que git ignora; la sesión se entera de los nombres de las claves, nunca de los valores. Un agente que necesita un archivo como
.envde otra máquina lo pide, y la persona aprueba una lista de nombres y tamaños. -
Ida y vuelta por voz
Dicta con whisper.cpp en tu propia máquina, alternando entre español e inglés sobre la marcha. Kokoro lee en voz alta las respuestas del agente a medida que llegan, y los comandos de voz pausan, repiten o saltan; decir «responde» graba una respuesta, la envía cuando dejas de hablar y lee la respuesta siguiente.
-
Agentes que trabajan juntos
Un servidor MCP en cada sesión permite que los agentes se encuentren, se manden mensajes, le pidan a la persona que inicie una sesión, pidan archivos y soliciten una ejecución de QA en un navegador real. Las sesiones que inició otra aparecen debajo de ella, aunque estén en otra máquina.
-
El plan y la caché de prompts, a la vista
El uso del plan de cada cuenta y, para cada sesión inactiva, su tamaño y cuánto tiempo sigue caliente su caché de prompts, porque volver después de que vence cuesta cerca del doble de la conversación.
-
Un mapa en vivo de lo que corre
La vista Activity dibuja el árbol de procesos de una sesión, con los servidores que dejó escuchando y un botón para detener comandos desbocados. La vista de red muestra todas las máquinas, con cada mensaje entre agentes volando por los enlaces.
-
Cuentas y terminales desde cualquier dispositivo
Agrega una cuenta de Claude y autentícala desde el teléfono, y abre en el navegador la terminal en vivo de una sesión cuando espera una tecla que la app no puede enviar.
03 Arquitectura
Cómo funciona
Un servidor Bun local encuentra cada sesión interactiva de Claude Code en la máquina, lee lo que está haciendo y la maneja a través de su propia terminal. Una app React, instalable en un teléfono, las muestra todas con una sola bandeja para las preguntas. Cualquier número de nodos corre el mismo programa sin interfaz y se conecta a la máquina principal por un enlace cifrado; la máquina principal da las órdenes y retransmite todo entre ellos.
-
Adaptadores para agentes y terminales
El servidor nunca llama directamente a Claude Code. Un adaptador de agente solo lee el estado propio del agente: sus cuentas, sesiones, transcripciones, subagentes, decisiones pasadas, flags de arranque y cómo retomar una conversación. Pausar, cerrar y entregar mensajes no dependen del agente, y un transporte de terminal envía teclas, lee la pantalla y abre pestañas. Claude Code, tmux y cmux están implementados; Codex CLI, Gemini CLI, opencode o Aider serían, cada uno, un adaptador más.
-
Encontrar y vigilar sesiones sin ser su dueño
Cada dos segundos el servidor lee el archivo que Claude Code escribe por cada proceso en ejecución y el final de cada transcripción, y mide CPU y memoria sobre todo el árbol de procesos de cada agente. Las sesiones viven en tmux, no como procesos hijos del servidor, así que lo sobreviven.
-
Una bandeja, respondida con seguridad
Las solicitudes de permiso y las preguntas se leen de la pantalla de la terminal y se revisan contra quince reglas de riesgo. Para responder, el servidor vuelve a leer la pantalla y solo escribe si la solicitud sigue coincidiendo con la que viste. Las aprobaciones de alto riesgo, como los force push, los merges y las migraciones, piden una segunda confirmación.
-
Las máquinas que quieras, sobre Noise
Cada nodo se conecta a la máquina principal con el handshake Noise IK, después de una primera vinculación con un token de un solo uso, así que no hay un número fijo de máquinas: cada una es otro par vinculado. La máquina principal solo puede invocar en él una lista cerrada de llamadas, sin ninguna llamada genérica de shell, archivos o exec. Los eventos van numerados y con acuse de recibo, y una llamada repetida nunca se ejecuta dos veces.
-
Un servidor MCP en cada sesión
La app instala un servidor MCP
agentsen cada cuenta de cada máquina. Los agentes lo usan para ver dónde corren y cuánto plan les queda, encontrarse, mandarse mensajes, iniciar o mover sesiones, pedir archivos y solicitar una ejecución de QA. Los mensajes llegan marcados como que no vienen del usuario. -
Sesiones que se mueven
Cuando una sesión llega al límite de su plan, la app la mueve por sí sola, prefiriendo el mismo grupo de cuentas, luego la misma máquina y después el margen de plan y la memoria libre. Entre máquinas, el trabajo sin commit viaja como un git bundle, parches binarios y un paquete de archivos, y el destino comprueba que
git statuscoincida. -
Secretos fuera de las conversaciones
El pegado seguro mantiene las credenciales cifradas en reposo y solo las escribe en archivos env que git ignora, así que un secreto nunca llega a un commit. Los agentes usan los valores desde la shell sin imprimirlos, y cada guardado, escritura, lectura y borrado queda auditado sin el valor.
-
Un teléfono que es un control remoto de verdad
Los teléfonos se vinculan con un código QR y llegan a la app por Tailscale. La app se bloquea con una passkey y las notificaciones push van cifradas de extremo a extremo. La voz se queda en tus propias máquinas: whisper.cpp transcribe, Kokoro lee en voz alta y Vosk escucha los comandos de voz.
04 Decisiones
Las decisiones que le dieron forma
-
Decisión 01
Adaptadores, para que Claude Code sea el primer agente y no el único
- Elegí
- Un adaptador de agente que solo lee el estado propio del agente, y un transporte de terminal que envía teclas, lee la pantalla y abre pestañas.
- En vez de
- Llamar a Claude Code y a tmux directamente desde el servidor.
- Porque
- Otro agente u otra terminal es un módulo nuevo detrás de la misma bandeja, el mismo teléfono y el mismo enlace entre máquinas.
- Costo
- Con un solo agente implementado, la interfaz está moldeada por Claude Code, y los traslados y el cierre por inactividad todavía leen directamente los archivos de Claude Code.
-
Decisión 02
Manejar la terminal real en vez de envolver al agente
- Elegí
- Leer los archivos de sesión y las transcripciones del propio Claude Code, interpretar la pantalla de su terminal y escribir en tmux.
- En vez de
- Iniciar las sesiones con un SDK, lo que las escondería de la terminal y perdería las funciones interactivas.
- Porque
- También aparecen las sesiones abiertas a mano, las funciones interactivas se mantienen y las sesiones sobreviven a los reinicios de la app.
- Costo
- Nada de eso es una interfaz pública, así que una actualización de Claude Code puede romper una lectura. Varias versiones arreglaron justamente eso, y los parsers se prueban contra pantallas y transcripciones reales.
-
Decisión 03
Escribir un canal Noise, con el nodo iniciando la conexión
- Elegí
- Un enlace Noise IK implementado en el repositorio sobre primitivas auditadas, probado contra los vectores de prueba publicados.
- En vez de
- SSH, que necesita un servidor y redirección de puertos dentro de WSL, y una VPN como enlace con los nodos, que autentica máquinas y no esta app.
- Porque
- Autenticación mutua en un solo intercambio, funciona detrás de NAT y sumar una máquina es pegar un comando.
- Costo
- Un protocolo a mi cargo, que hay que mantener seguro, y una red en estrella: todo lo que va entre nodos pasa por la máquina principal.
-
Decisión 04
Mover el trabajo sin commit ni push
- Elegí
- Empaquetar el trabajo sin commit como un bundle, parches y un paquete de archivos, enviarlo por el enlace y verificarlo al llegar.
- En vez de
- La primera versión, en la que los agentes hacían commit y push a una rama de trabajo en curso antes de moverse.
- Porque
- El trabajo nunca debe llegar a un remoto solo porque una sesión se movió; puede contener archivos que nunca deberían salir de la máquina.
- Costo
- Una larga cola de casos borde: archivos de metadatos de la plataforma, un traslado de 1,2 GB que hizo caer el servidor y el rollback cuando un traslado falla a medio camino.
-
Decisión 05
Acotar lo que los agentes pueden hacer solos
- Elegí
- Un agente puede moverse sin preguntar solo cuando se le agotó el plan o a su máquina le falta memoria, y solo dentro de su grupo de cuentas. Iniciar sesiones, moverse entre grupos y pedir archivos quedan esperando al usuario.
- En vez de
- La autonomía total o preguntar por todo.
- Porque
- Las emergencias comunes se resuelven de inmediato, y todo lo que gaste el plan de otra persona o se meta en otra máquina sigue siendo una decisión humana.
- Costo
- Más tarjetas de aprobación en la bandeja.
-
Decisión 06
Mantener los secretos fuera de la conversación
- Elegí
- Un almacén local cifrado para las credenciales, un traspaso de archivos entre máquinas que la persona aprueba, y un hook que detiene, una vez, la llamada a una herramienta que está por leer un secreto y le indica la vía segura.
- En vez de
- Pegar claves en el chat, o dejar que los agentes lean archivos
.envcada vez que necesitan un valor. - Porque
- Todo lo que entra a una conversación queda en su transcripción y se vuelve a enviar con cada solicitud posterior.
- Costo
- Un paso más cada vez que un agente necesita una clave, y por ahora los secretos guardados se quedan en la máquina principal.
-
Decisión 07
Llegar al teléfono por una red privada
- Elegí
- Tailscale con su certificado, bloqueo con passkey y Web Push implementado en el repositorio.
- En vez de
- Un túnel público con una capa de acceso adelante, o un servidor de retransmisión que mantener en marcha.
- Porque
- Nada es accesible desde internet y no hay un servidor extra que mantener.
- Costo
- Tailscale en ambos dispositivos, y los teléfonos vinculados en la red local tienen que vincularse de nuevo.
05 Lo interesante
Vale la pena mirarlo de cerca
-
Las aprobaciones son compare-and-swap
Una tecla solo se envía si la solicitud, leída otra vez desde la pantalla, todavía tiene la huella que la persona aprobó. Los mensajes solo se escriben en un cuadro de texto vacío, nunca encima de un borrador.
-
Estado e historial van por separado en un enlace inestable
Los snapshots son estado y se envían completos, así que una reconexión solo reenvía el último. Lo que tiene que pasar una sola vez son eventos numerados que se retienen hasta el acuse de recibo, y las llamadas son idempotentes por id. Las reconexiones usan backoff con jitter.
-
Pensado en torno a la caché de prompts
Muestra la vida útil de la caché de cada sesión, mantiene las cachés calientes con pings mínimos que se detienen al 80% del plan, cierra las sesiones inactivas después de escribir una nota de traspaso y agrega sus propias indicaciones a los resultados de las herramientas en vez del system prompt, para que la caché siga siendo válida.
-
Traslados que sobreviven a una mala red
Los archivos viajan en trozos que retransmite la máquina principal, cuatro a la vez, cada uno verificado con SHA-256 y reenviado si llega dañado. Sobre 1 GB, el origen primero arma un solo paquete, comprimido con zstd cuando ambas máquinas lo tienen.
-
Comandos de voz sin un micrófono abierto
Mientras lee en voz alta, escucha unas pocas palabras de comando. Solo salen del dispositivo ráfagas cortas de voz, hacia la máquina que corre la voz, y se ignora un comando que la propia voz acaba de decir.
-
Pocas dependencias
Diez dependencias de ejecución. Noise, Web Push, la verificación de WebAuthn y el servidor MCP están escritos en el repositorio, cada uno lo bastante pequeño para leerlo.
06 Hecho con agentes
Cómo encajan los agentes
Se construyó en once días, 149 commits de la versión 0.1 a la 0.76, y cada uno de sus 132 commits que no son merge tiene a Claude como coautor. El archivo de reglas fija tres hábitos: todo en inglés, una entrada en el CHANGELOG y subir la versión con cada pull request, y la verificación completa (tipos, pruebas, build web, validación del plugin) antes de cada push. Los merges pasan por un script que corre las mismas verificaciones sobre el resultado en un worktree desechable.
Lo que más cambió es quién lleva los mensajes. Los agentes se preguntan entre ellos directamente en vez de pasar por mí, piden los archivos que necesitan sin que se peguen secretos en una conversación, le piden a QA Console que revise su trabajo de interfaz en un navegador real y se mueven solos fuera de una cuenta cuyo plan se agotó.
Hizo trabajo real mientras se construía, y el historial lo muestra bajo carga: un traslado de sesión de 1,2 GB que hizo caer el servidor, servidores de voz acumulándose en memoria, transcripciones de 90 MB. Cada uno se convirtió en un arreglo y una prueba.