☰
Firstmate 监督宿主(Supervision Host):在非 Pi 主控上以无头引擎会话接管监督分支的完整实现指南
2026/10/9 4:46:02 网站建设 项目流程

【免费下载链接】firstmate

Talk to one agent. Ship with a crew.

项目地址:https://gitcode.com/gh_mirrors/fi/firstmate
点击查看免费下载

导读

本文围绕 Firstmate 仓库中的核心设计文档 docs/supervision-host.md 展开,系统讲解"监督宿主"(Supervision Host)这一架构:它如何在一台非 Pi 主控上以无头引擎会话运行监督分支(supervision branch)的完整契约,接管原本由 watcher 周期负责的任务唤醒处理。读完本文,你将掌握:宿主在哪些主控上默认/可选启用、attended 与 away 两种姿态的行为差异、config/supervision-host的引擎选择与门控语义、六种主控各自的 arm owner 接入方式、单次唤醒(one wake)的完整处理流程、对话镜像(dialog mirror)、park 边界与 broken-session 闩锁等关键机制,以及对应的源码归属与测试验证路径。

为什么需要监督宿主:与 Pi 分支同构而非另起炉灶

在 Pi 主控上,监督分支是船长(captain)自身进程内的第二个会话,由 docs/pi-supervision-branch.md 定义。但在非 Pi 主控(Claude、Cursor、OpenCode、omp、Grok、Codex)上,不存在这样一个进程内分支。监督宿主的职责正是补上这个缺口:宿主为这条主控拥有 watcher 周期(watcher cycle),并把监督分支作为一个无头引擎会话(headless engine session)运行。

文档强调它是与 Pi 分支"同一个架构,而不是第二个架构"("It is one architecture with Pi's, not a second one")。两者共享的部件包括:

  • 相同的分支提示词(branch prompt):由bin/fm-branch-prompt.sh发出与 Pi 分支逐字节一致的提示词;
  • 相同的行资格(row eligibility):由bin/fm-branch-dispatch.mjs计算分支可认领的队列行;
  • 相同的记录(records):如 away-posture 记录state/.afk-contract;
  • 相同的受保护脚本(guarded scripts):引擎以FM_SUPERVISION_ACTOR=branch身份运行,受保护的脚本对它与对 Pi 分支一视同仁。

这里需要区分两个概念:close是 watcher 周期结束时打印的输出;arm owner是每个主控 harness 中负责启动 watcher 周期并读取其 close 的组件。

当前范围(Scope today)

宿主的启用规则(home gate)由 docs/configuration.md 定义,并由bin/fm-supervision-engine-lib.sh中的fm_supervision_host_enabled实现:

  • Claude 主控:默认运行(即使没有任何配置文件,也以 Claude 引擎的默认模型运行);
  • Cursor、OpenCode、omp、Grok、Codex 主控:可选启用,只有当config/supervision-host文件存在且未退出(未被config/supervision-host-off覆盖)时才运行;
  • Pi 主控:永远保持进程内分支,无论两个文件内容如何;
  • Kimi 没有主控监督协议,没有 arm owner 可运行宿主。

不运行宿主的 home 行为与没有宿主时完全一致。目前宿主可运行在六种主控旁:away 姿态覆盖全部六种,而attended 姿态只覆盖 Claude 和 Cursor,因为只有这两种主控拥有经过验证的对话镜像(dialog mirror)。

尚未落到宿主上的能力("Not yet on the host")包括:Codex 主控旁的 attended 监督、其余五种主控上默认启用宿主,以及 daemon(state/.afk守护进程)的退役——这些是同一设计的后续步骤。

按姿态与 harness 的行为

场景行为
Attended(无 away 记录)· Claude/Cursor引擎接过 Pi 分支本会接管的唤醒,常规结果绝不唤醒 main;其余 close 与普通 watcher arm 一样原样到达 main
Attended · OpenCode/omp/Grok/Codex宿主是直通(pass-through):每个 close 都像没有宿主一样到达 main
Away(存在 away 记录)宿主把每个 close 交给引擎;main 保持 parked,除非宿主把唤醒交回
/afk在运行宿主的 home 上不启动 away daemon,因为宿主就是那里的 away 会话(bin/fm-afk-launch.sh start-native/start会拒绝启动 daemon)
/quietattended 宿主运行处不做任何事(见 Quiet mode);别处启动 daemon;当 daemon 的标记state/.afk存在时,宿主与普通 arm 一样靠边站
Pi无论文件说什么都保持进程内分支,且不构建 Pi 引擎

