La pregunta que no está en el README
Cuando le das a un agente de inteligencia artificial la capacidad de ejecutar comandos en una terminal real, la primera pregunta técnica que surge no es cómo conectarlo ni cómo escalar. La pregunta real es esta: ¿quién decide qué comandos puede correr, y bajo qué condiciones?
Es una pregunta filosófica antes de ser una pregunta de ingeniería. Toca el problema clásico de la agencia delegada: cuando un sistema actúa en nombre de alguien, ¿cuánta autonomía le das, y dónde pones el freno?
Agora Worker nació de esa pregunta. Es un runtime de workers en Docker —uno por workspace— que le abre una terminal PTY a un agente de IA y le deja correr comandos reales en un servidor de producción. En el momento en que escribo esto, hay ~43 contenedores activos en el servidor ils-server, cada uno representando un workspace distinto, todos conectados al hub central en hub.elenxos.com.
Lo interesante no es la escala. Lo interesante es el modelo de seguridad que tuvieron que inventar para hacer eso posible sin que el agente destruyera algo.
La política tri-tier: una taxonomía del peligro
El corazón del proyecto es una función llamada runWorkerCommand, que implementa lo que el README llama una policy tri-tier. La idea es simple pero tiene una profundidad conceptual real:
No todos los comandos son iguales. Hay comandos que leen sin tocar —ls, cat, pwd, head— y hay comandos que modifican el estado del mundo —rm, mv, truncate. En el medio hay un territorio gris: comandos que no son destructivos por definición pero tampoco son inocuos por contexto.
La policy los separa en tres niveles:
- Safe-reads: se ejecutan directamente, sin pedir permiso. El agente puede explorar, leer, navegar.
- Destructivos: siempre piden confirmación explícita del usuario, sin excepción.
- El resto: requieren confirm también.
Esta taxonomía es una toma de posición sobre la autonomía. El agente tiene libertad total para observar, libertad condicionada para actuar, y cero libertad unilateral para destruir. Es, en términos filosóficos, una arquitectura de autonomía graduada.
Lo que me parece elegante es que la whitelist no es ad hoc: son ~40 binarios explícitamente listados. No hay permiso implícito. Si no está en la lista, no corre. El diseño por omisión es la restricción, no la permisividad.
PTY: la ilusión de una terminal real
Hay una decisión técnica que vale la pena detenerse a pensar: el worker no ejecuta comandos como subprocesos simples. Abre una PTY —una pseudo-terminal— para cada comando.
¿Por qué importa eso?
Una PTY simula un terminal interactivo. Muchos programas de línea de comandos cambian su comportamiento según si están corriendo en una terminal real o siendo redirigidos a un pipe. Con PTY, el agente recibe exactamente lo que vería un humano sentado frente a la pantalla: colores ANSI, prompts interactivos, el output completo tal como fluye.
Eso tiene consecuencias de diseño. Significa que el agente no recibe datos serializados ni una API limpia: recibe texto crudo, tal como la terminal lo produce. Es una apuesta por la generalidad sobre la estructura. En vez de modelar cada herramienta con su propio adaptador, el sistema dice: el lenguaje universal es la terminal, y el agente que sabe leer texto sabe usar cualquier herramienta.
Hay algo filosóficamente honesto en eso. La terminal es la abstracción más duradera de la computación. Tiene cuarenta años y sigue siendo la interfaz donde viven los sistemas más serios.
El daemon que mantiene vivo el mundo
La otra pieza central es worker-host-sync, un daemon que corre en el host y hace dos cosas: sincroniza el directorio /workspace contra el backend (AgoraBack, MinIO, Firestore) cada cinco segundos, y revive contenedores que hayan caído.
Esto no es trivial. Estamos hablando de un sistema distribuido donde la unidad de trabajo —el container— puede morir en cualquier momento. El daemon no espera a que alguien note el fallo: actúa solo, relanza el container, y el worker se reconecta al hub en menos de cinco segundos.
El diseño acepta una realidad que muchos sistemas evitan nombrar: los procesos mueren. No como caso de error, sino como condición normal de operación. La resiliencia no es el plan B; es el plan A.
Al mismo tiempo, el daemon tiene una lista de patrones que ignora al sincronizar: .scratch/, .agent-tmp/, tmp-*, *.tmp. Es un detalle pequeño con implicaciones grandes. Significa que el sistema tiene un modelo explícito de lo que es estado real versus basura transitoria. No todo lo que existe en disco merece vivir en el backend.
Esa distinción —entre estado persistente y artefactos efímeros— es una de las decisiones de diseño más importantes que se toman en sistemas distribuidos y casi nunca se documenta bien. Aquí está explícita.
Lo que este sistema enseña sobre agentes
Hay una tendencia en la industria a pensar los agentes de IA como entidades que necesitan más autonomía para ser útiles. Darle más herramientas, más permisos, más acceso. Agora Worker propone lo contrario: la utilidad viene de la confianza, y la confianza viene de saber exactamente qué puede y qué no puede hacer el agente.
Cuando el agente sabe que sus comandos de lectura corren sin fricción, trabaja rápido en exploración. Cuando sabe que una operación destructiva va a pedir confirmación, el sistema no se paraliza: simplemente escala la decisión al humano que corresponde.
Eso es diseño de sistemas con criterio. No prohibición ciega ni confianza ciega. Un contrato claro entre lo autónomo y lo supervisado.
El hecho de que el cwd del agente quede fijo en /workspace —y que el handler agent-command ya no prependa un cd automático— es otro ejemplo de esto. Una decisión aparentemente pequeña que elimina toda una clase de bugs de contexto. El agente siempre sabe dónde está. No hay estado implícito.
La terminal como metáfora
Termino con la imagen que me dejó pensar en este proyecto: cuarenta y tres terminales abiertas al mismo tiempo, cada una dentro de su propio container, cada una respondiendo a un agente distinto, todas conectadas al mismo hub.
Es una imagen de computación distribuida, sí. Pero también es una imagen de algo más antiguo: la idea de que un sistema inteligente necesita un entorno delimitado para operar con seguridad. No un entorno sin restricciones, sino un entorno donde las restricciones están diseñadas con cuidado.
La jaula no es la antítesis de la inteligencia. A veces es la condición que la hace posible.