ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Firstmate 主 Harness 启动与钩子体系解析:Turn-End 守卫、PreToolUse 保护、会话启动与监视器监督的职责边界

Firstmate 主 Harness 启动与钩子体系解析:Turn-End 守卫、PreToolUse 保护、会话启动与监视器监督的职责边界 【免费下载链接】firstmateTalk to one agent. Ship with a crew.项目地址https://gitcode.com/gh_mirrors/fi/firstmate点击查看免费下载本文基于 .agents/skills/harness-adapters/references/common/primary-hooks.md 展开。这篇参考文档是 Firstmateharness-adapters技能中面向主会话primary操作的核心路由契约规定了在修改会话启动、回合结束处理、工具调用前保护、监视器监督与 secondmate 集成之前必须加载的权威职责边界。读完本文你将掌握 Firstmate 各主 HarnessClaude、Codex、OpenCode、Pi、omp、Cursor、Grok 等在这些生命周期钩子上的分工、每类钩子的语义所有权归属、验证要求以及如何在修改任何一类钩子时准确更新对应的工具事实与验证记录。一、定位primary-hooks 在 Firstmate 中的角色Firstmate项目描述为Talk to one agent. Ship with a crew.是一个以单个主代理为船长、以一组 crewmate/scout 工作树与 secondmate 为船员的多代理编排体系。harness-adapters技能.agents/skills/harness-adapters/SKILL.md是其唯一技能、触发与路由所有者而references/common/primary-hooks.md是其中专门面向主会话primary的公共参考文件。根据技能内嵌的harness-adapter-routing-v1路由矩阵primary-hooks.md在以下操作场景被选中primary操作默认场景recovery场景下的secondmate与replacement-secondmate子场景verify场景验证一个新 harness 适配器。该文档的开篇即给出使用前提在修改会话启动session startup、回合结束处理turn-end handling、工具调用前保护pre-tool protection、监视器监督watcher supervision或 secondmate 集成之前必须以检测到的主 harness 的工具引用tool reference加载本文件。工具引用建立的是该身份的实证路径empirical path或其不支持边界unsupported boundary——即对每个 harness要么存在经过实证验证的接入方式要么明确标记为不支持绝不臆造。四个核心领域——Turn end、Pre-tool protection、Session start、Watcher supervision——各自有独立的文档所有者ownerprimary-hooks 本身不重复实现而是作为路由表指出每个主题的权威契约、底层脚本与验证记录位置。下面逐节展开。二、Turn End谁拥有不盲目结束回合契约2.1 权威所有者primary-hooks.md明确指出docs/turnend-guard.md拥有 no turn ends blind回合不得盲目结束契约、钩子安装方式、各 harness 表面surface的阻塞行为以及钩子无法阻塞时的权衡。docs/turnend-guard.md是这份契约的可读权威文档其核心不变量invariant是在 primary 自己的回合边界上当以下两个条件同时成立时守卫必须行动存在需要监督的工作在途任务、process-event 源、注册的自定义检查、或 Relay 轮询没有身份匹配的监视器持有新鲜的心跳信标state/.last-watcher-beat。守卫行动时harness 集成必须二选一阻塞回合结束或强制一次有界的跟进使用会话启动协议中发出的恢复指令。相关PreToolUse守卫docs/arm-pretool-check.md、docs/cd-guard.md、docs/subagent-guard.md负责在命令执行前拒绝不安全命令与回合结束守卫职责互补、互不僭越。2.2 底层谓词与实现回合结束守卫的谓词位于bin/fm-turnend-guard.sh与原生会话启动适配器共享主会话范围判定库bin/fm-primary-scope-lib.sh。守卫依次检查主会话范围 → 监督需求 → 监视器健康。从bin/fm-wake-lib.sh源码可见其关键谓词fm_watcher_healthy state-dir watch-path [grace-seconds] [home]约 L162 起PID 严格、身份匹配的锁加新鲜信标检查与bin/fm-watch-arm.sh使用的原语相同。守卫在回合边界必须使用这种严格检查因为此时 auto-arm 正在为即将到来的空闲期拉起新监视器守卫要与这次 arm 协作而非信任上一周期遗留的信标。fm_poll_derived_graceL123由轮询周期推导的宽限公式max(300, FM_POLL 60)是FM_GUARD_GRACE默认 300 秒之外的第二条宽限来源用于长轮询如FM_POLL300下避免健康监视器被误判为陈旧。fm_afk_daemon_owns_supervisionL340当state/.afk存在away/quiet 模式时守护进程bin/fm-supervise-daemon.sh以单次监视器方式接管监督该证明要求state/.afk存在且state/.supervise-daemon.lock命名的 pid 仍是身份匹配的活进程。2.3 各 Harness 的集成方式docs/turnend-guard.md的 Harness integrations 一节给出完整矩阵Harness回合结束钩子强制执行方式Claude.claude/settings.json中两个Stop钩子以退出码 2 阻塞与 Stop auto-arm 协作Codex.codex/hooks.json中Stop钩子以退出码 2 阻塞OpenCode.opencode/plugins/fm-primary-turnend-guard.js中session.idle被动回调调度一次跟进Pi.pi/extensions/fm-primary-turnend-guard.ts中agent_settled被动回调调度一次跟进omp.omp/extensions/fm-primary-turnend-guard.ts中session_stop阻塞钩子强制一次继续Cursor.cursor/hooks.json中stop钩子无法阻塞改为 park 并至多返回一次跟进Grok.grok/hooks/fm-primary-turnend-guard.json中Stop钩子原生阻塞或一次遗留grok --resume回退值得注意的实现细节Claude注册两个Stop钩子bin/fm-turnend-guard.sh --claude与bin/fm-claude-stop-autoarm.sh带asyncRewake: true、timeout: 28800。Claude 模式忽略stop_hook_active、与 Stop 拥有的 auto-arm 协作并等待FM_CLAUDE_AUTOARM_SYNC_WAIT_MS默认 800ms配合state/.claude-autoarm-epoch生成声明generation claim与有界阻塞预算FM_CLAUDE_TURNEND_BLOCK_BUDGET默认 3。Cursor parkCursor 的 blocked-response mapper 对stop步骤返回空对象退出码 2 是静默无效的因此bin/fm-turnend-guard-cursor.sh从不退出 2而是park运行bin/fm-watch-arm.sh作为受跟踪子进程、保持边界打开直到监视器关闭、返回一次可操作的watcher类跟进。双重循环上界Cursor 自身的loop_limit与 Firstmate 内部的FM_CURSOR_TURNEND_LOOP_CEILING默认 180刻意低于loop_limit。Grok依据每次Stop载荷做出唯一一次能力决策优先布尔stopHookActivecamel-case 优先兼容stop_hook_active两者皆缺时保留一次grok --resume遗留回退由GROK_TURNEND_GUARD_ACTIVE守护且省略--permission-mode。bin/fm-turnend-guard-grok.sh负责能力选择。pi-code 兼容层pi-code 加载project/.claude/settings.json且无asyncRewake因此bin/fm-claude-stop-autoarm.sh在 pi-code 交付的载荷上主动 stand down判别依据是载荷自身的transcript_path含/.pi/路径分量而非环境变量。2.4 验证要求与测试证据primary-hooks.md强调任何回合结束改动必须在 scratch 项目或一次性 home 中对照真实 harness 验证然后更新其可执行文件/钩子所有者、简洁工具事实以及 docs/verification/supervision.md 中 Turn-end guard 条目下的活跃实证记录。docs/verification/supervision.md记录了 2026-07-08 至 2026-09-21 跨七个 harness 的验证Claude 2.1.278、Codex 0.142.1、OpenCode 1.17.6、Pi 0.80.5、omp 18.1.11、Grok 0.2.112/0.2.73、Cursor 2026.08.11-e8db854。回归覆盖由以下测试承担tests/fm-turnend-guard.test.sh谓词、主/secondmate 范围、子工作树排除、FM_HOME/FM_STATE_OVERRIDE优先级、--claude协作等待、单调失败 epoch 递进、有界 attended fail-open、五个 primary 注册、Grok 原生/遗留选择等tests/fm-turnend-foreign-owner-arm-fix.test.sh外部 live owner 阻止 arming、非 owner Stop 安全诊断退出tests/fm-guard-stale-banner.test.sh各监督模型下的 pull-guard 谓词与横幅措辞tests/fm-cursor-primary.test.sh与FM_CURSOR_PRIMARY_LIVE_E2E1 tests/fm-cursor-primary-live-e2e.test.shCursor park 端到端可选实时路径FM_PI_LIVE_E2E1 tests/fm-pi-primary-live-e2e.test.sh、FM_OMP_LIVE_E2E1 tests/fm-omp-primary-live-e2e.test.sh。三、Pre-Tool Protection拒绝监视器 arm 反模式3.1 权威所有者与语义所有者受支持的主 harness 会在命令执行前拒绝监视器 arm 反模式包括 shell后台、截断管道truncating pipes、打包bundling与宽泛的pkill -f fm-watch。docs/arm-pretool-check.md拥有钩子命令、输出怪癖output quirks与实证记录工具引用命名的是集成形式。docs/arm-pretool-check.md进一步明确了双层所有权bin/fm-arm-command-policy.mjs是唯一的语义所有者导出分词器与命令位置分析cd-guard座带也复用它而非重复实现 shell 词法分析bin/fm-arm-pretool-check.sh只是稳定的 harness 传输层与输出渲染器。3.2 传输形式与 fail-open 行为bin/fm-arm-pretool-check.sh支持的入口形式Claude 与 Codexstdin JSON 的.tool_input.commandGrokstdin JSON 的.toolInput.commandOpenCode、Pi、pi-signed、omp--command exact string--background仅兼容字段绝不改变判定--claude保留 Claude 仅 stderr 拒绝的要求Claude 在 stdout 非空时忽略 PreToolUse 拒绝。快速放行fast path只在命令即便经分类器解码器归一化后也不可能包含fm-watch字节序列时触发且要求原始命令不带引号解码标记ANSI-C$...或 bash locale$...。畸形/空 stdin、无效 JSON、stdin 传输缺jq、缺 Node、缺分类器或分类器响应无效时全部 fail open退出 0 且无输出防止损坏的钩子拒绝所有 shell 工具调用。3.3 受保护脚本与稳定原因码命令位置中归一化路径后缀匹配以下受保护脚本即构成受保护执行bin/fm-watch-arm.sh (arm; blessed entry point) bin/fm-watch-checkpoint.sh (checkpoint; blessed entry point) bin/fm-watch.sh (watch; protected but never blessed)相对形式、以code-root锚定的绝对形式、任何以/bin/script结尾的词都解析为该身份静态引号形式普通引号、ANSI-C、bash locale在后缀匹配前先被 cook。bin/fm-watch.sh直接执行永远以watcher-direct拒绝。每个语义拒绝都带一个稳定原因码reason code是测试与适配器的稳定契约代码含义watcher-background受保护执行在异步列表或使用nohup/disownwatcher-pipeline受保护执行参与任意管道watcher-redirection受保护执行使用 shell 重定向watcher-bundled外层命令列表不是受祝福的 setup-plus-final 树watcher-nested包装器、组、替换、嵌套 shell、eval或构造的动态载荷执行受保护命令broad-watcher-kill实际执行的宽泛进程杀死针对监视器unclassifiable-protected-command畸形或不支持的语法含受保护命令且无法安全分类watcher-direct直接执行bin/fm-watch.sh监视器必须经bin/fm-watch-arm.sh或bin/fm-watch-checkpoint.sh到达3.4 内建委托的护栏subagent-guardprimary-hooks.md特别强调主 harness 还必须应对内建委托在 Firstmate 持久记录之外创建工作的可能。Claude 的已验证委托守卫在references/harness/claude.mddocs/subagent-guard.md拥有其完整契约、本地加固、逃生舱口与逐 harness 适用性审查。绝不能在缺乏实时证据的情况下泛化 Claude 工具名或权限。docs/subagent-guard.md的 shipped 机制是bin/fm-subagent-pretool-check.sh按形状shape而非固定清单分类工具名。当归一化小写名包含以下任一词干即视为委托形状agent subagent task workflow cron schedul worktree delegate spawn dispatch handoff remote sendmessage monitor三个排除集防止误报mcp__前缀永不分层OBSERVE_ONLY_TOOLStaskoutput、taskstop、taskget、tasklist、cronlist、bashoutput、killshell观察/停止已有工作PLAN_ONLY_TOOLStaskcreate、taskupdate只写会话本地待办清单。唯一逃生舱口是FM_ALLOW_SUBAGENT1环境变量必须在 harness 进程启动时存在因此会话内工具调用无法伪造对所有其他值含空、0、yes、true失败关闭。Claude primary 还应使用未跟踪的逐 home 本地permissions.deny清单作为加固将已知 Claude 委托工具从模型 schema 中整体移除但该清单不得进入受跟踪的.claude/settings.json——因为它仅 Claude 专属且跟踪的项目设置会传播进链接工作树、解除合法 crewmate 的武装。四、Session StartRun 层与 Nudge 层4.1 行为所有者AGENTS.md第 3 节与session-start-recovery技能是行为所有者docs/sessionstart-nudge.md拥有原生层级分配native tier assignment、传输、源路由source routing、运行时上界与 fail-open 行为。修改会话打开行为前必须先读它docs/verification/supervision.md中 Native session-start delivery 条目拥有活跃的带日期实证。4.2 两层架构docs/sessionstart-nudge.md定义了两种会话打开层tier层是 harness 表面的属性而非 home 的属性层适配器做什么使用者Run通过原生会话打开适配器执行bin/fm-session-start.sh并在首轮前把其有序 digest 门控进模型上下文Claude、codex exec、Pi/pi-signed、omp、CursorNudge通过原生适配器或受跟踪的会话启动指令要求 agent 自行运行 digestGrok、OpenCode以及路由到 nudge 的 run 层源Run 层存在的理由nudge 只能请求agent 可以推迟指令而通过原生适配器运行 digest 消除了这种自由裁量。bin/fm-sessionstart-run.sh是会话打开源意味着什么的唯一所有者bin/fm-sessionstart-nudge.sh与它共享bin/fm-gate-refuse-lib.shno-mistakes gate 静默与bin/fm-primary-scope-lib.sh与bin/fm-turnend-guard.sh共享同一个 primary 检测所有者。4.3 源路由源动作原因startup、new完整 digest真正的会话启动、尚未掌舵clear、compact在已证明完整启动后--reemit否则完整 digest此进程通常已掌舵、只丢失上下文但早期钩子可能在获取锁后被截断resume、reload、fork委托给 nudge 包装器先前上下文已恢复不可读或未识别完整 digest冗余掌舵便宜且幂等会话打开整体受bin/fm-session-start.sh上界约束默认FM_SESSION_START_TIMEOUT120 秒纯 Bash 进程组看门狗回退保证任何受支持主机都不会无限运行网络工作全部移出阻塞路径由bin/fm-startup-network.sh在独立有界 deferred 阶段执行每个任务端点的存活读取串行运行于各自崩溃隔离子进程受FM_SESSION_START_ENDPOINT_TIMEOUT默认 10s约束。子进程提前退出时父进程打印STARTUP TRUNCATED横幅命名未完成阶段与是否命中上界但父进程仍退出 0。4.4 传输细节Claude.claude/settings.json注册一个未匹配SessionStart钩子经CLAUDE_PROJECT_DIR调用、180s 超时原生 stdout 上下文注入受支持。Codex exec.codex/hooks.json锚定到钩子进程工作目录、验证 Firstmate 形状的钩子承载根、以 180s 超时把载荷管道进包装器。Codex 交互 TUI无受跟踪传输0.146.0 不触发项目SessionStart钩子。Pi/pi-signed受跟踪传输是.pi/extensions/fm-primary-turnend-guard.ts把session_start原因startup/new/resume/fork映射到包装器源Pi 是唯一注入消息而非钩子 stdout 的适配器注入内容必须携带操作出处U2063 FIRSTMATE_OP:前缀且消息交付保留至多 512 KiB超出时附加PI SESSION-START DELIVERY TRUNCATED标记。OpenCodenudge 层.opencode/plugins/fm-primary-sessionstart-nudge.js监听session.created、每会话 id 一次、仅当包装器打印 nudge 时调用client.session.promptAsyncheadlessopencode run刻意 fail-open。Groknudge 层.grok/hooks/fm-primary-sessionstart-nudge.json注册项目SessionStart钩子经内联默认值${GROK_WORKSPACE_ROOT:-}调用Grok 当前丢弃钩子 stdout因此该路径刻意 fail-open。Cursorrun 层.cursor/hooks.json注册sessionStart经$CURSOR_PROJECT_DIR、180s 超时、bin/fm-sessionstart-cursor.sh载荷无source字段注册本身提供--sourcedigest 作为additional_context返回。项目钩子仅在以--trust启动时加载。omprun 层.omp/extensions/fm-primary-turnend-guard.ts自动发现、无信任门session_start无原因字段已验证 18.1.11源按 Cursor 先例推导进程首启为startup携带--continue/--resume为resume、进程内后续启动/new、/resume、/fork为clear。五、Watcher Supervision只跟随渲染出的协议5.1 一条协议原则bin/fm-session-start.sh为检测到的主 harness 恰好打印一个协议块block只跟随渲染出的那一份协议。这与AGENTS.md第 8 节的纪律一致不要替换另一 harness 的等待形状wait shape也呼应了bin/fm-busy-lib.sh作为语义忙碌semantic busy所有者、工具引用只命名其来源与证据的定位。各 harness 的协议位于docs/supervision-protocols/如 docs/supervision-protocols/claude.md 的 Claude Stop-hook-owned supervision 模式先bin/fm-wake-drain.sh排干、然后由 StopasyncRewake钩子拥有例程 arm/re-arm、在Stop hook feedback唤醒上先排干再处理、等待钩子拥有的周期是静默的唤醒协议由bin/fm-supervision-instructions.sh渲染。5.2 修改监视器适配器的要求修改监视器适配器时primary-hooks.md要求更新docs/supervision-protocols/下对应 harness 的协议文件若共享的空闲idle或回合结束行为发生变化更新docs/turnend-guard.md刷新工具事实tool fact。没有专用协议的 harness 身份使用其文档化的不支持边界或未知边界unknown boundary绝不从相似 TUI 臆造一个。这正是docs/supervision-protocols/unknown.md存在的原因也对应harness-adapters技能中在unknown上询问船长而非猜测的安全规则。5.3 协议内容示例Claude以 docs/supervision-protocols/claude.md 为例渲染出的协议要点每次需要监督的回合结束都会由 StopasyncRewake钩子启动或挂接一个 home 作用域监视器周期无需模型命令、不消耗模型 token可操作的关闭以钩子的退出码 2 重唤醒Stop hook feedback送达普通唤醒后不要运行bin/fm-watch-arm.sh——下次回合结束在仍需监督时自动重新 arm收到自动机制失败通知firstmate watcher auto-arm FAILED ...时排干、检查机制失败不要把通知变成重复手动 arm 循环回合结束守卫bin/fm-turnend-guard.sh --claude是最终后备在 Stop 边界要求 PID 严格活监视器 新鲜信标谓词Claude 专属外部 live owner 安全退出除外。六、贯穿四条主线的纪律验证、证据与绝不臆造primary-hooks.md通篇贯穿着几条共同纪律这也是修改 Firstmate 主 harness 生命周期时必须遵守的工程准则改前加载改后验证任何变更先以检测到的 primary 的工具引用加载本文档任何回合结束或 pre-tool 改动必须在 scratch 项目或一次性 home 中对照真实 harness 验证后才可信docs/arm-pretool-check.md 记录了 2026-07-09 五个 harness 的现场验证矩阵及其版本Claude Code 2.1.206、codex-cli 0.144.0、grok 0.2.93、OpenCode 1.17.15、Pi 0.80.5。单一语义所有者每个机制只有一个权威所有者——回合结束守卫是bin/fm-turnend-guard.sh与docs/turnend-guard.mdarm 座带是bin/fm-arm-command-policy.mjs与docs/arm-pretool-check.md会话启动路由是bin/fm-sessionstart-run.sh与docs/sessionstart-nudge.md委托守卫是bin/fm-subagent-pretool-check.sh与docs/subagent-guard.md。工具引用只命名来源与证据绝不替代语义所有者。无证据不改边界受支持 harness 的边界由实证建立未验证的 harness 要么走文档化的不支持边界要么明确询问船长unknown绝不从相似 TUI 推断。证据入册每条活跃实证都记录在 docs/verification/supervision.md 的对应条目下Turn-end guard、Native session-start delivery并带有版本、日期与可复现命令回归由 tests/fm-turnend-guard.test.sh、tests/fm-arm-pretool-check.test.sh、tests/fm-sessionstart-nudge.test.sh、tests/fm-cursor-primary.test.sh 等可移植套件与FM_*_LIVE_E2E1可选实时守卫共同承担。七、总结primary-hooks.md是 Firstmate 主会话生命周期钩子的路由中枢它不重新实现任何机制而是精确指出回合结束找docs/turnend-guard.md、工具保护找docs/arm-pretool-check.md、会话启动看AGENTS.md第 3 节与docs/sessionstart-nudge.md、监视器协议跟随bin/fm-session-start.sh渲染出的那一份、验证记录在docs/verification/supervision.md并强加两条底线——绝不替换另一 harness 的等待形状、绝不为无专用协议的 harness 臆造协议。理解这张路由表是安全修改 Firstmate 主会话行为、或为新的 harness 适配器做验证verify工作的前提对应的可执行实现与回归测试则分布在bin/下的守卫脚本与tests/下的各套件中供读者按需深入。赞分享【免费下载链接】firstmateTalk to one agent. Ship with a crew.项目地址https://gitcode.com/gh_mirrors/fi/firstmate点击查看免费下载相关推荐Firstmate 中的 Grok Build Harness 适配启动、操作事实、Composer 识别与 Turn-End 守卫集成指南Firstmate 中的 Grok Build Harness 适配启动、操作事实、Composer 识别与 Turn End 守卫集成指南 Grok BuiFirstmate 主会话委派守卫解析用 PreToolUse 工具形状分类拦截绕开 Fleet 的委派Firstmate 主会话委派守卫解析用 PreToolUse 工具形状分类拦截绕开 Fleet 的委派 导读 本文以仓库文档 docs/subagent gDeepSeek Harness 源码启动与仓库构建分离pnpm dsh、pnpm run build 与 dev:web 的职责边界DeepSeek Harness 源码启动与仓库构建分离 pnpm dsh 、 pnpm run build 与 dev:web 的职责边界 导读 DeepS人工智能AI AgentAgent 框架DeepSeek上一篇如何零成本解锁MobaXterm专业版功能Python密钥生成工具完全指南下一篇Windows端口转发终极指南PortProxyGUI图形化管理工具完整教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进