组件与代码归属

原文档给出了一张组件归属表,这是理解宿主实现的关键。以下为完整表格(对应源码均在仓库中可查):

组件归属职责
主循环bin/fm-supervision-host.sh头部(header)拥有每个 close 的处理顺序、park 边界与计时、arm 退出采样与信号观察延迟、所有权检查、前任清理、状态文件与可调参数
arm owner各主控既有的 arm owner为运行宿主的 home 运行宿主,并把交回的唤醒投递给 main
引擎bin/fm-supervision-engine-lib.sh拥有 home gate(含 Claude 默认开启与退出)、已验证引擎列表,以及一次有界的引擎轮次(含对存活过轮次的引擎工具进程的收割)
行资格与 offer 规则bin/fm-branch-dispatch.mjsbranchOfferForWake的命令入口,宿主与 Pi 扩展用它计算分支可认领的行、任务范围,以及分支能否接走某个 close
授权与 drainbin/fm-wake-grant.sh把分支的行发布并绑定到宿主自身进程;docs/watcher-continuity.md 拥有引擎执行的逐角色 drain 与确认
提示词bin/fm-branch-prompt.sh发出与 Pi 分支相同的字节稳定提示词;每个唤醒都指明其宿主的报告面
报告面bin/fm-branch-report.shPi 分支fm_branch_report工具的命令孪生,任务范围相同
租约与权限bin/fm-lease-lib.sh拥有逐任务租约、main 持有的角色划分与 away 迁移
对话镜像bin/fm-host-mirror.sh拥有镜像文件、写入者、已验证写入者列表与 feed
船长结果 drainbin/fm-wake-drain.sh在BRANCH OUTCOMES小节呈现可见的新/未处理结果;bin/fm-branch-outcome.sh mark-processed是 main 的确认
main 侧docs/supervision-protocols/supervision-host.md运行宿主的 home 在会话开始时渲染给 main 阅读的协议文本

从bin/fm-supervision-host.sh的用法行可以看到宿主入口:fm-supervision-host.sh park [--restart]。宿主对其所属 arm owner 来说就是一个 arm:它打印 arm 自己的行,只在 main 被需要时退出,并在它自行处理的每个 close 之间保持 parked。--restart让首个周期以fm-watch-arm.sh --restart启动,并把FM_WATCH_PREDECESSOR_ARM_PID只传给该首周期(供 OpenCode、omp 这类每个 close 后自行启动后继者的 owner 使用)。

Arm owners:六种主控如何接入

宿主头部拥有 arm owner 读取的输出契约。交回的唤醒通过各 harness 已信任的唤醒路径到达 main:

主控Arm owner交回的唤醒以何种形式到达 main
ClaudeStop 自动 arm,bin/fm-claude-stop-autoarm.sh(在其单飞生成内)hook 的 exit-2 重唤醒(Stop hook feedback)
Cursorstophook park,bin/fm-turnend-guard-cursor.shpark 的watcherfollow-up
OpenCodeTUI 插件,.opencode/plugins/fm-primary-watch-arm.js(每个 close 后重启自身后继者)经promptAsync的watcher提示
ompwatch 扩展,.omp/extensions/fm-primary-omp-watch.ts(每个 close 后重启自身后继者)扩展的watcherfollow-up
Grok模型的被跟踪后台调用(会话开始时渲染为bin/fm-supervision-host.sh park)后台任务完成通知
Codex前台 checkpoint,bin/fm-watch-checkpoint.sh(替代 watcher)checkpoint 自身输出

hook、插件、扩展与 checkpoint owner 会把它们的 harness 作为主控 pin 传入(FM_SUPERVISION_HOST_PRIMARY);Grok 的模型自有调用依赖主控检测。宿主把分派的工作 pin 到主控的 crew harness 而非引擎的 harness。注意 Grok 的 arm 命令在会话开始渲染时就固定了,因此增删文件要到下一次会话开始才生效;其余 owner 每次 arm 时读取文件。

报告面与租约权限

bin/fm-branch-report.sh把结果追加到结果存储(bin/fm-branch-outcome.sh)并写入宿主要求的逐轮收据(per-turn receipt)。away 轮次在船长返回后记录的非静默行还会作为持久化的 check 唤醒排入 main 的队列;静默结果留在存储中但不排队、不作为备注转达;attended 轮次不排任何队。

