Agent-first 终端控制面深入报告:从 PTY、状态感知到多 Agent 编排
AI 时代的 terminal 正在发生一次职责上移:它不再只渲染字符、托管 shell 或复用 pane,而是在尝试理解“哪个 Agent 正在做什么、谁需要人、怎样被程序控制、重启后怎样继续”。这已经是控制面,只是它主要控制的是本地执行,而不是组织承诺。

信息图 A|从 terminal data plane 到 Agent-first control 的能力阶梯;最上层仅用于标示产品能力边界,不表示 terminal 与某种外部控制面存在固定的部署关系。原创分析图;生成记录见 ImageGen Manifest。
研究范围:主样本为固定提交的 cmux 17466308 与 Herdr d6dae883;Ghostty、Warp/Oz、Orca 与 tmux 只用于能力边界对照。完整文件级证据、版本、链接和未验证项见 Source Manifest;研究问题与验收标准见 Research Spec。
文中使用三种措辞:
- 实现事实:固定提交源码、同提交文档或官方文档直接支持;
- 跨项目归纳:cmux、Herdr 或其他对照样本出现相似职责,但对象名与实现不同;
- 设计建议:本文为自建系统提出的接口与模块,不代表任何项目已经采用。
先给结论
如果只记住一个公式,可以记住:
Agent-first terminal = 可持久的 PTY 容器 + Agent 语义层 + 注意力路由 + 可编程控制接口 + 分级恢复。
它与传统 terminal、tmux 和完整 Agent 产品控制面之间的区别,不在于“是否有 AI 按钮”,而在于控制对象逐级变化:
Terminal 控制字符、屏幕和输入输出
Multiplexer 控制 PTY、pane、布局和进程存活
Agent terminal 控制 Agent 身份、状态、注意力、Session 与自动化
Product plane 控制任务意图、owner、依赖、预算、权限、验收与审计本次源码研究得到十二个核心判断:
- Agent-first 的关键不是内置一个聊天模型,而是 terminal 开始维护 Agent 语义。 能区分 plain shell 与 Agent、识别
working / blocked / done / idle、保存原生 Session 引用,并把状态向上聚合,才算跨过分界线。 - terminal 控制面首先是边缘控制面。 它最擅长掌握 PTY、进程、cwd、屏幕、输入和本地 Session;它通常不知道任务为何存在、谁承诺了什么、什么叫验收通过。
- 可编程执行容器不是“暴露一个 pane ID”就结束。 当 terminal 希望让人、脚本和其他 Agent 共同控制一个执行单元时,它至少要提供可寻址、可观察、可驱动、可等待、可恢复、可订阅的接口,并有明确的信任边界。
- 最难的不是启动 Agent,而是回答“现在是什么状态,谁说了算”。 进程名只能证明身份,屏幕与 OSC 容易误判,hooks 可能漏报;一个可靠实现必须显式仲裁状态来源。
- cmux 与 Herdr 代表两条不同路线。 cmux 是原生 macOS GUI:以稳定 surface、语义事件日志、通知、browser 和大范围 CLI/socket primitives 构建控制面;Herdr 是 server-owned PTY multiplexer:以 foreground process、screen manifests、单一 hook authority 和强 Agent API 构建语义运行时。
- “未读”是 Agent 状态之外的第二维。 Herdr 用
state + seen把已经完成但尚未看过的 idle 映射为 done;cmux 用 notification queue/unread/ring 把需要注意的 pane 提升到 workspace。这个维度无法只靠 Agent 生命周期表达。 - read / send / wait 比“打开很多 pane”更接近编排。 只有稳定选择器、结构化读取、状态等待、事件订阅和超时,另一个 Agent 才能可靠地驱动目标 Agent;模拟键盘仍然是最弱的一环。
- 恢复不是一个功能,而是四种不同连续性。 detach 保留原进程;snapshot 只重建布局;native resume 重建 Agent 对话;live handoff 转移 PTY/进程。把它们都叫“Session restore”会制造错误预期。
- Agent Session ID 不是进程,也不是 pane。 同一 pane 可以运行不同进程;同一原生 Agent Session 不应被两个 pane 同时恢复;layout identity、runtime identity 与 conversation identity 必须分开。
- terminal 已经可以协调 Agent,但通常还不能治理 Agent。 cmux Teams 和 Herdr 的 Agent API 能创建 pane、发 prompt、等待状态、收集结果;但 owner、依赖、产物验收、成本与审计仍应在上层控制面。
- 本地并不自动安全。 socket 控制、输入注入、屏幕读取、scrollback 持久化、Session ID、自动 resume command 与 SSH 转发都跨越信任边界;控制接口本身等同于代码执行能力。
- 最值得抄的不是某个 UI,而是一组深模块。 Container Registry、Agent Adapter、State Arbiter、Attention Router、Session Ledger、Recovery Planner、Control API 与 Policy Gate 应彼此分离。
零、先把 Herdr 当成一款软件用
后文会出现 PTY、state authority、screen manifest、Session ref、event sequence 等概念。如果还没有形成产品画面,这些词很容易变成一堵实现细节墙。先暂时忘掉源码,把 Herdr 当成一个“专门同时看守多个 Agent TUI 的 tmux”用十分钟:打开一个项目,分成两个终端,各运行一个 Agent,只在侧边栏需要你时切回去,然后离开并重新连接。
1. 一屏看懂 Herdr

