跳转至

Herdr 源码深度解析

Herdr 0.8.0 Rust 固定提交 bbd7c2

Herdr 表面上是一个“给 AI 编码 Agent 用的终端工作区管理器”,源码里真正困难的问题却远不止终端分屏:它必须同时管理长寿命 PTY、复杂 VT 状态、Agent 生命周期、多个本地/远程客户端、Git worktree、断点恢复,以及更新时不杀掉正在工作的进程。

这套文档从这些难点反推架构,再沿真实调用链落回代码。目标不是记住 243 个 Rust 文件,而是建立一张可以用于阅读、调试和二次开发的心智地图。

审计快照

本站对应上游提交 bbd7c2094a44fcbcc4a3a3aedef236c4d697d793,Cargo 版本 0.8.0,审计日期 2026-08-10。所有源码链接都固定在该提交。

一句话架构

Herdr 是一个服务端拥有状态与 PTY、客户端只负责输入与呈现的终端工作区系统;它以 TerminalId 把可持久化状态和活跃运行时重新连接起来,以多来源证据推导 Agent 状态,并用序列号、代际和两阶段交接保护所有跨线程、跨进程的异步边界。

flowchart LR
    User["键盘 / 鼠标 / IME"] --> Client["本地或 SSH 客户端"]
    Client -->|"ClientMessage"| Server["Headless Server"]
    Server --> AppState["AppState\n纯状态"]
    Server --> Runtime["TerminalRuntimeRegistry\n活跃资源"]
    Runtime --> PTY["PTY actor"]
    PTY --> Child["Shell / AI Agent"]
    PTY --> VT["Ghostty VT"]
    VT --> Detect["Agent 检测与仲裁"]
    VT --> Render["虚拟渲染 + diff"]
    Render -->|"Semantic / ANSI frame"| Client
    AppState --> Persist["session.json / history"]
    Server --> API["本地 JSON API / EventHub"]

你会学到什么

  • 从单体 TUI 到 Server/Client


    为什么启动入口仍保留单进程模式,而默认路径已经是后台服务;服务器如何在一个循环里公平处理 PTY、API、客户端和定时任务。

  • 终端不是字符串缓冲区


    PTY 字节怎样进入 Ghostty VT,如何回送终端查询响应,为什么可见文本、检测文本、ANSI 快照和渲染脏行必须是不同读模型。

  • Agent 状态是推导结果


    进程树、屏幕规则、OSC 和官方 Hook 如何竞争;为什么需要序列号、会话代际、权威源和 skip_state_update。

  • 长寿命资源如何跨更新存活


    Live Handoff 如何暂停 PTY reader、复制 master FD、交换快照、切换 socket,并在任何失败点回滚。

  • 性能来自边界设计


    256 容量事件队列、64 条公平 drain、合并渲染信号、脏行 patch、客户端背压和“发送成功后才推进基线”如何协作。

  • 如何验证并发系统


    3458 个 Rust 测试入口、维护脚本、架构守卫、跨平台 CI 和端到端 PTY 测试分别守住哪一层契约。

推荐路线

第一次阅读建议按以下顺序:

  1. 全景与阅读地图:先认识进程、状态和数据流边界。
  2. 启动与运行模式 → Server/Client:理解默认执行骨架。
  3. 状态与身份 → 工作区布局:建立领域模型。
  4. PTY/终端 → 输入/渲染:打通一帧和一次按键。
  5. Agent 检测 → 持久化与交接:攻克最具 Herdr 特色的部分。
  6. 再按兴趣选择 API、插件、worktree、远程、平台、性能和测试章节。

若你在定位具体文件,直接看源码地图;若你想核对数字或复跑统计,看证据、统计与复现。

本书如何拆代码

每一章尽量遵循同一条证据链:

问题是什么 → 完整旅程怎样走 → 核心类型保存什么 → 关键分支为什么存在 → 失败边界如何收口 → 测试证明了什么 → 设计可迁移到哪里

代码片段只截取理解所需的最小形状;精确实现以紧邻的固定提交源码链接为准。文中明确区分:

  • 源码事实:可以从类型、函数、测试或配置直接验证;
  • 设计解释:根据调用关系和约束推导出的意图;
  • 评价与建议:本拆解对取舍、风险或演进方向的判断。

这一区分很重要。源码阅读最危险的不是漏掉一个函数,而是把“作者写了什么”和“我们认为为什么这样写”混成同一件事。