宿主的引擎以以下设置运行,从而让每个受保护脚本对它如同对待 Pi 分支:

  • FM_SUPERVISION_ACTOR=branch;
  • session-lock 持有者作为FM_LEASE_HOLDER_PID;
  • 主控的 harness pin;
  • 本轮的报告 id。

姿态(Postures)

宿主在每个 close 以及每轮开始时读取记录的模式(bin/fm-afk-contract.sh的 "AWAY OR QUIET")。只有 away 记录算 away:没有记录,或 quiet 模式 daemon 写入的记录,都表示船长在场,因此宿主会在 daemon 未运行的 quiet 记录旁以 attended 运行。

Attended(在场)

宿主通过bin/fm-branch-dispatch.mjs offer询问 Pi 分支的 offer 规则(branchOfferForWake)分支能否接走这个 close。因此,非 Pi 上的 close 到达 main 的时机与 Pi 完全一致:check 触发、决策持有的信号或过期触发、不安全或无分支可拿的扫描,都仍归 main。

在 main-only 直通时,宿主启动后继 watcher 周期并让它保持运行,然后原样打印 close。它让 watcher 的 recovery marker 保持 downtime 读数,因为 re-arm owner 只在 marker 读 downtime 时才向 main 投递 close。会话的下一次不带--restart的 park 会请求 takeover 以恢复单一宿主持有的 arm。

宿主在以下任一条件成立时也把 close 原样直通、不加任何行(该列表由bin/fm-supervision-engine-lib.sh的fm_supervision_host_attended_ready拥有):

  • 本 home 没有可用的引擎;
  • 轮次所需工具缺失:引擎可执行文件、node、jq,或用于限界轮次的 perl / timeout / gtimeout 之一;
  • 主控没有经过验证的对话镜像;
  • 无法识别 main 会话的锁持有者;
  • 会话因引擎错误处于冷却中(broken-session 闩锁)。

引擎接走的 close 按"单次唤醒"处理,唤醒消息头部带对话镜像。处理完且只有常规结果的唤醒绝不打扰 main;处理完且记录了船长结果(船长仍在场)的唤醒以一行supervision-host: branch-outcome:退出并点名其存储行;失败轮次把 close 连同一行supervision-host:交给 main(与 away 相同)。引擎轮次在船长在场时运行,其受保护操作会取得任务租约,从而避免它和 main 处理同一个任务。

Away(离开)

每个 close 都交给引擎;船长结果留在结果存储,直到返回 drain 呈现。每个以 attended 开始的轮次在开始时再次检查 attended 规则:away 时被接受、但轮次开始时船长已返回的 close,或 attended close 在继任者启动期间任务变成 main-only(出现决策)的,都原样到达 main 并让后继周期继续运行。船长在 attended 轮次运行期间离开的,其船长结果转为 away 结果,同样等待返回。

Quiet mode(静默模式)

/quiet请求的正是 attended 宿主已经在做的事:常规唤醒不打扰在场的船长。因此在 attended 宿主运行处,/quiet只是不进入任何状态;在 broken-session 闩锁期间它表示会话已暂停;当宿主缺少某个部件时,/quiet点名缺失部件并进入 quiet daemon,而 away 记录活跃期间船长返回优先。bin/fm-afk-launch.sh的quiet-check契约拥有就绪测试与拒绝逻辑,.agents/skills/quiet/SKILL.md 拥有操作流程。

对话镜像(The dialog mirror)

引擎会话在两次唤醒之间收不到任何对话,因此每次 attended 唤醒的头部都携带自上次唤醒以来船长与 main 的发言:与 Pi 分支作为镜像消息收到的[captain]/[main]上下文一致,由bin/fm-branch-prompt.sh的 "Context channels" 规则框定(上下文用于判断,绝不是指令)。