官方界面截图|Herdr 固定提交 d6dae883 的仓库资产,Apache-2.0;本地副本与 SHA-256 见 Source Manifest。
这张图可以分成五个区域来读:
| 界面区域 | 用户看到什么 | 它替用户记住了什么 |
|---|---|---|
| 左上 workspace 列表 | herdr、pi-extensions 等项目,旁边带状态点 | 哪些项目存在;哪个项目里有 Agent 仍在工作或需要关注 |
| 左下 agents 列表 | pi idle、claude working 等跨 workspace 汇总 | 不必逐 pane 轮询;可以直接跳到某个 Agent |
| 中央 pane | 一个完整 Agent 对话或普通 shell | 这里是真实终端,不是控制面重新摘要过的聊天记录 |
| 蓝色 pane 边框 | 当前焦点所在的 terminal | 键盘输入发给谁;哪个完成结果已经被用户“看过” |
| pane 内部 TUI | Codex、Claude 等 Agent 自己的输入框、工具调用和状态栏 | Agent 仍拥有自己的交互协议、上下文与权限 UI |
Herdr 并没有把 Claude、Codex 改造成自己的聊天组件。它保留每个 Agent 的原生 TUI,只在外围补上项目组织、稳定终端、状态汇总、跳转和自动化。这一点是理解全文的起点:Herdr 控制的是装着 Agent 的终端,而不是替代 Agent Runtime。
2. 十分钟完成第一次使用
以 macOS 或 Linux 为例,可以用 Homebrew 安装,然后从项目目录启动:
brew install herdr
cd ~/Projects/my-project
herdr官方也提供安装脚本、mise、Nix、Windows 和手动二进制方式;这里不展开成安装手册。第一次执行 herdr 时,它会启动或连接默认后台 Session;没有 workspace 时自动创建一个 workspace、一个 tab 和一个根 pane。你看到 shell 提示符时,已经同时拥有了 Herdr client、后台 server、workspace/tab/pane 拓扑和一个真实 PTY。
接下来可以完全用鼠标操作:在第一个 pane 运行 codex;右键选择向右分割;在新 pane 运行 claude;拖动分割线调整大小;点击左侧 Agent 或任一 pane 切换焦点。Herdr 从 foreground process 自动识别两个 Agent,并把状态显示到侧边栏。
如果更喜欢键盘,prefix 默认是 ctrl+b:先按下并松开 ctrl+b,再按动作键。先记六个就够:
| 用户动作 | 默认按键 | 鼠标等价操作 |
|---|---|---|
| 向右 / 向下分割 | prefix+v / prefix+minus | pane 右键菜单选择 split |
| 在 pane 间移动 | prefix+h/j/k/l | 直接点击 pane |
| 新建 tab | prefix+c | 右键创建 tab |
| 打开 workspace / Agent 导航 | prefix+w | 点击左侧项目或 Agent |
| 查看全部生效快捷键 | prefix+? | — |
| 分离 client、让进程继续 | prefix+q | 关闭当前终端窗口也可分离 |
这里最容易混淆的是最后一项:prefix+q 不是退出 Agent。它只让当前 TUI client 离开后台 Session;Herdr server、PTY、Codex 和 Claude 仍继续运行。稍后再次执行 herdr,会连接回原 Session。只有 herdr server stop 才会停止默认 server,并结束其中的 pane 进程。
3. 侧边栏状态就是主要交互
Herdr 的日常循环不是频繁切 pane,而是让 Agent 在后台工作,只根据侧边栏决定下一眼看哪里:
| 状态 | 界面语义 | 用户通常做什么 |
|---|---|---|
working | Agent 仍在运行 | 继续做别的事,不必进入 pane 轮询 |
blocked | 检测到审批、问题或输入请求 | 跳到该 Agent,阅读原生 TUI 后作决定 |
done | Agent 已经停下来,但完成后尚未被查看 | 切过去检查结果、diff 或测试 |
idle | Agent 已停下来,而且相关 pane 已被查看 | 可以继续输入新任务,或暂时忽略 |
unknown | 已识别 Agent,但无法可靠判断生命周期 | 仍按普通 terminal 使用,不把状态点当事实 |
done → idle 很能说明为什么这类产品不只是给进程加颜色:Agent 底层可能一直都是 idle,变化的是 seen / unseen。当后台 Agent 停下来,Herdr 显示 done;当你聚焦该 tab/pane 后,它才成为 idle。仅用 CLI 读取输出不会把它标成已看,界面焦点才会改变 attention state。workspace 旁的状态点则是内部 Agent 状态的优先级汇总,而不是另一个独立任务状态。
4. 同一套能力有三种入口
Herdr 的特殊之处不是另做了一套“自动化模式”,而是鼠标、快捷键、CLI 和其他 Agent 最终操作同一份 server-owned 状态:
| 入口 | 适合谁 | 典型动作 |
|---|---|---|
| TUI / 鼠标 | 人类日常工作 | split、focus、resize、查看 blocked/done、直接回答权限问题 |
| CLI / socket API | 脚本与外部工具 | 查询拓扑、读屏幕、发输入、等输出或 Agent 状态 |
| Herdr Skill | 运行在 Herdr pane 内的另一个 Agent | 创建 helper pane、分配工作、等待、收集结果 |
先用只读命令观察界面背后的对象即可:
herdr workspace list
herdr agent list
herdr agent get w1:p2
herdr agent explain w1:p2
herdr agent wait w1:p2 --until blocked --until done --timeout 120000
herdr agent read w1:p2 --source recent-unwrapped --lines 80实际使用时,应从 workspace list、pane list 或 agent list 的返回值取得 ID,不要照抄 w1:p2 猜目标。agent explain 用来回答“为什么它显示这个状态”;agent wait 等语义状态;agent read 读取目标 Agent 的真实终端内容。这三个命令分别对应后文的可解释性、可等待性和可观察性。
5. 把一次操作映射到后文概念
下面这张表是全文的阅读索引。后文每个看似抽象的概念,都可以还原成刚才经历过的一个界面动作:
| 刚才的用户动作 | 界面结果 | 内部真正发生的事 | 后文对应 |
|---|---|---|---|
在项目目录执行 herdr | 出现 workspace 和根 pane | client 连接后台 Session;server 创建/恢复 workspace、tab、pane 与 PTY | 第二、六章 |
右键或 prefix+v 分割 | 多出一个真实 terminal | layout registry 创建新 pane identity 与 PTY | 第二、三章 |
在 pane 中执行 codex / claude | 侧边栏出现 Agent 名称与状态 | foreground process detection 把当前 pane occupant 识别成 Agent | 第四、六章 |
| 后台 Agent 停下来 | workspace/Agent 出现 done | lifecycle state 与 seen 合成 attention state,再向上汇总 | 第二、四、六章 |
点击 done Agent | 跳到正确 pane,状态变 idle | stable pane/Agent selector 完成寻址;focus 更新 seen | 第二、六章 |
prefix+q 后再次执行 herdr | 回到原进程与原屏幕 | client detach/attach;原 server 和 PTY 从未停止 | 第六、十章 |
| server 真正重启 | 布局回来,但任意进程未必还在 | snapshot 重建 topology;受支持 Agent 可用 native Session ref 启动新进程继续对话 | 第十章 |
CLI read / wait / send | 脚本也能控制同一个 pane/Agent | socket API 在 identity guard、state sequence 与 policy 下操作 server registry | 第三、六、九、十一章 |
如果先记住这条用户旅程,后文就不是八组陌生模块,而是在回答八个具体问题:Herdr 怎么找到正确 pane?状态点为什么可信?done 为什么会随查看消失?关窗口后进程为什么还在?重启后又究竟恢复了什么?脚本为什么能等待 Agent 而不只是盲发键盘?
一、为什么半年内会出现这么多“新 terminal”
Claude Code、Codex、Gemini CLI、Pi、Hermes、OpenCode 等 Agent 把大量软件开发重新拉回 terminal。原因不是 terminal 比 GUI 更现代,而是它已经拥有 Agent 最需要的几个原语:任意命令、真实文件系统、现有凭据、编译器和调试器、SSH、管道、脚本,以及可以长时间运行的交互式进程。
但传统 terminal 的对象模型仍停留在“人盯着一个前台程序”:
- terminal emulator 解析 VT/ANSI 字节流并渲染屏幕;
- shell 启动命令;
- tmux/Zellij 之类 multiplexer 让 PTY 和 pane 在客户端离开后继续存在;
- 用户自己记住哪个 pane 在做什么,并轮询它是否需要输入。
单 Agent 时,这些缺口可以靠人的短时记忆掩盖。十几个并行 Agent 出现后,人遇到的是一个调度问题:
- 哪个 Agent 正在工作,哪个已经完成?
- 哪个停在权限确认、问题或计划审批?
- 哪个输出还没看过?
- 当前 pane 属于哪个 repo、branch、worktree 和原生会话?
- 能否从脚本或另一个 Agent 读取、发送、等待,而不是人工切 pane?
- app、server 或机器重启后,到底能继续什么?
所以这批产品虽然外形差异很大——native terminal、TUI multiplexer、worktree IDE、cloud agent shell——却在共同补一层东西:把“多个黑盒 PTY”提升为“多个有身份、有状态、可寻址、可恢复的 Agent 执行单元”。
1. 能力阶梯
| 层级 | 控制对象 | 解决的问题 | 典型能力 | 典型代表(主要覆盖) | 仍然不知道什么 |
|---|---|---|---|---|---|
| L0 Terminal data plane | 字节、cell、screen | TUI 能否正确显示与输入 | VT/ANSI、字体、GPU 渲染、clipboard | Ghostty / libghostty、xterm.js、Alacritty | Agent 身份与任务 |
| L1 Multiplexer | PTY、pane、session | 进程能否持续、布局能否复用 | split、detach/attach、scrollback | tmux、Zellij、GNU screen | 谁需要注意 |
| L2 Attention control | notification、seen/unread | 人应先看哪里 | ring、badge、jump、rollup | cmux notification rings / unread、Orca Needs You / done-unread | 可靠生命周期 |
| L3 Agent semantics | Agent、phase、session ref | pane 里是谁、在做什么 | hooks、manifest、working/blocked/idle | Herdr process/screen/hook 状态权威、cmux Agent Journal、Orca Agent hooks | 业务验收 |
| L4 Programmable container | selector、command、event | 人/脚本/Agent 能否统一控制 | read/send/wait/subscribe/resume | Herdr Agent/socket API、cmux CLI/socket/browser API、Orca CLI | 组织承诺 |
| L5 Product control plane | task、owner、dependency、budget | 为什么做、谁负责、是否完成 | queue、lease、approval、audit | Warp / Oz | — |
这里的“典型代表”不是互斥的产品分类,而是用来说明每一级新增了什么职责。同一产品通常纵向跨层:cmux 从 libghostty 数据面一直覆盖到可编程 API,Herdr 从持久 PTY 覆盖到 Agent/socket API,Orca 从终端容器覆盖到 Agent 感知与 CLI,而 Warp / Oz 已进一步进入任务、委派和治理所在的产品控制面。L5 只是能力范围的对照项,不表示它与 L0–L4 必须组成一套上下层系统。
Ghostty 很好地说明了 L0 与上层的分工。官方把核心拆为跨平台、C ABI 的 libghostty,负责 terminal emulation、字体和渲染;GUI 承担平台原生 tabs/splits 等界面。cmux 直接复用 libghostty,把研发精力投入 workspace、通知、browser、Agent hooks 和控制 API。这不是“Ghostty 不够 Agent-first”,而是稳定 terminal core 让更上层的控制面可以独立生长。
二、通用对象模型:Agent 不等于 pane,也不等于 Session
不同产品命名不一致,但两个主样本呈现出相似的执行拓扑:
图 1|通用对象关系。它是跨项目归纳,不是 cmux 或 Herdr 的官方类图。
需要刻意分开四种身份:
- 布局身份:workspace、tab、pane/surface。用于找到“屏幕在哪里”。
- 运行身份:PTY、foreground process group、PID。用于判断“什么在运行”。
- Agent 身份:claude、codex、pi 或自定义 label。用于判断“谁在运行”。
- 会话身份:Agent 自己的 conversation/session ID 或 path。用于判断“重启后继续哪段上下文”。
这四者的生命周期不同。pane 可以活着但 Agent 已退出;一个新 Agent 可以复用旧 pane;app 重启后 pane 被重建但原进程已不存在;native resume 可以在新 PID 中延续旧 conversation。把它们压成一个 session_id,几乎一定会在恢复、并发或重绑定时出错。
Herdr 的实现已经把这种分离写进类型:PaneState 只保存 attached terminal 与 seen;TerminalState 保存 cwd、detected agent、fallback state、hook authority、native session reference、revision 和 launch argv;PaneAgentSessionSnapshot 再只保存 source / agent / kind / value。cmux 则把 CMUX_SURFACE_ID 当作 hook 归属与恢复绑定的关键身份,同时区分 workspace、surface 和 Agent native session。
done 为什么通常不是 Agent 原生状态
Herdr 的底层 AgentState 只有 Idle / Working / Blocked / Unknown。用户看到的 done 来自另一个事实:Agent 已 idle,但这个 pane 自状态改变后还没被用户看过。源码中 PaneState.seen 与 AgentState 分开保存,workspace 聚合优先级则把 blocked、unseen idle、working、seen idle、unknown 排序。
这揭示了一个通用设计:
Agent lifecycle 回答“机器在做什么”;attention state 回答“人是否处理过这次变化”。
把二者混在单一 enum 中,会让“完成但未读”“等待用户但已查看”“后台仍在工作”之间难以演化。
回到 Herdr 界面: 左侧的
claude working不是 pane 本身的名字;它是 Herdr 先用 pane 找到 PTY,再用 foreground process 识别 Agent,最后把有效生命周期与seen合成出来的视图。点击这一行时,Herdr 用稳定 Agent/pane identity 跳转,而不是按屏幕标题猜位置。
三、终端如何成为可编程的 Agent 执行容器
这里的“容器”只是功能比喻:它不是 Docker 容器,也不意味着把终端暴露到公网。它指 terminal 将一个承载 Agent TUI 的执行单元,通过稳定身份、结构化状态、命令、事件和恢复接口,开放给 UI、脚本、插件、远程客户端与其他 Agent 控制。这样的可编程执行容器需要七项契约。

