Documentación
Lo necesario para ejecutar una flota en tu propia máquina. Cada comando de abajo está copiado sin cambios del README del proyecto.
Inicio rápido
Requisitos
fleetmux maneja CLI reales y un Redis privado, así que la máquina tiene que proporcionarlos.
| Qué | Mínimo o nota |
|---|---|
| macOS con cmux | cmux ≥ 0.64.25 (new-surface --command). Ejecuta start dentro de un terminal cmux, o cambia el automation → socket control mode de cmux para que no sea cmuxOnly; por defecto cmux solo acepta comandos de procesos que él mismo ha lanzado. |
| Codex CLI (para agentes Codex) | ≥ 0.156 (perfiles por archivo, check_for_update_on_startup). El daemon analiza los archivos rollout .jsonl de Codex (token_count, task_complete, metadatos de subagentes); si una versión de Codex cambia ese formato, fija la versión que te funcione. |
redis-server en el PATH |
brew install redis. Cada ejecución arranca su propia instancia privada en .runtime/redis/<token>/ del checkout (socket, PID, log y datos); TCP está desactivado y nunca toca localhost:6379. El binario sigue siendo una dependencia externa, no viene incluido. |
Python 3.14 + requirements.txt |
3.14 es la versión sobre la que se ejecuta la suite de pruebas; deps.yaml enumera cada programa externo y su versión mínima. |
| Un modelo con el que Codex pueda hablar | La API de OpenAI, un inicio de sesión de ChatGPT o un servidor local compatible con OpenAI. El daemon lee la ventana de contexto del rollout, así que no hay que configurar ningún tamaño de ventana. |
Los agentes se ejecutan en un home de Codex aislado (--codex-home, por defecto ~/.fleetmux/codex-home): auth.json y el archivo de perfil se enlazan desde tu ~/.codex, así que el inicio de sesión se comparte, pero tus hooks, servidores MCP, plugins y skills no se ejecutan dentro de los agentes (skills.include_instructions=false). Las entradas de «confiar en esta carpeta» de cada agente se escriben ahí automáticamente.
Instalación
Un solo comando: fleetmux <subcommand> (por ejemplo fleetmux start, fleetmux status, fleetmux channel). El binario ejecuta por sí mismo sus subcomandos de terminal (server, attach, reap, web, gui --runtime D) y pasa los subcomandos de flota a la CLI de Python de la misma versión. Las instalaciones hechas con el nombre anterior del proyecto siguen funcionando: su comando, sus variables de entorno y sus directorios de versiones se siguen aceptando como alias.
git clone https://github.com/justindbca/fleetmux && cd fleetmux
pip install -e .
Añade un perfil de Codex para los agentes. Codex ≥ 0.156 guarda cada perfil en su propio archivo (consulta codex.config.example.toml):
# ~/.codex/fleet.config.toml (selected with `codex --profile fleet`)
model = "gpt-5.5"
Nada más: la flota crea su propio home de Codex para los agentes y responde ella misma, para cada directorio de agente, a la pregunta de Codex «¿confiar en esta carpeta?».
El código fuente, las versiones y las incidencias están en GitHub: github.com/justindbca/fleetmux.
Arrancar una flota
Abre un terminal de cmux, configura tu proveedor y modelo de Pi, y sustituye local/my-model más abajo. Los arranques nuevos requieren Redis v2 y una autorización explícita del modo de desarrollo. Sin --supervisor-cli / --worker-cli, start toma el par por defecto de fleetmux/config/fleet.yaml#cli_pairs: un supervisor Pi con openai-codex/gpt-6-astra (thinking high) y workers oficiales de Claude Code. Esa tabla enumera los pares admitidos y su estado; un par que no figure en ella se rechaza. start también exige --lifecycle <mode> (service, dev_task, program, scheduled, review_loop, smoke); un par unverified solo arranca con --lifecycle smoke.
fleetmux start --workers 2 --supervisor-cli pi --worker-cli pi --lifecycle <mode> \
--pi-model local/my-model --allow-unsandboxed-pi --allow-development-v2
echo '{"goal": "Survey the three most-cited papers on X and write a comparison",
"hours": 4,
"artifacts": [{"path": "worker-1/report.md", "min_chars": 800, "contains": ["Conclusion"]}]}' \
| fleetmux task - # hand the goal to the supervisor
fleetmux status # task + per-agent state, context usage, inbox depth
fleetmux stop # cancel; confirm runtime retirement before Redis shutdown
fleetmux task -entrega el objetivo al supervisor.fleetmux statusmuestra la tarea, el estado de cada agente, el uso de contexto y la profundidad de la bandeja de entrada.fleetmux stopcancela la ejecución y confirma la retirada del runtime antes de apagar Redis.
El JSON del objetivo declara los entregables que verifica el daemon: path, min_chars y las cadenas contains. La tarea solo se cierra cuando cada entregable declarado existe, es lo bastante largo y contiene el texto requerido; tres declaraciones COMPLETED falsas la bloquean.
Dos formas de ejecución: task y service
Cada start indica un --lifecycle, que decide qué significa «terminado» y cómo se cierra la ejecución (fleetmux/config/fleet.yaml#lifecycle_modes). Los valores son service, dev_task, program, scheduled, review_loop y smoke. Dos formas cubren el primer día.
Una ejecución de tipo task
Entregas un único objetivo y la ejecución termina. task pone el objetivo en la bandeja de entrada del supervisor; el daemon lo pega en la pestaña del supervisor; el supervisor responde con un único objeto JSON (status, completion, reason, dispatch, artifacts); cada envío va a la bandeja de entrada de ese worker y se entrega cuando el worker está libre; la respuesta del worker vuelve marcada [worker-1 report]. COMPLETED se comprueba contra los entregables declarados y los fallos vuelven como «Independent verification failed»; tres devoluciones bloquean la tarea; BLOCKED detiene la tarea y deja el motivo en status para una persona. Tres JSON inválidos seguidos bloquean la tarea.
Una ejecución de tipo service
--lifecycle service mantiene la flota en marcha sin objetivo ni final: responde a un flujo de peticiones. El supervisor solo enruta; cada worker puede tener su propio prompt; las respuestas son JSON comprobados por el daemon, no por un modelo.
fleetmux start --lifecycle service --worker-names exit-1,reentry-1,rung-1 --roles ~/my-roles \
--supervisor-cli pi --worker-cli pi --pi-model local/my-model \
--allow-unsandboxed-pi --allow-development-v2 --max-busy 2
echo '{"id": "cu-1:r7", "to": "exit-1", "payload": {...},
"reply": {"required": ["verdict", "param", "evidence"], "allowed": ["verdict", "param", "evidence", "note"]}}' \
| fleetmux request -
fleetmux results --drain # one JSON line per finished request
fleetmux pause --reason "market closed"; fleetmux resume
~/my-roles es el directorio de roles de ejemplo del README; apunta al tuyo.
--worker-names: ranuras de worker con nombre por rol (minúsculas, sin puntos; los puntos marcan generaciones). Cada nombre termina en-<number>(proto-1, noproto).--roles DIR:DIR/<agent>.mdse añade alAGENTS.mdde ese agente como «Your role». Un prompt por worker.--max-busy N: como máximo N workers trabajan a la vez; el supervisor no cuenta.- Una petición:
id(de un solo uso),payload(cualquier JSON, ≤ 200 000 caracteres),reply.required,reply.allowedopcional,toopcional (enruta directamente a ese worker, sin turno del supervisor),instructionopcional,timeout_sopcional. La respuesta debe ser un único objeto JSON; una respuesta mala tiene una corrección; una segunda respuesta mala se publica comoinvalid. Estados del resultado:ok,invalid,skipped,timeout,stalled. pausedetiene la entrega y la rotación; las peticiones siguen en cola. Los calendarios (horario de mercado, festivos) son cosa de quien llama:pauseyresumedesde fuera.
Las reglas de nombres y cómo añadir workers sin reiniciar están en Roster YAML.
Roster YAML (añadir / quitar en caliente)
start --roster roster.yaml (en lugar de --workers / --worker-names) toma los workers de un archivo, y el daemon lo vuelve a leer mientras la flota está en marcha.
workers:
- name: exit-1 # same rules as --worker-names (ends with -<number>)
role: roles/exit.md # optional, relative to the yaml; overrides --roles DIR/exit-1.md
- name: reentry-1
- Añadir: un nombre nuevo se escribe en
config.json(ylatest.jsonsi apunta a esta ejecución) y luego se lanza con el prompt READY como pestaña al final de la columna de workers. El supervisor ve la lista nueva añadida a su siguiente mensaje ([Roster change] Workers you can dispatch to now: ...). - Quitar: el worker no acepta trabajo nuevo (los envíos hacia él se rechazan), termina lo que ya tiene y, cuando está inactivo con la cola vacía, su pantalla pasa a la página de historial y su pestaña se cierra. Su directorio de trabajo se queda. Volver a ponerlo en el archivo antes de eso cancela la retirada.
- Cambio de rol de un worker existente: se aplica en su siguiente generación; no se reinicia.
- El supervisor también lo edita: cada prompt del supervisor incluye una sección «Adding and retiring workers (the roster)» que nombra el archivo de roster de esta ejecución. Añade una fila
- {name: <role>-<n>, role: <path>}cuando hay más trabajo listo que workers libres, borra una cuando un worker ya no tiene trabajo, dentro de los límites de su brief de tarea. - Rechazado en bloque (el roster se conserva, motivos en la pestaña daemon como
roster.rejected): YAML inválido, claves desconocidas, nombres inválidos o duplicados, cero workers, un archivo de rol que falta o un nombre ya usado en esta ejecución. Los cambios se registran comoroster.changed. - Umbral de traspaso por modelo: tabla
handoff:opcional ywindow/handoff_ratiopor worker; los cambios se aplican en la siguiente comprobación de entrega, sin reinicio.
Necesita PyYAML en el Python que ejecuta el daemon.
Reanudar una ejecución interrumpida
fleetmux start --resume <run-dir> vuelve a engancharse a una ejecución Redis v2 sin terminar en vez de crear una nueva; --resume auto toma la ejecución de ~/.fleetmux/latest.json y arranca una ejecución nueva cuando esa ya ha terminado (existe <run>/result.json) o falta. Con un daemon vivo la orden no hace nada.
fleetmux start --resume <run-dir>
- Redis arranca otra vez desde el directorio y el socket propios de la ejecución; el AOF recupera lo escrito hasta el último fsync (alrededor de 1 s). Un Redis que sigue vivo se reutiliza.
- Los workers oficiales de Claude cuya pestaña aún ejecuta la CLI se vuelven a enganchar; el resto de agentes (el supervisor de la tarea, los agentes Pi, las pestañas cerradas) arrancan como su generación siguiente (
worker-1->worker-1.1) en el sitio. - Tarea: los entregables verificados y los informes aceptados se conservan; los envíos que estaban en cola o en vuelo los vuelve a encolar el daemon, y el nuevo supervisor recibe un resumen (objetivo, entregables verificados/faltantes, envíos terminados con extractos del informe, envíos reencolados). Servicio: las peticiones aceptadas sin resultado se vuelven a encaminar; una petición con
"replay": falsese publica comotimeout. --max-attempts N(2 por defecto, guardado en la config, modificable al reanudar): un envío / una petición se ejecuta como máximo N veces en total; por encima del tope la tarea quedablocked/ la petición quedatimeout, nombrando el elemento.- Una tarea terminada (cualquier estado terminal) o un
stopexplícito escribe<run>/result.json; una ejecución así nunca se reanuda. Sin--resume, un daemon rearrancado sigue con trabajo incierto y se pausa (sin replay).
Ventana y ratio de traspaso por modelo
La sesión se renueva cuando used >= ratio × window; --handoff-ratio vale 0.6 por defecto, y cada agente resuelve window y ratio por separado: gana la primera coincidencia.
handoff:
claude:opus: {window: 1000000}
claude: {window: 200000, ratio: 0.6}
codex: {ratio: 0.6}
Backend de terminal: tres opciones
fleet habla con el terminal a través de un único backend intercambiable. start --backend cmux|wezterm|fleetmux lo elige, y esa elección se escribe en el config.json de la ejecución: el daemon, los actores, start --resume y cada subflota siguen ese valor, y una ejecución arrancada antes de que existiera esta opción se queda en cmux. Sin la opción, macOS no cambia — cmux cuando cmux está instalado, WezTerm en un Mac sin cmux — y Linux (WSL2 incluido) usa fleetmux por defecto desde M3; --backend wezterm (o --backend cmux) sigue seleccionando los otros backends de forma explícita. Un backend elegido a mano que no puede funcionar en la máquina se rechaza antes de provisionar nada, diciendo qué falta.
| Backend | Por defecto en | Disposición | Necesita |
|---|---|---|---|
cmux |
macOS, cuando cmux está instalado | Supervisor arriba a la izquierda, navegador de historial debajo, workers como pestañas a la derecha. | cmux ≥ 0.64.25 |
wezterm |
Linux / WSL2 cuando se elige a propósito; por defecto en un Mac sin cmux | Una ventana, pestaña 1 con el supervisor a la izquierda y daemon debajo, una pestaña por worker; sin superficie de navegador ni pestañas dentro de un split, así que la página de historial no se integra — fleet imprime <run>/history.html en daemon y la abre con open / xdg-open cuando hay escritorio disponible. |
wezterm y wezterm-mux-server ≥ 20240203 en el PATH; cada ejecución arranca su propio servidor mux privado, nunca tu WezTerm. |
fleetmux |
Linux (WSL2 incluido) desde M3; en macOS solo con --backend fleetmux |
el terminal de disposición fija de fleet, escrito en Rust (rust/, MIT); un fleetmux server privado es dueño del PTY y de la pantalla de cada agente, y tú lo miras con fleetmux attach — ábrelo y ciérralo cuando quieras, varios a la vez, también por ssh; cerrar una vista nunca detiene a un agente. |
fleetmux en el PATH (o $FLEETMUX_BIN); fleetmux --version debe imprimir fleetmux <x.y.z> (protocol 1) con una versión al menos igual al mínimo de deps.yaml. |
fleetmux start --backend wezterm --workers 2 --supervisor-cli pi --worker-cli pi \
--lifecycle <mode> --pi-model local/my-model --allow-unsandboxed-pi --allow-development-v2
fleetmux start --backend fleetmux --workers 2 ... # prints: Watch it with: fleetmux attach --runtime <dir>
fleetmux start --backend fleetmux --attach ... # same, then becomes that `fleetmux attach`
fleetmux attach --runtime <dir> [--no-mouse] # from any terminal, as many times as you like
Compila el backend fleetmux desde este repositorio (cd rust && cargo install --path crates/app), o ejecuta scripts/install_fleetmux.sh [--dest DIR] para instalar el binario precompilado que fija deps.yaml.
Limitaciones conocidas
- En macOS el backend por defecto es cmux (WezTerm sin cmux); en Linux es el terminal propio de fleetmux. En macOS el daemon usa
caffeinatecuando está disponible para que la máquina no se duerma. - Un espacio de trabajo cmux nuevo roba el foco un instante al crearse (cmux ignora
--focus falseparanew-workspace); el daemon te devuelve a donde estabas. - Codex no guarda un registro en flujo de una pestaña; cuando una pestaña se retira, su pantalla final se guarda junto a la nota de traspaso. La transcripción completa es el archivo rollout de Codex en
~/.codex/sessions. - A veces un modelo hace más de un paso despachado en un turno. La comprobación de entregables atrapa el resultado, no el exceso.