bin/fm-host-mirror.sh拥有记录、写入者、文件、feed 与已验证写入者列表,其头部拥有格式、边界与失败契约。关键实现细节(来自脚本头部):

  • 写入者使用代码拥有的轮次面(code-owned turn surfaces)而非模型生成的文本:Claude 经其 prompt-submit 与 Stop hooks,Cursor 经其beforeSubmitPrompt与afterAgentResponsehooks;写入者追加的是船长文本(提交的提示)与 MAIN 文本(轮次的最终助手消息),绝不记录工具流量;
  • 镜像文件为$STATE/.host-mirror.jsonl,每行一个 JSON 对象:{"seq":N,"epoch":N,"key":"<main session>","id":"<source id>","tag":"captain"|"main","text":"..."};
  • 每条文本上限 4000 字符(含截断说明,保留头尾);文件超过 300 条时裁剪到最新 200 条;追加采用写全新文件、owner-only、rename 到位的原子写,失败或中断不会破坏镜像;每次追加与 feed 都在$STATE/.host-mirror.lock下运行;
  • feed 由$STATE/.host-mirror-cursor记录已投递给某引擎会话的最新条目;feed <session> new|resume输出下一次唤醒携带的条目(最旧在前),先暂存目标游标到.next,commit只在引擎轮次连同报告被接受后才推进游标——轮次失败、无记录或被停止时,其条目留给下一次再喂;
  • feed 有界:16000 字符,保留最新,并有一行说明在这个边界内省略了多少更早条目;
  • 新的引擎会话在当前 main 会话的最新条目上重新锚定,恢复的会话只获得新增内容。

只有 Claude 和 Cursor 有写入者(已针对真实 harness 验证能记录从第一条船长提示起的对话),因此只有它们运行 attended 姿态。Codex 尚无写入者(监督中的 Codex main 在跨前台 checkpoint 的单个轮次内,船长消息不会触发 prompt 或 Stop hook);Grok 与 OpenCode 无写入者(其会话在首个轮次占用 fleet 锁,首轮船长提示永远无法被记录);omp 无验证写入者(没有可验证的 omp 实例)。

单次唤醒(One wake)

在每个引擎接走的可行动 close 上,宿主按以下顺序执行:

  1. 启动并验证后继 watcher 周期、确认处理交接(handling handoff),保证引擎工作期间 fleet 仍受监督;
  2. 计算本轮姿态下分支可认领的行并发布授权(bin/fm-wake-grant.sh);
  3. 运行一次有界的引擎轮次,携带分支提示词与唤醒消息——attended 时头部为对话镜像,away 时尾部为记录回读;引擎与 Pi 分支一样 drain、处理、经bin/fm-branch-report.sh报告并确认;
  4. 释放分支的租约与授权,无论唤醒是否被处理;
  5. 只在处理过唤醒时 park 到后继者上;main-only 直通不是 park,宿主留下该周期运行后退出。

宿主只在以下三条件全部成立时才把唤醒计为已处理:

  • 轮次干净退出;
  • 轮次至少记录了一份报告;
  • 轮次在唤醒队列中未留下任何其授权的行。

处理过的唤醒其结果去向

Away 时,处理过的唤醒永不打扰 main(无论结果是常规还是船长级);船长结果在结果存储中等待,返回后由 drain 的BRANCH OUTCOMES小节呈现,返回简报(bin/fm-afk-return.sh)统计其数量并指向该小节。

船长在轮次运行期间返回

这是 away 规则的唯一例外:返回简报可能渲染于该轮可见结果存在之前,因此宿主把 close 连同所有可见结果交给 main 转达(无论轮次是否处理了唤醒)。每个在返回后记录的可见结果都可通过返回简报或排队的check唤醒到达 main——返回 owner 在读取存储前归档记录,报告面在记录消失后为非静默行排队。可见结果即使在交接丢失时也仍可用;静默结果留在存储中,既不排队也不作为备注转达。

船长结果(Captain outcomes)

attended 引擎在船长仍在场时记录的船长结果,会通过 owner 的常规唤醒路径唤醒 main 一次,带一行supervision-host: branch-outcome:点名其存储行。main drain,bin/fm-wake-drain.sh在BRANCH OUTCOMES小节呈现它,并给出精确的确认命令:bin/fm-branch-outcome.sh mark-processed --through <seq>。之后每次 drain(包括会话开始摘要)都会再次呈现未处理的船长结果,直到 main 确认——被忽略的结果不会浪费额外轮次,也永不丢失。