信息图 B|可编程执行容器的七项契约。容器不是 pane 外观,而是一组跨生命周期接口。
| 契约 | 最低要求 | cmux 例子 | Herdr 例子 | 缺失后的故障 |
|---|---|---|---|---|
| Addressable | 稳定 ID、selector、当前上下文 | UUID、refs、CMUX_SURFACE_ID | workspace/tab/pane public IDs、agent name | 事件归错 pane;脚本依赖焦点 |
| Structured | workspace/tab/pane 拓扑可查询 | tree、list/create/move/split | workspace/tab/pane schema 与 layout API | 只能模拟快捷键 |
| Observable | 读 screen、process、cwd、state、revision | read-screen、top、sidebar state | pane.read、agent.get/explain | 只能盲发输入 |
| Actuatable | send text/key、focus、run、prompt | send、send-key、browser API | pane.send_*、agent.prompt | 无法自动处理交互 |
| Waitable | 状态/输出等待、timeout、identity guard | reconnectable events;通知流 | agent.wait、events.wait、output match | polling、竞态、误把旧结果当新结果 |
| Recoverable | layout、screen、process、Agent Session 分级恢复 | snapshot + native resume + trusted binding | detach + snapshot + native resume + handoff | “恢复”含义不清;重复恢复 |
| Policy-bound | socket ACL、command trust、secret handling | password/socket、signed resume prefix、env sanitize | local socket 权限、handoff token、history opt-in | 控制 API 变成无门槛 RCE |
真正的分界是 wait
send 很容易实现,本质是向 PTY 写字节;read 也可以从 emulator buffer 截取文本。但自动化是否可靠,取决于是否能表达:
- 等目标 Agent 进入一组语义状态;
- 只接受这次 prompt 之后发生的变化;
- 目标进程或 Session 被替换时立即失败;
- 超时返回结构化错误;
- 短暂状态不会因为轮询间隔而丢失。
Herdr 的 agent.prompt --wait 会先记录 state_change_seq,要求 prompt 后五秒内观察到活动,再等待目标状态;等待期间还会验证 terminal identity、Agent identity 与事件序列。这个实现已经比“每秒读取一次屏幕”可靠得多,但官方文档也明确提醒:它等待的是状态,不是严格的 turn correlation;如果目标本来就在 working,一个既有回合可能满足条件。
因此自建协议还应增加 turn_id / command_id / causation_id。状态等待解决“什么时候安静下来”,因果标识才解决“是不是我刚才发起的工作完成了”。
回到 Herdr 界面: 鼠标点击 pane、
prefix+w选择 Agent、CLI 的agent get/read/wait看似是三种交互,其实都需要同一项基础能力:先稳定找到目标,再对目标当前 occupant 做操作。七项契约就是把“这个界面很好用”翻译成可以实现和测试的系统属性。
四、状态权威:terminal 控制面最难的核心
状态识别通常有四类信号,它们回答的问题不同:
| 信号 | 能证明什么 | 不能可靠证明什么 | 典型风险 |
|---|---|---|---|
| Foreground process | pane 当前是否由已知 Agent 进程占用 | Agent 正在思考、等待还是完成 | wrapper/runtime 名称、子进程、进程切换 |
| Screen / OSC | 当前 TUI 最近显示了什么 | 完整生命周期、因果与长期身份 | 文本误匹配、历史内容、UI 版本变化 |
| Lifecycle hook/plugin | Agent 主动声明语义事件 | hook 未覆盖的路径、进程是否仍活着 | 漏报、乱序、旧 Session 延迟事件 |
| Native session reference | 可恢复哪段 Agent conversation | 当前运行状态、任务完成 | stale/duplicate ref、Provider 兼容变化 |

信息图 C|状态权威与注意力路由。身份、生命周期、会话连续性和是否已读是四类不同事实。
图 2|不要直接把任一输入信号写进 UI;先显式仲裁,再生成有效状态。
1. Herdr:一个 pane 只能有一个状态 authority
Herdr 先从 foreground process 识别 Agent;然后在两条状态路线中选一条:
- 对 Pi、OMP、OpenCode、Kimi、MastraCode 等具有完整 lifecycle integration 的 Agent,live hook/plugin 上报是 authority;同一时刻不再让 screen fallback 与它竞争。
- 对 Claude、Codex、Hermes 等只上报 Session 身份或 hook 不完整的 Agent,仍由 bottom-buffer screen manifest 与 OSC 信号判定状态,Session report 只负责恢复身份。
源码把仲裁集中在 TerminalState:HookAuthority 带 source / agent_label / state / reported_at / session_ref;设置 authority 时检查 source sequence、foreground Agent 冲突、Session owner 冲突、进程退出和旧 Session 延迟事件。完整 lifecycle authority 存活时,visible blocker 也不能反向覆盖它。
这比简单的优先级表更重要:authority 不是“hook 总比 screen 强”,而是与具体 Agent、当前 foreground process 和 Session ownership 绑定。 当进程退出或 Session 被替换,authority 必须释放;否则旧 hook 可以把一个已经变成 shell 的 pane 永久标成 working。
2. Screen detection 不是正则堆,而是小型感知系统
Herdr 的 screen route 包含几个容易被忽略的可靠性细节:
- 读取 recent bottom-of-buffer,而不是用户滚动后的 viewport;
- manifests 可以同时匹配 screen、OSC title 与 OSC progress;
- 把
visible_idle / visible_blocker / visible_working作为置信元数据,而不只返回 enum; - working → plain idle 需要短暂多次确认,避免 TUI 重绘瞬间抖动;
- Agent 自己的 transcript/history viewer 可以设置
skip_state_update,避免浏览旧文本改变 live state; agent explain暴露 manifest、匹配规则与证据,状态误判可以调试,而不是黑盒。
它仍然是脆弱适配层:Agent TUI 改文案、布局或 spinner,都可能让规则失效。Herdr 用可远程更新的 TOML manifests 和本地 override 降低升级成本,但这只是让 heuristic 可维护,并没有把它变成协议。
3. cmux:从 hook 事件到可重放的语义日志
cmux 当前源码的 Agent Journal 走了更事件化的路线:hook 不直接设置一个 sidebar 字符串,而是发出 sessionStarted / turnStarted / turnCompleted / approvalRequested / questionRequested / planReviewRequested / errorReported / sessionEnded 等语义事件。事件先持久写入 append-only journal,失败进入有界 dead-letter JSONL;reducer 再按 sequence 去重、丢弃 stale arrival,按 surface 与 Agent 聚合成 unknown / running / needsInput / idle / error。
图 3|cmux 的语义事件归约。未归属事件保留为 diagnostic,而不是猜一个 pane。
AgentLifecycleReducer 的关键不是 enum 本身,而是归约契约:结果只取每个 Session 最新的 lifecycle-bearing event,重复和乱序不改变结果;surface 上有多个同类 Session 时再按 precedence 合并。subagent 事件不会直接改写宿主 pane badge,因为子 Agent 完成并不等于父 Agent 完成。
与 Herdr 相比,cmux 更依赖 Agent hook 生态,但事件日志天然适合重放、诊断和多端 reconcile;Herdr 的 screen route 覆盖面更广,但适配成本与误判风险更高。两者不是谁完全取代谁,而是代表“主动协议”与“被动感知”的两端。
回到 Herdr 界面: 侧边栏的一个状态点并不是 Agent 直接画上去的。对 Codex/Claude,它可能来自“前台进程已识别 + bottom-buffer 命中 manifest + 稳定化”;对具有完整 lifecycle integration 的 Agent,它可能来自 hook authority。用户只看到统一状态,内部必须记住证据来自哪条路线。
五、cmux 源码剖析:原生 GUI 上长出的可组合控制面

