文档

在自己机器上跑起一支 fleet 需要的全部内容。下面每条命令都照项目 README 原样抄,一个字未改。

Python 3.14 redis-server cmux ≥ 0.64.25

快速上手

依赖要求

fleetmux 驱动的是真实 CLI 和一个私有 Redis,所以这些得由宿主机提供。

宿主机需要提供什么。
需要什么版本下限或说明
macOS + cmux cmux ≥ 0.64.25(new-surface --command)。start 要在 cmux 终端里运行,或者把 cmux 的 automation → socket control mode 从 cmuxOnly 改开;默认情况下 cmux 只接受由它自己启动的进程发来的命令。
Codex CLI(跑 Codex agent 时) ≥ 0.156(每档一个配置文件、check_for_update_on_startup)。daemon 解析 Codex 的 rollout .jsonl(token_count、task_complete、子 agent 元数据);若某个 Codex 版本改了该格式,就把版本钉在能用的那个上。
PATH 里有 redis-server brew install redis。每个 run 在 checkout 的 .runtime/redis/<token>/ 下起自己的私有实例(socket、PID、日志、数据);TCP 关闭,绝不碰 localhost:6379。二进制仍是外部依赖,不随包分发。
Python 3.14 + requirements.txt 3.14 是测试套件跑在的版本;deps.yaml 列出每个外部程序及其版本下限。
Codex 能对话的模型 OpenAI API、ChatGPT 登录,或本地 OpenAI 兼容服务。daemon 从 rollout 里读上下文窗口,不用自己配窗口大小。

agent 跑在独立的 Codex home(--codex-home,默认 ~/.fleetmux/codex-home)里:auth.json 和配置文件从你的 ~/.codex 软链过来,登录是共用的,但你的 hooks、MCP server、插件和 skill 不会在 agent 里跑(skills.include_instructions=false)。每个 agent 目录的「信任此目录」条目会自动写进去。

安装

只有一个命令:fleetmux <subcommand>(如 fleetmux start、fleetmux status、fleetmux channel)。二进制自己跑终端子命令(server、attach、reap、web、gui --runtime D),其余 fleet 子命令转交同一发版的 Python 命令行。用项目旧名装好的环境照常可用:旧的命令名、环境变量名和发版目录名继续作为别名。

terminal
git clone https://github.com/justindbca/fleetmux && cd fleetmux
pip install -e .

给 agent 加一个 Codex 配置档。Codex ≥ 0.156 每个配置档是一个独立文件(见 codex.config.example.toml):

~/.codex/fleet.config.toml
# ~/.codex/fleet.config.toml   (selected with `codex --profile fleet`)
model = "gpt-5.5"

别的不用配:fleet 会给 agent 建一个独立的 Codex home,并替每个 agent 目录自动回答 Codex 的「信任此目录?」提示。

源码、发版与 issue 都在 GitHub:github.com/justindbca/fleetmux。

起一个 fleet

打开一个 cmux 终端,配置好你的 Pi provider/model,并替换下面的 local/my-model。新启动要求 Redis v2,并需显式的开发许可。不带 --supervisor-cli / --worker-cli 时,start 取 fleetmux/config/fleet.yaml#cli_pairs 里的缺省组合:Pi 监工跑 openai-codex/gpt-6-astra(thinking high),配官方 Claude Code worker。该表列出所有支持的组合及其状态;表里没有的组合直接拒绝。start 还必须带 --lifecycle <mode>(service、dev_task、program、scheduled、review_loop、smoke);unverified 组合只能以 --lifecycle smoke 启动。

terminal
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 - 把目标交给监工。
  • fleetmux status 显示任务、各 agent 的状态、上下文占用和待投递数。
  • fleetmux stop 取消任务,确认 runtime 已退役后才关闭 Redis。

目标 JSON 里声明的是 daemon 要核对的产物:path、min_chars 和 contains 字符串。只有每个声明的产物都存在、长度够、含必需文字,任务才结束;三次误报 COMPLETED 即判任务阻塞。

两种运行形态:task 与 service