drain 头部拥有该小节的边界,这些规则使其有界且有序:

  • 船长结果排在前面,绝不等待常规结果;
  • 同一任务的重复船长结果折叠为最新一条,并点名携带了几条,一次确认覆盖全部;
  • 字节上限只显示最旧的连续船长结果段,因此打印的确认恰好覆盖显示的行,并把保留的更新行计入,待该段确认后继续;
  • 常规结果永不打开 main 轮次:下一次 drain 只列一次最新的可见常规结果(仅告知、无需确认),并把更旧的可见常规备注折叠为计数;静默常规结果从不出现。

该小节只对运行宿主且主控非 Pi 的 home 上的 main 运行,且 away 记录存在时永不运行。drain 是这些结果的唯一呈现者、读游标的唯一 owner(包含 away 窗口:返回简报统计窗口结果并指向该小节而非逐一列出)。在 Claude Code 主控上,Calm 扩展单独向船长显示有界的只读监督备注(docs/calm.md),不移动任何结果标记、不向 main 上下文添加内容。

无法读取或投影存储(含 jq 缺失)、无法打印小节或推进读游标的 drain 会说明情况、不把未显示的内容标为已读、以非零退出,从而保持返回的追赶门控(catch-up gated)。多字节摘要按 UTF-8 字符边界截断以适配预算。未处理的船长结果绝不会被当作已处理(含索引修复或切换到 Pi 时);缺标记规则由bin/fm-branch-outcome.sh拥有。已切换宿主的 home 可在升级或中断切换后重新呈现未确认结果,每条船长行显示记录时长,小节要求 main 先检查任务当前状态再行动。main 对船长的回复只覆盖仍开放的结果,且运行打印的确认覆盖每个已呈现的结果(已解决的与处理中的开放项都要)。

一个限制:若船长在 attended 引擎轮次运行期间离开并返回,而宿主在该轮branch-outcome唤醒投递前被终止,则不会有即时唤醒到达 main;船长行仍是持久的,下一次 drain 会一直呈现它直到被确认。

失败方向(Failure direction)

任何无法完成引擎已接走唤醒的路径,都会把唤醒交回 main,close 之后带一行supervision-host: <why>。交回前宿主停止其后继周期;只要记录过继任代(无论是否确认),就显式重新发布该代的 downtime——即使继任者已退出也必须发布,因为不再有 watcher 清理能让 close 可投递给 arm owner。若发布失败,交接追加一行supervision-host: watcher downtime could not be restored并以非零退出。因此 owner 的下一次 arm 从与无宿主时相同的状态开始,唤醒在队列中保持持久。

交回唤醒的路径

  • 未验证的继任者;
  • 被拒绝的交接;
  • 不可读的队列;
  • main 已认领的行;
  • 引擎或 node 缺失;
  • attended 唤醒上无法读取的对话镜像;
  • 反复引擎错误后闩锁的会话(冷却期内);
  • 超时或失败的轮次;
  • 未记录报告的轮次;
  • 已报告但留下任何授权行未确认的轮次(其行点名并留在队列中供 main drain)。

失败轮次还会让下一个唤醒从全新引擎会话开始。船长在记录可见结果的失败轮次期间返回时,交接也携带这些结果供 main 转达;静默结果留在存储中而无交接备注。

Broken-session 闩锁

宿主复制 Pi 分支的 broken-session 策略(docs/pi-supervision-branch.md),以引擎错误代替 provider 错误:轮次非零退出、命中界限、或无完整成功结果即算错误。连续两次引擎错误闩锁会话:五分钟冷却期内每个唤醒都到达 main(attended close 原样,away close 带supervision-host:行),之后一个唤醒探测引擎,每次探测以引擎错误告终就把冷却翻倍,上限一小时。记录报告且无引擎错误的轮次清除闩锁;有完整引擎结果但无报告的轮次既不计数也不清除;引擎错误即使未记录报告也计数。

首次触发在失败轮次的交接上加一行supervision-host:;恢复只记录在宿主台账中,常规探测不打扰 main。返回简报(bin/fm-afk-return.sh)报告窗口内引擎错误与返回时任何可见的闩锁。闩锁归属一个 main 会话、引擎与模型,因此新 main 会话或另一个引擎/模型从干净状态开始。首轮冷却值在引擎库中为FM_SUPERVISION_HOST_COOLDOWN=300(秒)。

失去所有权与宿主无 close 死亡