信息图 D|左:cmux 的 native GUI / surface / event-journal 路线;右:Herdr 的 server-owned PTY / state-authority / Agent API 路线。
cmux 的 README 对自己的定位很克制:它是一个 primitive,不是一个规定工作流的 orchestrator。它提供 terminal、browser、notifications、workspaces、splits、tabs 和 CLI,让用户或 Agent 自己组合。这个定位与源码相符:控制面不是单一 AgentManager,而是多个可编排 subsystem 的组合。
1. 数据面:libghostty + native macOS UI
cmux 不是 Ghostty fork,而是把 libghostty 当 WebKit 式的渲染库。Swift/AppKit 管 workspace、sidebar、split、browser、notifications 和 session persistence;libghostty 负责 terminal emulation/rendering。这使“terminal correctness”和“Agent workflow”成为可独立演进的模块。
2. 容器身份:surface 是事件归属的锚点
每个 cmux terminal surface 注入 CMUX_WORKSPACE_ID、CMUX_SURFACE_ID、CMUX_TAB_ID 与 socket context。Agent 从这个 shell 中启动后自然继承 surface token;hook CLI 也可以通过显式参数、环境、TTY 或 process tree 把事件绑定回具体 surface。
这个设计解决了一个常见错误:不能用 cwd、tab title 或“最新 transcript 文件”猜某个 Agent 属于哪个 pane。多个 pane 可以在同一 repo、同一 cwd 运行相同 Agent;标题和 mtime 都不是 identity。cmux 对无法形成完整 target 的事件不强行归属,而是写成 unattributed diagnostic。
3. 控制接口:GUI 对象被系统化暴露
cmux 的 CLI contract 已经远超传统 terminal automation:
- 以 UUID、
workspace:2之类 ref 或 index 选择 window/workspace/pane/surface/tab; - 创建、移动、拆分、重排 workspace、pane 和 surface;
read-screen、send、send-key、process/resourcetop;- notifications、status pill、progress、log、sidebar state;
- browser open/navigate/snapshot/click/fill/evaluate;
- reconnectable NDJSON events 与 raw v2 RPC;
- native Agent sessions 列表、hooks、resume binding、teams/subagent panes。
这就是可编程执行容器的关键:GUI 的主要对象不只接受鼠标和快捷键控制,也能被 Agent 与脚本寻址、读取和驱动;CLI 不是旁路工具,而是同一 socket capability 的客户端。
4. Attention router:通知不是弹窗,而是可导航状态
cmux 同时接收标准 OSC 9/99/777、cmux notify 与 Agent hooks。通知经历 received、unread、read、cleared;pane 有 ring,workspace 有 badge,notification panel 保存队列,用户可以跳到最新 unread。它还抑制当前窗口/当前 workspace 已在看的桌面通知。
与传统 macOS notification 最大的区别是:通知携带 workspace/surface address,可以回到正确执行现场。可导航性把 notification 从信息变成了控制索引。
5. Browser 让容器跨出 PTY
cmux 的 browser pane 暴露 accessibility snapshot、element refs、click、fill 与 JS evaluation。对 Web 开发 Agent 来说,terminal 负责代码与进程,browser 负责运行结果与交互验收;二者都在同一 workspace/split 和 socket API 下。
这也是 cmux 与 Herdr 的重要差别:cmux 更像一个本地 Agent workbench,容器不止 terminal;Herdr 更像可嵌入/远程的 Agent-aware terminal runtime。
6. 恢复:先重建 layout,再运行受信任的 continuation
cmux 明确区分:
- app-owned state:window/workspace/pane layout、cwd、best-effort scrollback、browser URL/history;
- arbitrary process state:不 checkpoint;tmux、vim、shell 等默认只恢复成普通 terminal;
- supported Agent state:hook 捕获 native Session ID 后运行 Agent 自己的 resume command;
- custom surface binding:例如
tmux attach -t work,只有 process-detected trusted binding 或用户批准的签名 command prefix 才自动执行。
批准还绑定 cwd 和精确环境值;token、password、secret、API key 等敏感环境变量在持久化前被丢弃。这个机制揭示了恢复真正的安全含义:restore 不是读 JSON,而是在未来某个时刻执行一条命令。 Resume Ledger 必须同时是 Policy Ledger。
六、Herdr 源码剖析:把 multiplexer 变成 Agent runtime API
Herdr 从另一个方向出发。它的 server 拥有 pane 与进程状态,client 只是附着的 TUI;用户 detach 后 server、PTY 和 Agent 继续运行。Workspace 是项目容器,Tab 是布局,Pane 是真实 terminal,Agent 是 pane 中被识别的 foreground process,Session 是彼此隔离的 server namespace。
沿着“零章”的操作继续往下看: 启动
herdr对应 client/server;第一次看到的项目行对应 Workspace;右键分屏对应 Pane/PTY registry;运行codex对应 Agent detection;左侧状态点对应 authority + attention rollup;prefix+q对应 client detach。下面五小节只是把这几个可见动作逐层展开。
1. Server-owned terminal 把“用户离开”与“进程退出”解耦
传统 terminal window 一关,child process 往往随之结束;tmux 通过 server 解决这个问题。Herdr 继承这一模型,并把 Agent state、metadata、Session reference 与 API event hub 一并放到 server。多个客户端、本地 CLI 或 SSH thin client 因此看到同一组运行时事实。
图 4|Herdr 的 client/server 结构让 terminal runtime 成为共享服务,而不是某个窗口的内部状态。
2. Agent API 明确高于 Pane API
Herdr 把 layout、pane、Agent 定义成三种 primitive:
- layout 创建和组织位置;
- pane 操作 raw terminal:run、send、read、wait output;
- Agent 操作 recognized process:start、prompt、send keys、read、wait、focus、rename、explain。
agent.start 必须指定一个已有 shell pane,不会隐式创建布局。这个边界很值得抄:容器创建与 Agent 启动分开,自动化不会因为一次启动顺便重写 UI topology。
Socket schema 中 Agent 查询返回 terminal_id / agent / status / session / workspace_id / tab_id / pane_id / state_change_seq / cwd / foreground_cwd / revision;events 可以订阅 pane.agent_detected、pane.agent_status_changed、pane.output_changed 等;pane.report_agent 与 pane.report_agent_session 分开,使生命周期和可恢复身份不必绑在同一 hook 上。
3. 可解释的状态检测是适配器开发工具
Herdr 内置 22 类 Agent 身份,20 类 screen manifest;运行时识别 Node/Python/Bun/shell wrapper 内真实 argv,避免只看到 node 或 python。当状态错误时,agent explain 会显示最终 state、manifest source/version、matched rule、evidence 和为什么跳过 screen detection。
这使 Adapter 的职责从“写几条 regex”升级为一个可测试的感知包:
process matcher
+ screen / OSC manifest
+ optional lifecycle reporter
+ optional native Session reporter
+ explain fixture
+ resume command builder4. 状态向上聚合,本质是人类注意力调度
Herdr 的 workspace rollup 不是对所有 Agent 求平均,而是选最需要人的状态:blocked 最高;unseen idle 表示 done;working 其次;seen idle 再次;unknown 最低。用户无需逐 pane 轮询,只看 sidebar 就能决定下一次注意力切换。
这仍不是任务调度器。它不知道两个 working Agent 是否修改同一文件、blocked 是否影响关键路径、done 的产物是否通过测试。它优化的是人类下一眼看哪里,不是组织下一步做什么。
5. Live handoff 展示了 multiplexer 的独特上限
Herdr 的 experimental live handoff 会把 session snapshot 与 pane runtime state 发给新 server,并通过 Unix file descriptor transfer 交接 PTY;token、protocol/version validation、validated → restored → ready → committed → owned 握手避免两端同时认为自己拥有 runtime。
它能尽力保住原 pane 进程,因此比 native Agent resume 更强:不需要终止当前 Agent,也不需要重新执行 prompt。但它不能保住 in-flight API requests、waits、subscriptions、client sockets 和 pane-to-pane messages;调用方仍需 reconnect/retry。
这个边界说明:控制面的运行连续性与控制协议的请求连续性是两件事。 进程活着不代表某个 wait 还能收到结果;协议必须支持幂等、重订阅与快照对账。
七、cmux 与 Herdr:同一个问题的两种答案
| 维度 | cmux | Herdr | 可复用判断 |
|---|---|---|---|
| 产品形态 | native macOS terminal/workbench | 跨平台 TUI multiplexer + background server | UI 形态不决定控制深度 |
| Terminal core | libghostty | 自有 server-owned terminal runtime | 渲染层应与 Agent 语义解耦 |
| 基本容器 | window/workspace/pane/surface | session/workspace/tab/pane/terminal | 至少分 topology 与 runtime identity |
| Agent 识别 | hooks、env/TTY/process binding、Agent registry | foreground process + wrapper argv detection | 不能只看 title/cwd |
| 状态主路线 | semantic hook events → journal → reducer | lifecycle authority;否则 screen/OSC manifest | 需要一个可解释的 arbiter |
| Attention | notifications queue、ring、unread、jump | state + seen,pane→tab→workspace rollup | lifecycle 与 attention 分层 |
| Control API | 大范围 CLI/socket,另含 browser | 强类型 local socket API,Agent/pane/events/wait | public IDs + structured responses |
| 编排 | Claude/Codex teams、split、browser、events | agent start/prompt/read/wait、plugins | wait 与 causation 是自动化门槛 |
| 远程 | SSH workspace、remote daemon、iOS/设备能力 | server/client、SSH/thin client、named session | runtime owner 必须明确 |
| 普通重启 | layout + cwd + scrollback + browser | layout + cwd;history opt-in | snapshot 只恢复形状 |
| Agent 恢复 | hooks 保存 ID,native resume;custom trusted binding | official integration ref,dedupe 后 native resume | session ref 与 command policy同行 |
| 保活/迁移 | remote PTY/session 与专用机制持续演化 | detach/attach;experimental FD live handoff | process continuity 单独建模 |
| 哲学 | composable primitive,偏 human workbench | Agent-aware runtime,偏 automation substrate | 可组合与意见化编排可分层 |
最有价值的差异不是 Swift vs Rust,也不是 GUI vs TUI,而是两个系统选择的“深模块”不同:
- cmux 把 stable surface identity + semantic event journal + notification/browser surface 做深;
- Herdr 把 server-owned PTY + status authority + explainable detection + waitable Agent API + handoff 做深。
一个理想实现可以组合两者:用 Herdr 式 state authority 与 wait semantics 管运行时,用 cmux 式 semantic journal 与 workspace/browser/attention surface 管人类工作台。
八、Ghostty、tmux、Orca、Warp 分别站在哪里
这些产品不能粗暴排成“谁更先进”,因为它们承担不同层级。
| 产品 | 它首先是什么 | Agent-first 能力 | 控制面边界 |
|---|---|---|---|
| Ghostty | 高性能原生 terminal emulator / libghostty | 为上层提供可靠渲染和平台 UI 基础 | 不以 Agent 身份、状态和编排为核心 |
| tmux | 持久 PTY multiplexer | detach/attach、session/window/pane、可脚本化 | 只认识进程与 pane,不认识 Agent 语义 |
| cmux | native terminal + browser workbench | hook/journal、notification、unread、session resume、teams、socket API | 仍以 composable execution primitives 为主 |
| Herdr | Agent-aware terminal workspace manager | process/screen/hook authority、rollup、Agent API、native resume、handoff | 不拥有完整业务 task/approval/audit |
| Orca | worktree-first Agent development workspace | Agent tabs/status、hooks、CLI、browser、remote、hibernation、mobile | 更意见化地把 worktree 当隔离与任务容器 |
| Warp / Oz | terminal + proprietary Agent orchestration platform | local/cloud agents、跨机器/repo/team 并发、trigger/schedule/audit | 已跨进完整产品控制面,开源实现证据有限 |
Orca 的官方文档尤其能说明市场正在向相同对象收敛:一个 Agent Session 被定义为“一个 worktree 的一个 terminal 中运行的一个 Agent CLI”;tab 显示 working、waiting、done/unread;状态来自 OSC title 与 hooks;CLI 可以管理 worktree、terminal、browser 与 runtime;idle Agent 还可以 hibernate,并用原生 resume flag 恢复。
Warp 则进一步把 terminal 当本地交互入口,把 Oz 定义为底层 orchestration platform,负责 local/cloud agents、trigger、schedule、environment、跨机器/repo/team 协调与审计。它说明这条演化的终点可能不是“更好的 terminal”,而是terminal 成为一个完整 Agent 平台的高带宽本地入口。
九、多 Agent:从“摆很多 pane”到可编程协调
传统 multiplexer 已经允许人同时启动多个 Agent;Agent-first terminal 的新增价值是把这种并发变成一个 feedback loop:
图 5|可编程协调回路。Agent state 只能触发检查,不能替代产物验收。
要让这个回路可靠,需要五个条件:
- 隔离工作目录。 不同 Agent 至少使用不同 worktree 或明确写冲突策略;pane 隔离不是文件系统隔离。
- 稳定因果标识。 同一次协调中的 prompt、hook event 与 output 共享
command_id或映射记录。 - 语义等待。 等 state change、permission、process exit 或 artifact,而不是固定 sleep。
- 结构化产物。 diff、test、file、URL 或 review result 应独立于 terminal prose 被检查。
- 独立验收。 Agent 进入 idle/done 只表示一次执行尝试结束,不表示产物已经正确。
cmux 的 teams 能把 Claude/Codex 子 Agent 显示为 native splits;Herdr 的 skill/API 允许一个 Agent 创建 pane、启动 helper、发 prompt、等待并读取结果。这已经是 coordination。但如果 terminal 自身没有协调记录,父 Agent 崩溃后仍可能不知道这些 worker 为什么存在、谁拥有结果、是否需要重试。
所以更准确的判断是:
Agent-first terminal 可以提供 orchestration substrate;是否进一步维护任务账本,是 terminal 产品自身的能力选择。
回到 Herdr 界面: 人手动右键分屏、在两个 pane 输入任务、等侧边栏出现
done再切回去,已经是在执行同一个协调回路。CLI/Skill 并没有发明另一套工作方式,只是把 split、prompt、wait、read 从人的点击变成可组合命令。
十、恢复不是一个布尔值:四种连续性与一种假象