每次 start 都要指定 --lifecycle,它规定「完成」的含义以及运行如何收尾(fleetmux/config/fleet.yaml#lifecycle_modes)。取值是 service、dev_task、program、scheduled、review_loop、smoke。上手只要两种形态。

任务型(task)

交一个目标,跑完就结束。task 把目标放进监工收件箱,daemon 把它粘进监工标签;监工用一个 JSON 对象回复(status、completion、reason、dispatch、artifacts);每条 dispatch 进入该 worker 的收件箱,worker 空闲时投递;worker 的回复带 [worker-1 report] 标签回到监工。COMPLETED 按声明的产物核对,核对失败以 "Independent verification failed" 打回;三次打回判任务阻塞;BLOCKED 停止任务,把原因留在 status 里等人处理。连续三次坏 JSON 也判阻塞。

服务型(service)

--lifecycle service 让 fleet 没有目标、没有终点地常驻:应答一串请求。监工只做路由;每个 worker 可以有自己的提示词;答案由 daemon 按 JSON 校验,而不是由模型自己判断。

service mode
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 是 README 里的示例角色目录,换成你自己的目录。

  • --worker-names:按角色命名 worker 槽位(小写、不含点;点用来标记代数)。每个名字以 -<number> 结尾(写 proto-1,不写 proto)。
  • --roles DIR:DIR/<agent>.md 以「Your role」追加到该 agent 的 AGENTS.md。每个 worker 一份提示词。
  • --max-busy N:最多 N 个 worker 同时干活;监工不计入。
  • 一条请求:id(一次性)、payload(任意 JSON,≤ 200 000 字符)、reply.required,可选 reply.allowed、可选 to(直接路由给该 worker,不经监工回合)、可选 instruction、可选 timeout_s。答案必须是一个 JSON 对象;坏答案给一次改正机会,第二次坏答案以 invalid 发布。结果状态:ok、invalid、skipped、timeout、stalled。
  • pause 停止投递和换代,请求继续排队。交易日历(开收盘、假期)是调用方的事:从外面 pause / resume。

名单命名规则与「不重启加减 worker」见 名单 YAML。

名单 YAML(热加载)

start --roster roster.yaml(取代 --workers / --worker-names)从文件取 worker,daemon 在 fleet 运行期间会重新读它。

roster.yaml
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
  • 增加:新名字先写进 config.json(若 latest.json 指向本 run 也一并写),然后用 READY 提示词起一个标签,放在 worker 列末尾。监工在下一条消息末尾看到新列表([Roster change] Workers you can dispatch to now: ...)。
  • 移除:该 worker 不再接新活(派给它会被拒),把手上的活做完,空闲且队列为空时把屏幕存进历史页、关掉标签。它的工作目录保留。在移除完成前把它写回文件即取消移除。
  • 改角色对已有 worker 在下一代生效,不重启。
  • 监工也改它:每份监工提示词都有「Adding and retiring workers (the roster)」一段,写明本 run 的名单文件;可派的活多于空闲 worker 时加一行 - {name: <role>-<n>, role: <path>},某个 worker 没活了就删掉那一行,且受任务书里的限制约束。
  • 整份拒绝(名单不变,原因以 roster.rejected 出现在 daemon 标签):YAML 非法、未知键、名字非法或重复、worker 数为零、角色文件缺失、名字在本 run 已用过。变更记为 roster.changed。
  • 按模型设换代阈值:可选 handoff: 表,以及每个 worker 的 window / handoff_ratio;改动在下一次投递检查时生效,不重启。

跑 daemon 的那个 Python 需要 PyYAML。

断点续跑

fleetmux start --resume <run-dir> 接回一个未完成的 Redis v2 run,而不是新起一个;--resume auto 取 ~/.fleetmux/latest.json 里的 run,若它已结束(存在 <run>/result.json)或不存在则新起一个 run。daemon 还活着时这条命令是空操作。

terminal
fleetmux start --resume <run-dir>
  • Redis 从该 run 自己的目录和 socket 重启;AOF 把直到最后一次 fsync(约 1 秒)的东西都带回来。仍然活着的 Redis 直接复用。
  • 标签里 CLI 还在跑的官方 Claude worker 会被重新接上;其余 agent(任务监工、Pi agent、已关闭的标签)原位以下一代启动(worker-1 -> worker-1.1)。
  • 任务:已核验的产物和已接受的报告保留;排队中或投递中的 dispatch 由 daemon 重新排队,新监工收到一份摘要(目标、已核验/缺失的产物、已完成的 dispatch 及其报告摘录、重新排队的 dispatch)。服务:已接受但没结果的请求重新路由;带 "replay": false 的请求以 timeout 发布。
  • --max-attempts N(默认 2,写进 config,可在 resume 时改):一条 dispatch / 请求总共最多跑 N 次;超过上限任务判 blocked / 请求判 timeout,并点明是哪一条。
  • 已结束的任务(任何终态)或显式 stop 会写 <run>/result.json;这样的 run 永不再 resume。不带 --resume 时,重启的 daemon 仍持有不确定的活,会暂停(不重放)。

按模型设换代窗口与比例

used >= ratio × window 时换代;--handoff-ratio 默认 0.6,每个 agent 各自解析 window 和 ratio,先命中者生效。

handoff
handoff:
  claude:opus: {window: 1000000}
  claude: {window: 200000, ratio: 0.6}
  codex: {ratio: 0.6}

终端后端三选一

fleet 通过一个可插拔的后端跟终端打交道。start --backend cmux|wezterm|fleetmux 选它,选择会写进该 run 的 config.json:daemon、各个 actor、start --resume 和每个子 fleet 都跟随这个值;在这个参数存在之前起的 run 仍留在 cmux。不带参数时 macOS 行为不变——装了 cmux 就用 cmux,没有 cmux 的 Mac 用 WezTerm——而 Linux(含 WSL2)自 M3 起默认 fleetmux;--backend wezterm(或 --backend cmux)仍可显式选另外两个。显式选了机器上跑不起来的后端,会在 provision 任何东西之前被拒,并说明缺什么。

三个后端。
后端默认时机布局需要什么
cmux macOS,装了 cmux 时 监工在左上,历史浏览器在下面,worker 作为标签在右侧。 cmux ≥ 0.64.25
wezterm Linux / WSL2 显式选择时;没有 cmux 的 Mac 上的默认值 一个窗口,标签 1 左边是监工、下面 split 出 daemon,每个 worker 一个标签;没有浏览器面板、split 里也没有标签,所以历史页不内嵌——fleet 在 daemon 里打印 <run>/history.html,有桌面时用 open / xdg-open 打开。 PATH 里有 wezterm 和 wezterm-mux-server ≥ 20240203;每个 run 起自己的私有 mux server,绝不用你的 WezTerm。
fleetmux Linux(含 WSL2)自 M3 起;macOS 上只有 --backend fleetmux 时 fleet 自己用 Rust 写的固定布局终端(rust/,MIT);一个私有 fleetmux server 拥有每个 agent 的 PTY 和屏幕,你用 fleetmux attach 看它——随时开关、可以同时开几个、也能经 ssh 跑;关掉查看器不会停掉任何 agent。 PATH 里有 fleetmux(或用 $FLEETMUX_BIN);fleetmux --version 必须打印 fleetmux <x.y.z> (protocol 1),版本不低于 deps.yaml 的下限。
wezterm
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 backend
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

fleetmux 后端可以从这个 checkout 构建(cd rust && cargo install --path crates/app),或跑 scripts/install_fleetmux.sh [--dest DIR] 安装 deps.yaml 钉住的预编译二进制。

已知限制

  • macOS 上默认后端是 cmux(没有 cmux 时是 WezTerm);Linux 上是 fleetmux 自己的终端。macOS 上 daemon 在可用时用 caffeinate,让机器不睡。
  • 新建 cmux workspace 时会短暂抢焦点(cmux 对 new-workspace 忽略 --focus false);daemon 会把你切回来。
  • Codex 不保留标签的流式日志;标签退役时最后一屏存在交接单旁边。完整记录是 ~/.codex/sessions 下 Codex 的 rollout 文件。
  • 模型偶尔会在一回合里多做一步。产物核对抓的是结果,不是越界。