当宿主失去 session-lock 所有权或 auto-arm 代次时,它静默让位,把连续性交给当前所有者;未获所有权就启动的宿主在激活前让位,绝不停止 owner 的宿主或 watcher、不释放其租约。宿主无 close 死亡时,owner 重试它;Grok 模型与 Codex checkpoint 视为失败周期并启动下一个。下次宿主 arm 前做两件事:按记录身份停止前任留下的运行中进程(含被杀轮次记录的引擎后代),并移除该轮的文件。宿主在state/.supervision-host记录自己运行的每个进程的身份,后继宿主用stop_recorded(TERM 后 KILL)精确停止它们,而不用名字猜测。

Park 边界(The park boundary)

宿主在每个自行处理的 close 间保持 parked,只在 main 被需要时退出。Claude 会丢弃它在 hook 超时时终止的 Stop hook 的 exit 2(见 docs/verification/supervision.md 的 2026-09-23 记录);Cursor 的stophook 携带同样的 28,800 秒注册。普通 watcher park 很少持续这么久(心跳 close 会唤醒 main),但宿主吸收自身唤醒,因此它在注册到期前自行结束 park。

设定边界

FM_SUPERVISION_HOST_PARK_SECONDS设定该边界,默认 27,000 秒;不是小于 28,800 的正整数的值按默认处理。OpenCode、omp 与 Grok 的 owner 没有 hook 超时并保持同一默认,因此它们的 park 以相同节奏结束。

到达边界时

宿主停止本 home 的 watcher,以一行supervision-host: cycle boundary退出。main drain 并确认,owner 启动下一次 park:

主控下次 park 何时开始
Claude 与 Cursor下一次轮次结束时
OpenCode 与 omp立即
Grok模型 re-arm 时

宿主在每次循环遍历时检查边界,因此已等待的 close 无法把它拖过边界;它也不会启动可能在边界时仍在运行的引擎轮次(轮次界限加引擎宽限),并在 close 到达时与轮次启动前各判定一次。拒绝这样的轮次时,该 close 在边界行之前到达 main,其唤醒在队列中保持持久。每个边界一次短暂的 main 轮次,是"绝不静默丢失 park"的代价。

Codex checkpoint 界限

Codex 没有异步唤醒,因此其 checkpoint 自身的界限就是 park。checkpoint 把它作为边界传入,并以普通安静行报告边界(checkpoint: no actionable wake within <n>s):

姿态Checkpoint 界限
AttendedFM_CODEX_WATCH_CHECKPOINT(默认 180 秒)
存在 away 记录提升到FM_CODEX_WATCH_CHECKPOINT_AWAY(默认 3,600),随后上限 27,000 秒,以免 parked 的 main 每隔几分钟被唤醒

因为该界限不是 harness 超时,checkpoint 还设置FM_SUPERVISION_HOST_PARK_LIMIT,让边界前启动的引擎轮次可在边界后完成。park 期间输入的船长消息等待 checkpoint 返回,最多为界限加一个引擎轮次,除非船长中断它。

引擎会话与成本

引擎跨唤醒保持一个会话,使字节稳定提示词保持缓存。会话以当前 main 会话为键,在两种情况下重开:每个 main 会话开始,以及每FM_SUPERVISION_HOST_ROTATE_TURNS(默认 20)轮(因为每次唤醒都增加历史,逐轮成本随之增长)。没有船长可见的东西依赖该会话,因为结果存储承载所有结果;引擎判断所依据的船长上下文是每个 attended 唤醒头部的对话镜像与每个 away 唤醒尾部的记录回读。

state/.supervision-host.log记录每个 close 的去向,每个引擎轮次行携带:结果、引擎报告的使用量、本轮成本、会话累计成本——引擎成本今天就从这个日志读取。

可调参数一览

宿主头部(bin/fm-supervision-host.sh)定义的环境变量(来自脚本头部原文):

变量默认说明
FM_SUPERVISION_HOST_PARK_SECONDS27000park 边界;须为小于 28800 的正整数,其他值按默认
FM_SUPERVISION_HOST_PARK_LIMIT等于边界park 界限;晚于边界且低于注册的值让轮次运行过边界,其他值为边界本身
FM_SUPERVISION_HOST_TURN_TIMEOUT1200引擎轮次超时(秒)
FM_SUPERVISION_HOST_ROTATE_TURNS20多少轮后重开引擎会话
FM_SUPERVISION_HOST_READY_TIMEOUT25后继周期验证可用时长(秒)
FM_SUPERVISION_HOST_POLL1循环轮询间隔(秒)
FM_SUPERVISION_ENGINE_GRACE30轮次界限外的引擎宽限(秒)
FM_SUPERVISION_ENGINE_CLAUDE_BINPATH 上的claudeClaude 引擎可执行文件(测试接缝用)