信息图 E|恢复阶梯。越靠左越接近保留原运行,越靠右越接近重建;screen replay 只恢复外观。
| 路径 | 原进程 | PTY / live screen | 布局/cwd | Agent conversation | in-flight wait/subscription |
|---|---|---|---|---|---|
| Client detach / reattach | 保留 | 保留 | 保留 | 因进程仍活着而保留 | server 若仍活着可保留部分状态 |
| Live server handoff | 尽力保留 | 通过 FD/runtime 转移 | 保留 | 因进程仍活着而保留 | 通常中断,需重连重试 |
| Snapshot restore | 不保留 | 不保留 | 重建 | 不自动保留 | 不保留 |
| Native Agent resume | 新进程 | 新 PTY | 重建 | Agent 自己恢复 | 不保留旧请求 |
| Screen/history replay | 不保留 | 只重放文本 | 可重建 | 不恢复 | 不保留 |
回到 Herdr 界面: 关掉终端窗口再运行
herdr,看到的是原进程继续运行,属于 detach/reattach;执行herdr server stop后再启动,看到相似布局却已经可能是新 shell 或 Agent native resume,属于重建。界面看起来都像“回来了”,底层连续性完全不同。
Herdr 的实现还处理了两个细节:
- 同一个 native Agent Session 在一次 restore 中只能恢复一次;重复引用的 pane 不再启动相同 conversation。
- 如果 native resume 生效,就不把旧 pane history 注入新 Agent terminal,因为那只是 presentation,不是 conversation,而且可能污染 TUI。
cmux 也按“先 layout,后 native resume command”重建。二者共同支持一个结论:terminal 层保存形状,Agent Runtime 保存对话;没有任一层可以单独恢复完整工作。 如果还要恢复 task ownership、重试次数、预算和验收状态,就必须由上层控制面保存。
十一、安全:控制 terminal 等于获得本地执行权
Agent-first terminal 的 API 看起来像 UI automation,实际上通常拥有读取屏幕、发送键盘、启动进程、访问 cwd、操纵 browser 和恢复命令的能力。设计时应把它视为本地 RCE control socket,而不是普通偏好设置接口。
回到 Herdr 界面: 右键 split、键盘输入和
agent send-keys最终都可能向真实 PTY 写入字节;差别只在调用者与路径。一个“帮我点一下审批”的 Agent 自动化能力,和一个可以在本机执行任意命令的 socket,只隔着身份、权限与策略校验。
1. 六条主要信任边界
| 边界 | 风险 | 最低防线 |
|---|---|---|
| Local socket client → server | 任意读取/输入/启动命令 | 0600 socket、peer identity、capability/token、可审计调用 |
| Agent hook → pane authority | 伪造状态、污染其他 Session | stable surface token、source ownership、monotonic seq、process corroboration |
| Screen/scrollback → persistence | prompt、token、命令输出泄露 | 默认关闭或 bounded、加密/权限、敏感提示、可清除 |
| Resume ledger → future shell | 持久化命令注入 | command canonicalization、签名 prefix、cwd/env binding、manual approval |
| Remote client → host | 跨主机输入与 secret 暴露 | host identity、transport auth、least privilege、显式路由 |
| Orchestrator Agent → worker pane | prompt injection、误操作、写冲突 | worktree sandbox、policy gate、command/task correlation、artifact review |
Herdr 的 handoff socket 会收紧为 0600,并用一次性 token、协议/版本校验和 ownership handshake;pane history 因可能含 secrets 默认关闭。cmux 的 custom resume command 要经过 trusted binding 或用户批准的签名前缀,且在保存前过滤敏感环境键。这些都不是附属功能,而是控制面正确性的一部分。
2. “worktree 就是 sandbox”不是通用安全结论
Orca 默认可以为受支持 Agent 预填 permission-bypass/yolo flags,其产品假设是 disposable worktree 提供主要隔离。但 worktree 只隔离 Git checkout,不隔离 home directory、network、credential helper、Docker socket、SSH agent 或系统命令。
如果自建产品采用类似模式,应明确拆开:
- 代码冲突隔离:worktree;
- 文件与进程隔离:container/sandbox/VM;
- 凭据隔离:scoped token / broker;
- 工具审批:Agent Runtime 或 terminal policy;
- 产物验收:独立 verifier 或人工检查。
十二、自建蓝图:八个深模块,而不是一个巨型 TerminalManager
图 6|建议实现分层。深模块之间通过小而明确的数据结构连接。
1. Container Registry
负责稳定身份与 topology,不解释 Agent:
Container {
workspace_id, tab_id, pane_id, terminal_id,
cwd, env_policy, foreground_process,
revision, created_at, runtime_owner
}关键不变量:pane/surface ID 在它声称的生命周期内不可因 UI 重排而变化;restore 若重新生成对象,必须有 durable identity 与 remap 记录。
2. Agent Adapter Registry
每个 Agent 适配器声明能力,而不是假装全都一样:
identify(process) -> confidence
observe(screen, osc) -> DetectionEvidence?
map_hook(native_event) -> SemanticEvent?
capture_session(event/files) -> SessionRef?
resume(session_ref, cwd, launch) -> Command?
capabilities -> { lifecycle, permission, interrupt, resume, usage }Screen manifest、hook mapper、Session collector 和 resume builder 应拆开版本;Claude 可以由 screen 判状态、hook 只报 Session,而 Pi 可以由完整 lifecycle hook 说了算。
3. State Arbiter
不要让 adapter 直接写 UI。Arbiter 接收 evidence,并执行:
- foreground ownership 校验;
- 每 source monotonic sequence;
- Session replacement / stale event 防护;
- 单一 active authority;
- working→idle debounce;
- process-exit backstop;
- provenance 与 explain trace。
建议输出同时带 state / authority / confidence / changed_seq / occurred_at / observed_at。
4. Attention Router
独立保存 seen_at / unread_since / notification_reason / priority,再按 workspace/task 聚合。不要把 done 固化进底层 lifecycle;它通常是 idle + unseen 的 presentation。
5. Session Ledger 与 Recovery Planner
Ledger 保存的是可恢复声明,不是“最后一次命令字符串”:
SessionBinding {
pane_id, agent_kind, native_ref,
cwd, launch_fingerprint, provider_identity_hint,
captured_by, captured_at, resume_capability,
trust_provenance
}Planner 再决定 reattach_live / import_runtime / native_resume / restore_shell / manual_only,并显式去重同一个 native Session。
6. Control API
最小协议应同时有快照、命令和事件:
{"id":"c-42","method":"agent.prompt","params":{"target":"reviewer","command_id":"run-17","text":"Review the diff","wait":{"until":["blocked","idle"],"timeout_ms":600000}}}{"sequence":891,"event":"agent.state_changed","data":{"pane_id":"w1:p2","agent":"codex","state":"idle","authority":"hook","state_change_seq":44,"causation_id":"run-17"}}协议至少需要:
- response 与 event 都可关联 request/command;
- 快照带 revision,事件带 global sequence;
- reconnect 后用 snapshot +
events_after(sequence)对账; - selectors 先解析成稳定 ID,等待期间持续验证 identity;
- 能力查询明确哪些 Agent 支持 hook、permission、interrupt、resume。
十三、最小原型:四个阶段就能验证方向
Phase 1:一个可寻址的 PTY Registry
- background server 拥有 PTY;
- workspace/pane 有稳定 ID;
- 支持 list/get/read/send/process-info;
- client detach 不杀进程。
不要一开始做 browser、cloud、mobile 或华丽 sidebar。先证明 runtime ownership 与 ID 不变量。
Phase 2:两种 Agent 的状态权威
选择一个 hook 完整的 Agent(例如 Pi/OpenCode)和一个主要依赖 screen 的 Agent(例如 Codex/Claude):
- foreground process identification;
- hook semantic events + sequence;
- screen manifest + explain;
- single authority arbitration;
idle + unseen = doneattention projection。
Phase 3:结构化 prompt / wait / event
agent.start与 layout create 分开;agent.prompt(command_id);- 观察活动门、状态 wait、timeout、target-replaced error;
- reconnectable event stream;
- 将 output 只作为证据,不作为唯一完成事实。
Phase 4:native Session restore
- 捕获 Session ref 与 provenance;
- snapshot 只保存 topology/cwd;
- native resume 去重;
- manual-only 与 trusted auto-resume;
- restore 后通过新 hook/session ref 重新确认绑定。
Conformance tests
| 主题 | 必须验证的失败场景 |
|---|---|
| Identity | 两个同 cwd、同 Agent 的 pane 不串事件;重排布局不换 ID |
| Sequence | 重复、乱序、延迟旧 Session hook 不回滚状态 |
| Authority | 完整 hook 存活时 screen 不抢权;进程退出后 authority 释放 |
| Attention | 后台 idle 变 done;查看后只清 seen,不伪造 lifecycle |
| Wait | prompt 无活动时报 stalled;目标进程替换时报 not-running;timeout 可重试 |
| Resume | 同一 Session ref 只恢复一次;无效 ref 回落为 shell;不重放旧屏幕污染新 TUI |
| Security | 未授权 socket client 被拒;secret env 不入 ledger;未批准命令不自动执行 |
| Reconcile | 事件丢失后 snapshot 修正;重连不重复执行 command |
十四、附录
A. Herdr Cheatsheet
适用证据版本:Herdr 固定提交
d6dae883。Herdr 仍在快速演化,真实使用前可运行herdr --version,并以当前二进制的herdr --help、herdr <command> --help和herdr api schema为准。
A.1 先背这一行对象关系
Session(后台 server 命名空间)
└── Workspace(项目 / 仓库 / 调查)
└── Tab(同一项目内的一组布局)
└── Pane(真实 terminal + PTY)
└── Agent(当前 pane 中被识别的前台 Agent 进程)最重要的区别:
- Session 决定一整套后台运行时是否隔离;多数时候只用默认 Session。
- Workspace 是日常项目边界;一个仓库或任务一个 workspace。
- Tab 用来区分
agents / logs / server / review等视图。 - Pane 是稳定可寻址的真实终端;普通 shell、测试和服务器也可以运行在里面。
- Agent 不是容器,而是 Herdr 在 pane 中识别出的当前进程;Agent 退出或被替换后,pane 仍然存在。
A.2 日常使用最短路径
| 想做什么 | 操作 |
|---|---|
| 在当前项目启动或重连默认 Session | herdr |
| 在 pane 中运行 Agent | 直接执行 claude、codex、pi、opencode 等 |
| 向右 / 向下分屏 | prefix+v / prefix+minus,或 pane 右键菜单 |
| 在 pane 间移动 | prefix+h/j/k/l,或直接点击 |
| 查看所有 workspace 与 Agent | prefix+w |
| 新建 tab | prefix+c |
| 临时放大当前 pane | prefix+z |
| 查看全部生效快捷键 | prefix+? |
| 离开但保持所有进程运行 | prefix+q,或关闭当前 client 窗口 |
| 回来 | 再次执行 herdr |
| 真正停止默认 Session 及其中进程 | herdr server stop |
prefix 默认是 ctrl+b:按下并松开 ctrl+b,再按动作键。例如 prefix+v 不是同时按三个键,而是先 ctrl+b、再 v。
A.3 高频快捷键
| 分组 | 动作 | 默认按键 |
|---|---|---|
| Pane | 向右 / 向下分割 | prefix+v / prefix+minus |
| Pane | 左 / 下 / 上 / 右移动焦点 | prefix+h/j/k/l |
| Pane | 缩放 / 还原当前 pane | prefix+z |
| Pane | 关闭当前 pane | prefix+x |
| Pane | 调整大小模式 | prefix+r |
| Pane | 复制模式 | prefix+[ |
| Tab | 新建 tab | prefix+c |
| Tab | 下一个 / 上一个 tab | prefix+n / prefix+p |
| Tab | 跳转到 tab 1–9 | prefix+1..9 |
| Workspace | 打开 workspace / Agent 导航 | prefix+w |
| Workspace | 新建 workspace | prefix+shift+n |
| UI | 切换侧边栏 | prefix+b |
| Session | 分离 client | prefix+q |
| Help | 查看并搜索快捷键 | prefix+?,随后按 / 搜索 |
鼠标同样是一等入口:点击对象以聚焦,拖动分割边框调整大小,右键创建 split/tab,拖选文本直接复制。快捷键是效率层,不是使用前提。
A.4 如何读 Agent 状态
| 状态 | 含义 | 推荐动作 |
|---|---|---|
working | Agent 正在运行 | 继续做别的事,不要轮询 pane |
blocked | 识别到审批、提问或输入请求 | agent focus/read 后查看原生 TUI,再明确回应 |
done | 底层已经 idle,但完成后尚未被看过 | 切过去检查结果、diff 与测试 |
idle | Agent 已停下来,且 pane 已经被查看 | 可继续发新 prompt |
unknown | Agent 存在,但 Herdr 无法可靠判断生命周期 | 当普通 terminal 使用,不假定成功或失败 |
记忆口诀:
blocked:现在需要我
working:还不用看
done:做完了但我没看
idle:做完了而且我看过
unknown:Herdr 不确定done 与 idle 的底层 Agent 生命周期可能相同,差异是 unseen / seen。agent read 只读内容,不会把结果标记为已看;agent focus 或在 UI 中聚焦相应 pane 才会更新 attention state。
A.5 查询对象:永远先拿 ID,不要猜
herdr status
herdr session list
herdr workspace list
herdr tab list --workspace <workspace_id>
herdr pane list --workspace <workspace_id>
herdr pane current --current
herdr agent list
herdr agent get <agent_name_or_pane_id>大多数创建命令返回 JSON。自动化脚本应从响应读取 workspace_id / tab_id / pane_id,不要依赖“当前是 w1:p2”之类的位置猜测。手动启动的 Agent 默认可以用其 pane ID 寻址;需要稳定可读的目标名时再执行:
herdr agent rename <pane_id> reviewer
herdr agent get reviewerA.6 布局与普通终端进程
# 创建项目 workspace;同时创建第一个 tab 和根 pane
herdr workspace create --cwd ~/project --label api --no-focus
# 为当前 pane 向右创建一个新 pane
herdr pane split --current --direction right --no-focus
# 新建 tab
herdr tab create --workspace <workspace_id> --label review --no-focus
# 查看 pane 的当前进程与屏幕
herdr pane process-info --pane <pane_id>
herdr pane read <pane_id> --source recent-unwrapped --lines 120
# 在普通 shell pane 中运行命令
herdr pane run <pane_id> "just test --watch"
# 等普通进程输出;这不解释 Agent 生命周期
herdr pane wait-output <pane_id> --regex "passed|failed" --timeout 120000选择原则:
- shell、测试、服务器、日志监视器使用
pane run/read/wait-output; - 已被 Herdr 识别的编程 Agent 使用
agent prompt/read/wait; - 提交 shell 命令优先使用
pane run,不要自行拼send-text再发送 Enter; workspace close只关闭 Herdr 中的 workspace;worktree remove才会显式执行 Git worktree 删除流程。
A.7 Agent 控制
# 列出、查看、聚焦和解释 Agent
herdr agent list
herdr agent get reviewer
herdr agent focus reviewer
herdr agent explain reviewer --verbose
# 在已经存在且停在 shell prompt 的 pane 中启动 Agent
herdr agent start reviewer --kind codex --pane <pane_id>
# 发 prompt,并等待 idle / done / blocked 之一
herdr agent prompt reviewer "Review the current diff" --wait --timeout 120000
# 只等待状态;显式列出感兴趣的状态更清楚
herdr agent wait reviewer \
--until blocked \
--until done \
--until idle \
--timeout 120000
# 读取真实 TUI 内容
herdr agent read reviewer --source recent-unwrapped --lines 120
# 对审批框或 TUI 明确发送逻辑按键
herdr agent send-keys reviewer esc
herdr agent send-keys reviewer enter
# 直接附加到一个 Agent terminal,而不是打开完整 Herdr UI
herdr agent attach reviewer关键限制:
agent start只启动 Agent,不创建布局;先创建 workspace/tab/pane。- 目标必须是唯一 live Agent name 或其当前 pane ID,不能只写
codex这种 Agent kind。 - 目标已经
blocked时,agent prompt会拒绝盲发新 prompt;应先read,再用send-keys明确处理交互。 agent wait等的是生命周期状态,不保证严格对应某一次 prompt;重要自动化仍需额外保存命令与产物的因果关系。agent wait、agent prompt --wait和pane wait-output不写--timeout时可能无限等待。
A.8 一套可以照抄的 reviewer 协作配方
下面的流程来自 Herdr 官方 Agent Automation 文档:分出新 pane、从 JSON 响应取得 ID、启动具名 Codex、分配 review、等待并读取结果。
split_json=$(herdr pane split --current --direction right --no-focus)
review_pane=$(printf '%s\n' "$split_json" | jq -r '.result.pane.pane_id')
herdr agent start reviewer --kind codex --pane "$review_pane"
herdr agent prompt reviewer "Review the current diff" \
--wait \
--timeout 120000
herdr agent read reviewer --source recent-unwrapped --lines 120如果想专门等待 Agent 请求决策:
herdr agent wait reviewer --until blocked --timeout 120000
herdr agent read reviewer --source recent-unwrapped --lines 80
# 读懂原生交互后,再选择 esc、enter、方向键或其他明确输入
herdr agent send-keys reviewer escagent read 取得的是终端文本,不等于验收结果。Review 是否可信,仍要检查 diff、测试或独立 verifier。
A.9 Worktree 隔离
# 从当前仓库创建 Git worktree,并作为新 workspace 打开
herdr worktree create \
--cwd ~/project \
--branch agent/reviewer \
--base HEAD \
--label reviewer \
--no-focus
# 查看 worktree workspace
herdr worktree list --cwd ~/project
# 显式移除检出;脏 worktree 会被 Git 拒绝,除非使用 --force
herdr worktree remove --workspace <workspace_id>Worktree 只隔离 Git checkout,不隔离 home、network、credential helper、SSH agent、Docker socket 或系统命令。不要把它当安全 sandbox。
A.10 分离、停止、恢复与更新
| 动作 | 命令 / 按键 | 原进程是否继续 |
|---|---|---|
| 分离当前 client | prefix+q | 是 |
| 重连默认 Session | herdr | 是,连接原 server |
| 查看命名 Session | herdr session list | 不改变进程 |
| 连接命名 Session | herdr session attach work | 连接该 Session |
| 停止默认 Session | herdr server stop | 否,会结束 pane 进程 |
| 停止命名 Session | herdr session stop work | 否,会结束该 Session 的 pane 进程 |
| 普通更新 | herdr update | 兼容 server 可继续;需重启时按提示处理 |
| 实验性实时交接更新 | herdr update --handoff | 对受支持 server 尽力保留 |
冷重启后的“画面回来”不保证原进程还在:布局/cwd 来自 snapshot;可选 pane history 只重放文本;受支持 Agent 可以通过原生 Session ref 启动新进程恢复对话。想判断恢复强度,回看第十章的恢复矩阵。
A.11 本地、SSH 与远程瘦客户端
# 本地
herdr
# 传统方式:先 SSH 到代码所在机器,再运行远端 Herdr
ssh you@server
herdr
# 本地 Herdr 作为瘦客户端,通过 SSH 连接远端 server
herdr --remote workbox
herdr --remote ssh://you@server:2222选择方式:
- 已经处在 SSH shell 或使用手机 SSH 客户端:远端执行
herdr; - 希望沿用本地按键绑定并桥接本地桌面能力:
herdr --remote <host>; - 代码、凭据、Agent 和 PTY 都运行在 server 所在机器,不会因为本地 client 断开而迁移回本机。
A.12 集成与状态排障
# 安装或检查 Agent integration
herdr integration install codex
herdr integration install claude
herdr integration status
# 状态不对时,先解释证据
herdr agent explain <target> --verbose
# 查看、更新或重载 screen detection manifests
herdr server agent-manifests --json
herdr server update-agent-manifests
herdr server reload-agent-manifests| 症状 | 先检查什么 | 常见原因 / 处理 |
|---|---|---|
| Agent 没被识别 | pane process-info、agent list | wrapper 只暴露 node/python;在宿主可见 wrapper 命令上设置 HERDR_AGENT=<agent> |
| 状态明显错误 | agent explain --verbose | TUI 文案或布局变化;更新 manifest,必要时添加本地 override |
一直 unknown | foreground process、manifest、integration | Herdr 知道 Agent 存在,但没有可靠生命周期证据;不要猜完成状态 |
blocked 没识别 | explain 中的 visible evidence | screen detection 对 blocked 刻意保守;新权限 UI 可能尚无规则 |
| pane 内又启动 tmux 后看不到 Agent | pane process-info | Herdr 看到的前台进程是内层 tmux;不要在 Herdr pane 内自动进入另一层 tmux |
agent prompt 返回 agent_blocked | agent read | 先理解审批/问题,再用 agent send-keys 回应 |
agent_prompt_stalled | 状态序列是否在 5 秒内变化 | prompt 已发送,但没观察到生命周期活动;检查目标、TUI 与检测证据 |
| wait 不返回 | 是否设置 --timeout、目标身份是否仍有效 | 默认可能无限等待;为自动化始终设置超时 |
| 重启后只剩 shell | integration status、Session ref | snapshot 只能重建布局;缺少有效 native Session ref 时不会恢复 Agent 对话 |
A.13 给其他 Agent 使用 Herdr
Herdr 内置一份与二进制版本匹配的 Skill,可直接查看:
herdr --skill支持 Skills 的 Agent 可以安装 Herdr 仓库中的 skills/herdr/SKILL.md。这份 Skill 教 Agent 使用 pane split/run/read/wait-output 与 agent start/prompt/read/wait。它首先检查 HERDR_ENV=1;变量不存在时,Agent 不应假装自己位于一个可控制的 Herdr pane 中。
A.14 最后只记住八条
- 日常用 Workspace 分项目,只有需要完全隔离 server/socket 时才用命名 Session。
- Pane 是终端位置,Agent 是当前 occupant;先建 pane,再启动 Agent。
- 看状态做注意力调度:
blocked先处理,done去验收,working不打扰。 - 自动化先 list/create 并读取返回 ID,不要猜 pane ID,也不要依赖焦点。
- 普通进程用
pane wait-output;Agent 用agent wait。 read不等于focus,idle/done不等于产物正确。prefix+q只是 detach;server stop才会杀掉 Session 中的进程。- 每个 wait 都加 timeout;每次自动输入前都确认目标 identity 与当前状态。
B. 主流 Agent TUI 如何接入 terminal 控制面
下表不是抽象猜测,而是对 cmux 固定提交的 resume registry/README 与 Herdr 固定提交的 Agent enum、manifest、integration/restore 文档做的合并速查。screen 表示从 bottom-buffer/OSC 识别状态;lifecycle 表示官方 integration 可以成为状态 authority;session 表示至少能上报 native Session 供恢复。具体 CLI 版本变化很快,实施前应重新跑 capability probe。
| Agent | 身份/启动入口 | Herdr 状态路线 | cmux / Herdr Session 恢复 | Adapter 实现重点 |
|---|---|---|---|---|
| Claude Code | claude | screen;integration 主要提供 session | claude --resume <id> | hook 覆盖不等于完整 lifecycle;permission/question 事件可单独映射 |
| Codex | codex | screen;integration 主要提供 session | codex resume <id> | OSC/TUI 规则与 hooks setup;区分 approval、turn end 与 process exit |
| Pi | pi | lifecycle hook 优先;否则 screen | pi --session <path-or-id> | 可作为完整 hook authority 样本;Session 可能是 path 或 id |
| Hermes Agent | hermes | screen;integration 提供 session | hermes --resume <id> | Python/wrapper 进程识别;hook 只覆盖部分事件时不可抢 lifecycle authority |
| OpenCode | opencode | lifecycle plugin 优先;否则 screen | opencode --session <id> | plugin event bus、Session 与 state 可同时上报 |
| OMP | omp | lifecycle hook | omp --resume=<path-or-id> / cmux registry 使用原生会话参数 | 需要 source sequence 与 Session replacement 防护 |
| Kimi Code | kimi | lifecycle hook 优先;否则 screen | kimi --session <id> | lifecycle + session;兼顾 TUI fallback |
| MastraCode | mastracode | lifecycle hook | mastracode --thread <id>(Herdr) | thread 是 conversation identity,不是 pane identity |
| Kilo Code CLI | kilo | lifecycle plugin 优先;否则 screen | kilo --session <id>(Herdr) | plugin authority 与 screen fallback 互斥 |
| Gemini CLI | gemini | screen(Herdr 标为较少测试) | gemini --resume <id>(cmux) | TUI 版本漂移与较弱 fixture 覆盖 |
| Cursor Agent CLI | cursor-agent | screen;session integration | cursor-agent --resume <id> | bundled Node wrapper argv 识别 |
| Grok / Grok Build | grok | screen + OSC | cmux 文档 grok -r <id>;Herdr grok --resume <id> | CLI 版本/参数差异必须 capability probe,不能硬编码一个全局命令 |
| GitHub Copilot CLI | copilot | screen;session integration | cmux copilot --resume <id>;Herdr copilot --resume=<id> | 同样存在参数形态差异;用 per-version builder |
| Devin CLI | devin | screen;session integration | devin --resume <id>(Herdr) | status 主要靠 TUI;Session hook 不做 lifecycle authority |
| Droid / Factory | droid | screen;session integration | droid --resume <id> | process exit、interrupt 后 screen state 要重新确认 |
| Antigravity CLI | agy | screen;session integration | agy --conversation <id> | Agent alias 规范化;conversation ref 独立保存 |
| Qoder CLI | qodercli | screen;session integration | qodercli --resume <id> | 中英文/发行渠道 binary alias 与 TUI 文案变化 |
| Qwen Code | qwen | screen;session integration | Herdr integration 可保存 session;恢复以当前官方 integration 为准 | Node/Bun wrapper 识别;Session-only 上报不改变状态 |
| Amp | amp | screen | amp threads continue <id>(cmux) | resume 不是统一 --resume 形态,需要独立 builder |
| Cline | cline | screen,较少测试 | 主样本未证明统一 native resume | 先声明 resume=false,不要虚构能力 |
| Kiro CLI | kiro | screen | 主样本中状态/恢复支持深度不一致 | 分开检测支持与恢复支持 |
| Maki | maki | screen | 主样本未证明统一 native resume | 只提供检测时应明确 capability degradation |
| OpenClaw / 非 TUI Runtime | 可能通过 daemon/API/普通命令运行 | 无通用 pane-native authority | 由自身 control/session protocol 决定 | 不应强行 screen scrape;需要时使用其原生 API,而不是硬套 terminal adapter |
这张表最重要的不是命令,而是三条适配原则:
- 检测、生命周期、Session、权限、取消、Resume 是独立 capability。 支持其中一个不代表全支持。
- 同一 Agent 在不同控制面甚至不同版本中 resume 参数可能不同。 需要 version probe 与 builder,不应在核心逻辑中散落 shell 字符串。
- 未知能力应降级为 plain terminal。 不支持 rich state 或 native resume 时,Agent 仍应能正常运行;UI 显示 unknown,恢复成 shell,绝不能靠猜测自动执行。
十五、最后的判断:terminal 正在成为 Agent 的本地操作系统外壳
把 Agent 类比为进程时,传统 terminal 只提供字符设备,tmux 提供进程容器;cmux、Herdr、Orca、Warp 这一波产品继续补上身份、状态、注意力、API、恢复和协调。这个类比足以帮助我们理解演化方向,不必把它做成严谨的一一映射。
但工程边界必须清楚:
- PTY/process continuity 不等于 Agent conversation continuity;
- Agent conversation continuity 不等于 task continuity;
- Agent idle/done 不等于 artifact accepted;
- 多个 pane 不等于多 Agent orchestration;
- worktree isolation 不等于安全 sandbox;
- local execution 不等于没有控制面。
因此,Agent-first terminal 可以从自身内部理解为三层组合:
- terminal / multiplexer core 管理字符、PTY、pane、布局和进程连续性;
- Agent semantic control 管理身份、状态、注意力、事件、等待和恢复入口;
- Runtime-specific integration 适配不同 Agent 的 hooks、Session、权限与 resume 语义。
对想“抄作业”的实现者来说,最值得复制的不是侧边栏、状态点或某组快捷键,而是两个主样本暴露出的共同纪律:稳定寻址,不猜绑定;状态有权威,不让信号打架;事件可重放,等待有因果;恢复分层,自动命令受信任;执行状态永远不冒充业务完成。
Source Manifest
- 完整来源、固定提交、文件定位、访问日期与证据边界:Source Manifest。
- ImageGen prompt、尺寸、SHA-256、视觉检查与事实边界:ImageGen Manifest。
- 本地相关证据:Agent 控制面、远程 Agent 控制栈、Agent 可观测性、tmux source note、Agent 产品的控制面与入口竞争。