引擎(Engines)与 Claude print mode 行为

已验证引擎(verified engine)是某 harness 的无头模式,其隔离、actor 传播、无提示权限、限界与缓存都经过实测。今天唯一已验证的引擎是 Claude 的 print mode,在 Claude Code 2.1.278 与 2.1.281 上实测(验证记录见 docs/verification/supervision.md)。引擎库的FM_SUPERVISION_ENGINES_VERIFIED='claude'是代码中的验证列表。

bin/fm-supervision-engine-lib.sh的fm_supervision_engine_turn展示了真实参数构造:

claude -p "<wake message>" --safe-mode --system-prompt-file "<prompt>" --tools Bash,Read --permission-mode dontAsk --allowedTools Bash Read --model "<model>" --output-format json [--session-id|--resume "<session>"]

其行为拆解:

  • 隔离:--safe-mode不加载本 home 的任何 hooks、CLAUDE.md、skills、插件或 MCP 服务器,引擎永远无法触发本 home 自己的 Stop / SessionStart hooks;--bare不可用(它从不读取 claude.ai OAuth);引擎 shell 内主控不在 harness 祖先链中,引擎永远无法充当 session-lock owner。
  • 权限:--permission-mode dontAsk配合Bash/Read允许列表从不提示,被拒调用作为工具错误到达模型而不会卡住轮次;--safe-mode不覆盖用户默认模式,因此模式总是显式传入;Claude 按工作目录路径检查直接文件读取,代码根之外的 home 或 state 目录以--add-dir传入。
  • 会话与输入:新会话--session-id、恢复--resume;提示词是第一个位置参数,stdin 为/dev/null(打开的 stdin 会付出三秒等待);引擎从被跟踪的代码根运行,其会话文件落在该目录的 Claude 项目存储中。
  • 结果与成本:--output-format json携带错误标志、轮次数、用量与工具自身成本估计;恢复会话时该成本是会话累计,而用量与轮次数是本轮的,因此引擎库从宿主上一轮记录的累计推导本轮成本。只有完整成功结果(type为result、subtype为success、is_error为 false、total_cost_usd、num_turns与四个usagetoken 计数都是有限数字)才被宿主计为成功;任何其他结果都使轮次失败并把唤醒交给 main(fm_supervision_engine_result以 Node 解析并输出error=0|1 cost=... conversation_cost=...)。
  • 工具进程收割:工具命令运行在各自的进程组中,界限的组信号够不到它们;引擎库在轮次运行时每秒快照引擎后代,轮次结束后按记录身份收割(TERM 后 KILL)。收割只针对观察到的后代尽力而为,不是硬界限:工具把进程分离进自己的进程组、且在两次快照间失去引擎祖先链的进程不会被记录并存活过轮次——这与bin/fm-timeout-lib.sh命名的残留一致。

模型与引擎选择

默认模型是sonnet,它以更大模型成本的一小部分正确处理了每次实测唤醒;config/supervision-host可指名其他模型。Claude 引擎可运行在六种主控任意一种旁,但只有 Claude 主控在宿主启用时默认选择它(即使无文件);Cursor、OpenCode、omp、Grok 或 Codex home 需在config/supervision-host中指名claude(可带模型)。那里/afk会在文件未选择引擎时说明。

配置与启用:config/supervision-host门控

综合 docs/configuration.md,宿主由两个可选的本地 gitignored 文件控制:

  • config/supervision-host-off:存在即在本 home 的所有主控上退出宿主,无论内容如何;
  • config/supervision-host:选择启用并选择引擎。可为空或含一行<engine> [<model>]:
    • 空或default:选择主控自身 harness 的引擎及其默认模型(Claude 引擎默认sonnet);
    • <engine> [<model>]:指名已验证引擎(目前只有claude)与可选的模型名/别名;default <model>以该模型选择主控 harness 的引擎。

只有 Claude 有自己的已验证引擎,因此 Cursor、OpenCode、omp、Grok、Codex home 在文件中写claude。未验证引擎、无验证引擎的主控或格式错误的行会让宿主无引擎可用:它不接任何唤醒,所有唤醒如同无宿主一样到达 main,且每个 away 姿态的唤醒都包含一行点名该问题。宿主在每次唤醒时重读两个文件,因此引擎更换或退出在下一个唤醒即生效、无需重启。

退出(opt-out)会继承进 secondmate homes(主控退出也退出其 secondmates);config/supervision-host是每个 home 本地选择、不继承。宿主运行时,main 的租约检查命令也取逐任务租约锁,宿主引擎的认领不会与 main 已开始的变更竞争(bin/fm-lease-lib.sh)。

验证与测试

每个 arm owner 的测试套件都针对 stub 宿主覆盖其宿主模式。原文档的验证表格(完整继承):

测试覆盖内容
tests/fm-supervision-host.test.sh以 stub 引擎驱动真实宿主、auto-arm、授权、drain、报告与租约脚本,两种姿态,含共享 offer 规则与 drain 的BRANCH OUTCOMES小节
tests/fm-claude-stop-autoarm.test.shClaude arm owner 的宿主模式(stub 宿主)
tests/fm-cursor-primary.test.shCursor arm owner 的宿主模式(stub 宿主)
tests/fm-pi-watch-extension.test.shOpenCode 插件的宿主模式(stub 宿主)
tests/fm-omp-harness.test.shomp arm owner 的宿主模式(stub 宿主)
tests/fm-watch-checkpoint.test.shCodex checkpoint 的宿主模式(stub 宿主)
tests/fm-supervision-instructions.test.sh渲染出的协议,含 Grok 的 arm 命令
tests/fm-host-mirror.test.sh经被跟踪的 Claude 与 Cursor 注册的镜像写入者、home gate、feed 与已验证写入者列表
tests/fm-afk-launch.test.sh各主控上的 home gate、/afkdaemon 拒绝,以及运行宿主的 home 上的/quiet:声明、暂停声明、各点名缺失部件、携带记录模式的 quiet daemon 回退、失败 quiet 启动归档其 quiet 记录、活跃 away 记录下直到返回前拒绝
tests/fm-afk-return.test.sh宿主 home 上返回的 drain 持有读游标穿过 away 窗口的推进,Pi 上不推进
tests/fm-supervision-host-live-e2e.test.sh运行真实引擎轮次;可选启用(消耗 token)
tests/fm-supervision-host-attended-live-e2e.test.sh可选启用、需凭证的守卫:对空闲 Claude 主控反复 attended main-only 交接、继任者自身 close、轮次转 main-only 的 close,以及占位远程监听器;接受 pre-fix ref 作阴性对照
tests/fm-host-mirror-live-e2e.test.sh针对真实 harness 验证 Claude 与 Cursor 镜像写入者;可选启用(消耗 token)

verification/supervision.md 记录了带日期的实况结果(如 "supervision host live (2.1.281 / 2.1.283 (Claude Code)): a real engine handles and resumes away wakes under the branch contract without waking main")。

小结:宿主的可靠性骨架

监督宿主的设计贯穿三条可靠性主线:交接(handoff)——每个引擎接走的唤醒先验证后继 watcher 并确认交接,失败路径一律把唤醒交回 main 并恢复 downtime 标记;持久性——结果存于bin/fm-branch-outcome.sh的结果存储,未确认的船长结果每次 drain 重新呈现,drain 是唯一呈现者与唯一读游标 owner;自愈——失去所有权静默让位、前任残留按身份清理、broken-session 闩锁以指数冷却探针引擎、park 边界保证宿主不会越过 28,800 秒 hook 注册。掌握这些机制后,无论是排查"为什么这个唤醒没有到达 main"、配置非 Claude 主控的引擎选择,还是为宿主新增一种主控的 arm owner,你都能从 docs/supervision-host.md 与其归属脚本(bin/fm-supervision-host.sh、bin/fm-supervision-engine-lib.sh、bin/fm-host-mirror.sh、bin/fm-branch-dispatch.mjs)出发,沿文档指出的 owner 边界逐层定位。

【免费下载链接】firstmate

Talk to one agent. Ship with a crew.

项目地址:https://gitcode.com/gh_mirrors/fi/firstmate
点击查看免费下载
上一篇:Hyper终端字体大小调整:保护视力的设置
下一篇:Bootstrap 5.3未来展望:2024功能路线图

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询