# CodexMonitor 源码分析：给 Codex 配一个指挥台——Tauri 桌面端如何用官方协议编排多个 Agent

> **分析对象**：CodexMonitor（`github.com/Dimillian/CodexMonitor`），MIT，本分析基于 **main 分支 2026-03-26**（commit dd61b9a，PR #584 "Refactor tray thread menu"）。作者 **Dimillian**（Thomas Ricouard）——SwiftUI 开源圈知名开发者（ACarousel、MovieSwiftUI）。
> **代码规模**：860 文件 / 28MB。Rust 后端 92 文件 / 31,171 行（`src-tauri/`），TS 前端 `src/` 557 个 ts/tsx 文件 / 112,428 行（连 css 计 603 文件 / 127,161 行），另有 docs/、scripts/（iOS 构建链）。运行时依赖官方 `codex` CLI（`codex app-server` 协议）。
> **一句话定位**：**给 OpenAI Codex CLI 配的桌面指挥台**——Tauri 2 应用，每个工作区 spawn 一个 `codex app-server` 子进程，通过**官方 stdio JSON-RPC 协议**（而非 PTY 抓屏）驱动多个 Codex agent thread；提供工作区/worktree 管理、流式对话、diff/PR 审查、用量统计、听写、iOS 远程 daemon 全套。
> **读者对象**：已经读过本系列至少一份分析的读者（尤其 **OpenAI Codex 源码分析** 与 **Kanban 源码分析**——本文是 Codex 分析"加深版"的姊妹篇，也是与 kanban"PTY+hooks 方案"的对照样本）。全文标注 `文件:行号`，可回源码核对。

---

## 目录

1. [项目概览：给 Codex CLI 配一个指挥台](#ch1)
2. [全景架构：Tauri 双进程 + app-server 协议 + 事件流](#ch2)
3. [app-server 接入：一个工作区一个 codex 进程，LSP 式握手](#ch3)
4. [协议交叉印证：CodexMonitor × codex 官方 app-server 协议](#ch4)
5. [thread 模型与事件驱动状态机](#ch5)
6. [Rust shared 层：app 与 daemon 共享的领域核心](#ch6)
7. [worktree 与工作区：一等对象](#ch7)
8. [git / GitHub：libgit2 读 + CLI 写 + gh 集成](#ch8)
9. [远程 daemon 与 iOS：一套 core 两份形态](#ch9)
10. [前端架构：feature-sliced + 零状态库](#ch10)
11. [前端功能特性：Composer / 听写 / diff / 用量](#ch11)
12. [配置三权分立：settings.json / workspaces.json / config.toml](#ch12)
13. [工程化与测试：AGENTS.md 分层纪律 + 单测密度](#ch13)
14. [开发思路与品味：Dimillian 的十个选择](#ch14)
15. [总结：三个最独特的设计与取舍](#ch15)

---

<h2 id="ch1">第 1 章 项目概览：给 Codex CLI 配一个指挥台</h2>

**CodexMonitor 是什么？** README 第一句：*A Tauri app for orchestrating multiple Codex agents across local workspaces*。它不是又一个编码 Agent——**Agent 是 codex，它是 Agent 的指挥台**：

- **多工作区**：侧栏管理多个项目，每个工作区 spawn 一个 `codex app-server`，独立 `CODEX_HOME`。
- **多线程**：一个工作区里多个 thread（对话会话），支持 resume/fork/archive/copy、pin/rename、每线程草稿、打断/steer 进行中的回合。
- **worktree 隔离**：给 agent 建独立 git worktree（独立分支 + 独立 codex home），多 agent 并行零冲突——和 kanban 一卡一 worktree 同一精神。
- **Composer**：图片附件、Queue vs Steer 双跟随模式、`$` skills / `/prompts:` / `/review` / `@` 文件自动补全、模型选择、context ring。
- **git/GitHub 全流程**：diff 面板、stage/revert、commit log、分支、GitHub issues/PR（列表/diff/评论），"Ask PR" 把 PR 上下文送进新 agent 线程。
- **用量仪表**：home 用量快照（解析 codex 的 rollout JSONL）+ 侧栏 credits 计量表。
- **听写**：本地 Whisper（whisper-rs），hold-to-talk + 波形。
- **iOS 远程模式**：桌面起 daemon（TCP JSON-RPC），iPhone 连 Tailscale 跑瘦客户端。

### 它和 codex / kanban 的关系（本系列的两个邻居）

| | codex（官方 CLI） | kanban（编排看板） | CodexMonitor（桌面指挥台） |
|---|---|---|---|
| 谁是 Agent | 自己就是 | 7 种 CLI Agent | 只有 codex |
| 集成方式 | — | **PTY 进程 + hooks 反向注入** | **官方 app-server 协议（stdio JSON-RPC）** |
| 形态 | 终端 CLI | 浏览器看板 | Tauri 桌面 + iOS |
| 编排粒度 | 单会话 | 卡片（任务） | workspace × thread |
| 与 codex 关系 | 本体 | codex 是 7 种之一（PTY） | **codex 是唯一核心，走官方插座** |

**最有意思的对照**：kanban 和 CodexMonitor 做同一件事（多 agent 编排），但走了两条截然不同的技术路线——**黑盒适配**（PTY 抓屏 + hooks）vs **官方协议**（app-server JSON-RPC）。这是贯穿全文的主线。

### 数字化的项目形状

| 维度 | 数字 | 说明 |
|---|---|---|
| 后端 | src-tauri/ 92 文件 / 31,171 行 | Tauri 命令 + shared 领域核心 + daemon |
| 前端 | src/ 557 个 ts/tsx / 112,428 行（含 css 计 603 文件 / 127,161 行） | React + Vite + feature-sliced，25 个 feature |
| Tauri 命令 | `generate_handler!` 注册 125 条（源码共 138 处 `#[tauri::command]`，差额是 dictation/terminal/menu 的 cfg 双实现） | lib.rs invoke_handler 注册，每条命令"本地 or 转发 daemon"双轨 |
| app-server 方法 | 约 22 个 | codex_core.rs 调用，全部官方协议方法（无私有/实验） |
| 依赖（前端） | 19 条运行时依赖，UI 库只有 6 类 | lucide / react-markdown(+remark-gfm) / prismjs / xterm / react-virtual / Sentry，另加 @pierre/diffs、vscode-material-icons 与 6 个 @tauri-apps 插件——无 Tailwind/Radix/状态库 |
| 平台 | macOS 主 + Windows/iOS | 托盘仅 macOS；Windows opt-in；iOS 远程 daemon 模式 |
| 许可 | MIT | 极宽松 |

---

<h2 id="ch2">第 2 章 全景架构：Tauri 双进程 + app-server 协议 + 事件流</h2>

### 分层约定（`AGENTS.md` 硬性规则）

```
┌──────────────────────────────────────────────────────────────┐
│  React 前端（feature-sliced，src/features/ 25 个 feature）    │
│  编排在 hooks/* 与 bootstrap/*，事件 fanout 只在 services/     │
└───────────────┬──────────────────────────────────────────────┘
                │ Tauri IPC（注册 125 条 #[tauri::command]）
┌───────────────▼──────────────────────────────────────────────┐
│  Rust 后端（src-tauri/）                                       │
│  ┌──────────────┐  ┌─────────────┐  ┌──────────────────────┐ │
│  │ lib.rs 命令层 │→ │ shared/*    │← │ bin/codex_monitor_  │ │
│  │ workspaces/  │  │ 领域核心     │  │ daemon（远程模式）    │ │
│  │ files/ ...   │  │ (source of  │  │ #[path] include 同源  │ │
│  │ 薄适配层      │  │  truth)     │  │                      │ │
│  └──────┬───────┘  └──────┬──────┘  └──────────────────────┘ │
│         ▼                 ▼                                   │
│  codex app-server 子进程（每 workspace 一个，stdio JSON-RPC）  │
└──────────────────────────────────────────────────────────────┘
```

- **领域逻辑放 `src-tauri/src/shared/*`**——app 与远程 daemon 编译进同一二进制、共用同一套 `*_core` 函数，避免逻辑漂移。
- `codex/`、`workspaces/`、`git/` 等是 **Tauri 命令薄适配层**；前端 IPC 只经 `src/services/tauri.ts`。
- daemon（`src-tauri/src/bin/codex_monitor_daemon.rs`，TCP 行分隔 JSON-RPC）与 app **复用 shared core**，方法名/负载对齐。
- 前端 **feature-sliced**：`src/features/{app,threads,workspaces,...}`，事件 fanout 只在 `src/services/events.ts`。

### 命令注册表（`src-tauri/src/lib.rs`，337 行）

`invoke_handler` 注册 125 条命令（源码共 138 处 `#[tauri::command]`，多出的是 dictation/terminal/menu 按 `cfg` 分桌面与移动两套实现）。**每个 codex 命令模式统一**：先查 `remote_backend::is_remote_mode`，是则 `call_remote`（转发 daemon），否则转 `shared::codex_core::*_core`。状态经 `state::AppState` manage：

- `workspaces: Mutex<HashMap<String, WorkspaceEntry>>`
- `sessions: Mutex<HashMap<String, Arc<WorkspaceSession>>>`——**每个 workspace 一个 app-server 进程**
- `app_settings`、`codex_login_cancels`、`tcp_daemon` 等

### 事件流（贯穿全篇的主干）

```
codex app-server stdout（JSON 行）
  → backend/app_server.rs 分类（请求响应 / thread/list cwd 归属 / 纯通知）
  → AppServerEvent{workspace_id, message}
  → event_sink.rs → app.emit("app-server-event")
  → 前端 services/events.ts createEventHub 懒订阅 fanout
  → useAppServerEvents（30+ 方法白名单 + approval 后缀匹配）
  → useThreadEventHandlers → 各 reducer slice dispatch
  → React UI
```

---

<h2 id="ch3">第 3 章 app-server 接入：一个工作区一个 codex 进程，LSP 式握手</h2>

### spawn（`src-tauri/src/backend/app_server.rs`，L749 `spawn_workspace_session`）

1. `check_codex_installation`（`codex --version`）。
2. `build_codex_command_with_bin` 构造命令：可配 `codexBin`/`codexArgs`，Windows 特判 cmd/bat。
3. `current_dir(entry.path)` 设工作目录，设 `CODEX_HOME`，参数 `["app-server"]`——**每个工作区一个独立 codex 进程 + 独立 codex home**（互不污染，配置/会话/用量数据天然隔离）。
4. stdio 全管道化。

### 生命周期（`src-tauri/src/shared/workspaces_core/connect.rs`）

- `connect_workspace_core` 持**全局 spawn 锁**：已有存活 session 直接复用（`take_live_shared_session` 遍历现有 session `try_wait()` 判活，**同进程多 workspace 共享同一 codex 进程**——`app-server` 本身支持多客户端）。
- 删除：`kill_session_by_id` → `kill_child_process_tree`。
- **重连是"惰性"的**：无后台自动重启，靠前端启动/窗口聚焦时逐 workspace 调 `connect_workspace`（README 明示），失败的 session 从 map 清除后下次调用重生。
- 握手：`initialize`（clientInfo + experimentalApi，15s 超时失败即 kill）→ `initialized` 通知 → 发 `codex/connected` 合成事件——**LSP 式双步握手**。

### stdio 读写与 JSON-RPC 编解码

- 写侧：`write_message`（JSON 单行 `\n` 定界）。
- 读侧两个 task：stdout 用 `BufReader::lines()` 逐行 `serde_json::from_str`；stderr 转发为 `codex/stderr` 事件。
- `send_request_for_workspace`（L494）：自增 `next_id` → 注册 `pending: oneshot` 与 `request_context(workspace, method)` → **超时 300s** → 响应按 id 回填。
- `send_notification` / `send_response`（服务器请求如 approval 的应答）。

### 事件分类（智能路由的关键）

每条 stdout 行先判三类：

1. **有 `id` + `result/error`** = 请求响应 → 查 `request_context` 还原 workspace/method，并 `extract_related_thread_ids` 回填 `thread_workspace` 映射。
2. **`thread/list` 响应**：额外解析 cwd → `resolve_workspace_for_cwd`（**最长前缀匹配**）建立 thread→workspace 归属；`memory_consolidation` 子代理线程加入 `hidden_thread_ids` 并改发 `codex/backgroundThread(hide)` 合成事件（**内存整理子代理不污染主界面**）。
3. **纯通知**：按 `thread_workspace` 路由 workspace；全局通知（`account/*`）广播到 session 下所有 workspace。

### 与 kanban PTY+hooks 方案的本质区别

这是本分析最重要的对照点：

| | kanban | CodexMonitor |
|---|---|---|
| 数据来源 | PTY 字节流 + hooks 文本回调 | **结构化 JSON-RPC 通知** |
| 状态语义 | 解析 hook payload / 轮询 rollout jsonl | **类型化事件**（thread/status/changed、item/started/completed、delta 流） |
| 控制方向 | 单向（只收） | **双向**（send_response 应答权限、turn/steer 打断） |
| 可靠性 | 依赖各 Agent hook 实现，Codex 甚至要轮询 | 协议原生保证 |
| 代价 | 适配 7 种 Agent 的适配器矩阵 | 绑定 codex 官方协议（生态单一） |

**一句话**：kanban 把 Agent 当"黑盒子"（屏幕抓取 + 文本流），CodexMonitor 把 codex 当"服务"（官方插座直连）。前者广、后者深。

---

<h2 id="ch4">第 4 章 协议交叉印证：CodexMonitor × codex 官方 app-server 协议</h2>

> 本章所有 codex 侧引用均来自本仓库检出的 codex 官方源码（`参考项目/codex/codex-rs/`）。这是把 CodexMonitor 的代码与 codex 本体对齐的一次"对照实验"。

### 4.1 协议方法全集（codex 侧）

codex 侧 `codex-rs/app-server-protocol/src/protocol/common.rs`（4072 行）定义了完整协议面（本节所有 `common.rs:` 行号均指该文件）：

- **客户端请求**（ClientRequestPayload，common.rs:482–1235）：`initialize`:482、`thread/start`:491、`thread/resume`:497、`thread/fork`:503、`thread/archive`:509、`thread/list`:630（cursor/limit/sortKey/sourceKinds）、`turn/start`:836、`turn/steer`:842、`turn/interrupt`:848、`review/start`:889、`model/list`:895、`collaborationMode/list`:964、`skills/list`:684、`account/rateLimits/read`:1058、`account/read`:1192 等。
- **实验性方法**（`#[experimental]`）：`thread/search`、`environment/*`、`process/spawn|kill`、`thread/increment_elicitation`、`memory/reset`。
- **服务器→客户端请求**（ServerRequestPayload，即 approval 请求，common.rs:1505–1554）：`item/commandExecution/requestApproval`、`item/fileChange/requestApproval`、`item/tool/requestUserInput`、`item/permissions/requestApproval`、`item/tool/call`、`attestation/generate`。
- **服务器通知**（common.rs:1658–1731）：`thread/started`、`thread/status/changed`、`turn/started`、`turn/completed`、`item/started`、`item/completed`、`item/agentMessage/delta`、`item/plan/delta`、`command/exec/outputDelta`、`account/updated`、`model/rerouted` 等。

### 4.2 CodexMonitor 的覆盖面（`src-tauri/src/shared/codex_core.rs`，1033 行）

调用约 **22 个方法，全部是官方协议方法，无一个私有/实验方法**。它虽然声明 `experimentalApi: true`，但一个实验方法都没用（协议能力开关，见下）。

- 审批请求用 `send_response(request_id, result)` **通用转发**（codex_core.rs:848–856），未按官方 `codex-rs/app-server/src/bespoke_event_handling.rs:1872–2018` 的类型化 `*ApprovalResponse` 结构化处理。
- **未覆盖**：多 agent 编排、plugin/marketplace 管理（skills 只读 list）、`fs/*` RPC、`command/exec` 双模式、`process/spawn`、config RPC（它改走直接读写 config.toml）、`thread/realtime`、`remoteControl`。

**结论**：CodexMonitor 不是"协议全覆盖客户端"——它覆盖了 thread/turn 生命周期、查询与账户这**一个纵深**，其余面刻意不碰。这是"够用就好"的品味：深度集成核心路径，外围用更简单的直接手段（config.toml 直写）。

### 4.3 app-server 的官方设计初衷（codex 侧 AGENTS.md）

- `AGENTS.md:104–106`：把 **app-server APIs** 列为外部集成面的破坏性变更必搜项（CodexMonitor 这类客户端要盯版本）。
- `:270`：新 API 只进 v2，v1 冻结；`:273` `<resource>/<method>` 单数命名；`:274–276` 线上 camelCase；`:286–287` 实验面用 `#[experimental]` 门控；`:294–296` list 方法默认 cursor/limit 分页。
- 官方 README 定位：**app-server = 给编辑器/IDE 的"插座"**——让 IDE 不用解析终端 ANSI、不用 hack hooks，以 LSP 式协议直接驱动 Codex。

**CodexMonitor 正是走在官方指定路径上的**：`src-tauri/src/backend/app_server.rs:760–771` spawn 参数、`build_initialize_params`（:419–430）的 `clientInfo{name:"codex_monitor"}` + `capabilities.experimentalApi:true`、`initialized` 通知（:1091）——与官方 codex `codex-rs/app-server/src/request_processors/initialize_processor.rs:70–101` 的握手契约**逐项吻合**。

### 4.4 rollout JSONL 用量统计的交叉印证

- 官方格式：`~/.codex/sessions/YYYY/MM/DD/rollout-<ts>-<uuid>.jsonl`，行结构 `RolloutLine{timestamp, ordinal, item}`（codex `codex-rs/protocol/src/protocol.rs:3380`），`item` 是 8 变体枚举 `RolloutItem`：`SessionMeta | ResponseItem | InterAgentCommunication | InterAgentCommunicationMetadata | Compacted | TurnContext | WorldState | EventMsg`（同文件 :3186–3199）。`TokenCountEvent` 含 `info`（total/last_token_usage 的 input/cached/output）+ **`rate_limits`**（同文件 :2140–2143）。
- CodexMonitor（`src-tauri/src/shared/local_usage_core.rs`，840 行）：`spawn_blocking` 解析 JSONL——`session_meta`/`turn_context` 的 cwd 过滤 workspace（:220–229）、取 model（:232–237）、token_count 差值去重计 input/cached/output（:281–380）、`agent_message`/`agent_reasoning` 计时长（:260–279）、`response_item` assistant 计运行数（:383–409）。
- **选了**：token 用量、缓存、model、cwd、活跃时长/次数。**漏了**：`rate_limits`（credits/plan_type——官方已落盘的最有价值字段）、originator/source（CLI vs VSCode vs appServer）、子 agent 血缘、exec_command/file_change 调用计数。

**品味观察**：用官方已落盘的数据做本地统计（不调 API、不挂钩子），是"与官方数据格式同频"的聪明做法；但 840 行手写解析也暴露了"够用为止"——真正该做的 credits 统计反而没读。

### 4.5 config.toml / auth.json / rules 三处小交叉

- `[features]`（codex `codex-rs/config/src/config_toml.rs:455` → `codex-rs/features/src/lib.rs:656` 的 `FeaturesToml`）：键定义在同文件 `steer`:1286（恒启用）、`collaboration_modes`:1340（恒启用）、`unified_exec`:856、`apps`:1106（需 chatgpt 认证）。`personality` 顶层枚举 `None|Friendly|Pragmatic`（codex `codex-rs/protocol/src/config_types.rs:311–317`）。**CodexMonitor 同步的 5 个字段与官方键名完全吻合**，且正确拒绝已废弃的 `collab` 键（CodexMonitor `src-tauri/src/codex/config.rs:50–53`）。
- `auth.json`：`{OPENAI_API_KEY, tokens:{id_token, access_token, refresh_token, account_id}, last_refresh}`；idToken JWT payload 含 `chatgpt_plan_type`/`email`。CodexMonitor `src-tauri/src/shared/account.rs:63–99` 的 URL_SAFE_NO_PAD 解码→取 `chatgpt_plan_type`/email **完全对应**，并作为 `account/read` 失败时的 fallback（`src-tauri/src/shared/codex_core.rs:627–650` 的 `account_read_core`）。
- `prefix_rule` 是 execpolicy 的 Starlark 内建（定义在 `execpolicy/src/parser.rs`；`config/src/requirements_exec_policy.rs:51–58` 是它在 `requirements.toml` 里的镜像结构，源码注释亦如此写），`decision="allow"` 可让 agent 绕过审批。CodexMonitor `src-tauri/src/rules.rs:97–100` 往 `~/.codex/rules/default.rules` 追加 `prefix_rule(pattern=[...], decision="allow")`——**语义正确**。

**这四组交叉印证说明一件事**：Dimillian 写 CodexMonitor 时是**逐字段对着 codex 官方源码和配置格式做的**——协议方法名、握手参数、config 键、auth JWT、rules 语法全部对齐。这是"与上游保持契约级同步"的集成品味。

---

<h2 id="ch5">第 5 章 thread 模型与事件驱动状态机</h2>

### 5.1 thread 生命周期（`src-tauri/src/shared/codex_core.rs`）

| 操作 | codex 方法 | 关键参数 |
|---|---|---|
| 新建 | `thread/start` | `{cwd, approvalPolicy:"on-request"}` |
| 列出 | `thread/list` | sourceKinds 排除泛 subAgent（防内存整理泄漏）分页 |
| 恢复 | `thread/resume` | 前端选中即调，从磁盘刷新消息 |
| fork/archive/compact | `thread/fork/archive/compact/start` | — |
| 改名 | `thread/name/set` | — |
| 发送 | `turn/start` | input items（text/image/localImage/mention），accessMode→sandboxPolicy（dangerFullAccess/readOnly/workspaceWrite）+ approvalPolicy |
| 插话 | `turn/steer` | 带 expectedTurnId |
| 打断 | `turn/interrupt` | — |

### 5.2 后端不维护状态机

**关键设计**：CodexMonitor 的 Rust 侧**不维护 thread 状态机**——状态是**事件驱动的前端 reducer**。`thread/status/changed`、`turn/started/completed`、`item/started/completed`、approval 请求（`*requestApproval` 后缀）经 `useAppServerEvents.ts` 的 `METHODS_ROUTED_IN_USE_APP_SERVER_EVENTS` 路由到 `useThreadsReducer` 的 slices，`threadNormalize.ts` 兜底 camelCase/snake_case。running/idle/needs_approval 完全由前端从事件推导。

**品味观察**：这是"协议即真相"的推论——既然 app-server 会推送权威事件，后端就不需要镜像状态（少一层状态同步 bug）。对比 kanban 的后端状态机（session-state-machine.ts 78 行），CodexMonitor 把状态迁移放进了前端 reducer（1179 行）。

---

<h2 id="ch6">第 6 章 Rust shared 层：app 与 daemon 共享的领域核心</h2>

`src-tauri/src/shared/` 共 17 个模块 / 31 文件 / 11,197 行——**纯逻辑层，不依赖 Tauri/IPC**，只依赖 types、codex::config/home、backend::app_server::WorkspaceSession、storage。app 与 daemon 编译进同一二进制、各自调用同一套 `*_core` 函数。

### 6.1 worktree 核心（`workspaces_core/worktree.rs`，642 行）

`add_worktree_core` 是**高度泛型**函数（FSpawn/FGit/FSanitize 等 9 个闭包注入），核心就是 `git worktree add`，三种分支情形：分支已存在 → `worktree add <path> <branch>`；本地无但有远程跟踪 → `worktree add -b <branch> <path> <remote_ref>`；否则 `worktree add -b <branch> <path>`。

- 根目录优先级：workspace 级 `worktrees_folder` > 全局 `global_worktrees_folder/<parent_id>` > `data_dir/worktrees/<parent_id>`。
- 创建后生成 `WorkspaceEntry{kind: Worktree, parent_id, worktree:{branch}}`；可 `copy_agents_md` 继承父仓库 agents.md；用 marker 文件记录一次性 `worktree_setup_script` 是否已跑。
- `remove_worktree_core`：kill session → `git worktree remove --force` → `git worktree prune --expire now`。
- `rename_worktree_core`：`git branch -m` + `git worktree move`（**带完整回滚**）。

### 6.2 git / GitHub（`git_ui_core/*`）

**混合实现**（本分析品味亮点）：
- **读操作走 libgit2（git2 crate）**：status/diff（`diff.rs` StatusOptions/Status 位枚举映射 A/M/D/R/T、DiffOptions patch、10MB 图片 base64、2MB 文本上限、`has_ignored_parent_directory`）；log（`log.rs` revwalk + `graph_ahead_behind`）。忽略文件识别才用 `git check-ignore --stdin -z` CLI。
- **写操作走 git CLI**（`commands.rs`，tokio_command）：commit/push/pull（`--autostash`→`--no-rebase` 回退链）/fetch/stage/revert/checkout/`git init`。
- **GitHub 全走 gh CLI**（`github.rs`）：`gh issue list --json`、`gh pr list --json`、`gh pr diff --color never`（`parse_pr_diff` 文本解析器切分文件）、`gh api /repos/.../issues/N/comments --jq`、`gh pr checkout`、`gh repo create`。

### 6.3 账号与用量

- `account.rs`（221 行）：`account/rateLimits/read` + `account/read`，失败回退解析 `$CODEX_HOME/auth.json` → idToken → JWT → `chatgpt_plan_type`/email（第 4 章已对照官方格式）。
- `local_usage_core.rs`（840 行）：扫描 `$CODEX_HOME/sessions/<YYYY-MM-DD>/*.jsonl`（含各 workspace 独立 codex_home），聚合每日 input/cached/output tokens、agent_time_ms、agent_runs，算 last7/last30、缓存命中率、峰值日、top4 模型；`MAX_ACTIVITY_GAP_MS=2min` 归并 run。

### 6.4 多 agent 配置（`agents_config_core.rs`，1026 行）

读写 `[features] multi_agent`、`[agents] max_threads/max_depth`（默认 6/1，钳制 1-12/1-4）；agent 定义为 `[agents.<name>]` 表，managed agent 配置落在 `$CODEX_HOME/agents/<name>.toml`。CRUD 齐全（create 模板生成、update 重命名同步改文件名、delete），含**路径穿越防护与配置回滚**。

### 6.5 设计品味

- **shared 独立的原因**：app 与 daemon 双进程共享同一 crate 的纯逻辑；所有状态经 `Mutex<HashMap>` 注入、副作用（git/fs/RPC）经闭包或类型注入——**天然可单测**（tests.rs 437 行）。
- **RPC 复用**：`git_rpc.rs`/`workspace_rpc.rs` 只声明方法名常量 + DTO（`to_params/from_params`），实现全在 `*_core`——daemon 的 handler 与 app 的 client 共用同一套签名，一层薄映射即可。
- **混合 git 策略**：读用 libgit2（快、纯内存、类型安全），写用 CLI（行为保真），worktree 编排也用 CLI——**务实而非教条**。

---

<h2 id="ch7">第 7 章 worktree 与工作区：一等对象</h2>

### 7.1 工作区模型（`workspaces_core`）

`WorkspaceEntry`：id/name/path/kind/parent_id/worktree/settings。kind 有普通仓库、worktree、clone 三种。持久化在 `workspaces.json`（app data 目录）。

- **add_workspace**：本地路径或 `git clone`。
- **add_worktree**：从父仓库建 worktree（第 6.1 章）。
- **add_clone**：URL clone 新工作区。
- 每个工作区可有独立 settings（codexBin、codex_home、worktrees_folder）。

### 7.2 与 kanban 的 worktree 对照

| | kanban | CodexMonitor |
|---|---|---|
| 粒度 | 一卡一 worktree（任务级） | 一工作区可建多个 worktree（agent 级） |
| 身份 | worktree 是卡片的"影子" | **worktree 是 workspace 的一种 kind（一等对象）** |
| 生命周期 | 卡片 trash 即删 | 可增删改查、改名（带回滚）、改上游 |
| 目的 | 并行任务的资源隔离 | 给 agent 独立分支 + 独立 codex home |

**品味观察**：kanban 把 worktree 当作任务执行的副产物；CodexMonitor 把 worktree 当作可管理的实体（`WorktreeEntry` 可 rename/pin/archive）——**它是"工作区管理产品"而非"任务看板"**，产品意图决定了数据模型。

---

<h2 id="ch8">第 8 章 git / GitHub：libgit2 读 + CLI 写 + gh 集成</h2>

（核心机制已在第 6.2 章覆盖，本章补充前端面）

- **diff 面板**：渲染用 `@pierre/diffs` + 自建 `diffsWorker`（WorkerPool）+ `@tanstack/react-virtual` 虚拟列表，主题在 design-system/diffViewerTheme。
- **stage/unstage/revert**：`stage_git_file` / `unstage_git_file` / `revert_git_file`（useGitActions）。
- **行内评论**：GitHub PR review——`usePullRequestLineSelection` 选行、`get_github_pull_request_comments` 拉取、提交为独立 review thread。
- **"Ask PR"**：PR 被选中即 `prefillDraft` PR 摘要进 composer；`runPullRequestReview`：确保 workspace 已连接 → 复用当前 thread 或新建 → `buildPullRequestReviewPrompt`（PR 元数据 + diffs ≤6 文件×40 行 + comments ≤8 + intent 指令，安全 fence）→ `sendUserMessageToThread` 送进新 agent thread 并 activate。

**品味观察**："Ask PR"是"把 GitHub 流程交给 agent"的妙笔——agent 能读 PR 上下文、按意图干活，这正是 CodexMonitor 相对传统 git GUI 的差异化价值。

---

<h2 id="ch9">第 9 章 远程 daemon 与 iOS：一套 core 两份形态</h2>

`src-tauri/src/bin/` 共 4,902 行：daemon 入口（1966）+ daemonctl（1414）+ rpc 域处理（codex 510 / workspace 273 / rpc.rs 201 / git 196 / prompts 149 / daemon 60）+ transport（103）+ dispatcher（30）。

### 9.1 daemon 架构

- **协议**：TCP + **行分隔 JSON-RPC**（JSONL，每行一个 JSON 对象），非 HTTP。默认 `127.0.0.1:4732`（daemon 内部）/ `0.0.0.0:4732`（daemonctl 对外）。
- **帧格式**：请求 `{"id":n,"method","params"}`；响应 `{"id":n,"result"|"error"}`；事件无 id：`{"method":"app-server-event","params":...}`（`broadcast::channel(2048)` 推给所有已认证连接）。
- **认证**：`auth` 握手。token 来自 `--token` 或 `CODEX_MONITOR_DAEMON_TOKEN`；未认证时非 auth 请求一律返回 `unauthorized`；`--insecure-no-auth` 跳过。并发限流：每连接 `Semaphore(32)`。

### 9.2 RPC 分发与复用度

`dispatcher.rs` 按序 try_handle：daemon → workspace → codex → git → prompts。**复用度极高**：daemon 入口用 `#[path=]` 直接把主程序模块（backend/shared/files/workspaces_core 等）include 进来，`DaemonState` 方法几乎 1:1 包装 `xxx_core`——与本地 Tauri 命令共用同一批 core。差异仅是 `AppState` → `DaemonState` + `DaemonEventSink`；`menu_set_accelerators` 降级 no-op（"remote parity"）。

### 9.3 daemonctl 生命周期控制（1414 行里的大半）

- **status**：`probe_daemon` 三步状态机——`ping`（未认证则收到 unauthorized）→ `auth` → `daemon_info`；产出 `NotReachable / Running{auth_ok} / NotDaemon`。
- **start**：先探测；在跑但身份/版本/mode 不匹配（`should_restart_daemon`）→ RPC `daemon_shutdown` 优雅停机 → 轮询 20×100ms → 仍不退则 `lsof -iTCP:<port>` 解析 PID → SIGTERM → SIGKILL。**仅当认证通过且进程名是 `codex-monitor-daemon` 才允许强杀**（`can_force_stop_daemon`）。
- **无日志文件、无 PID 文件**——PID 全靠端口反查（极简主义，但依赖 lsof）。
- **command-preview**：输出完整启动命令模板（token 用 `<remote-backend-token>` 占位符）。

### 9.4 为什么 iOS 要远程模式

iOS 无桌面资源：无法 spawn `codex app-server` 子进程、无法跑 git、无本地 workspace 文件系统——做成**瘦客户端**连桌面 daemon。`terminal_mobile.rs` 与 `dictation/stub.rs` 全部命令直接返回 `"Terminal/Dictation is not available on mobile builds."`（终端需要本地 PTY；听写需要本地 Whisper 模型）。

### 9.5 Tailscale 集成

`tailscale_status`：扫描候选二进制（PATH → homebrew → Tailscale.app bundle）→ `tailscale version` 验证（`looks_like_tailscale_version` 防 GUI 错误输出）→ `tailscale status --json`。macOS 用 `launchctl asuser <uid>` 以 GUI 用户身份执行。`suggested_remote_host` 解析 `DNSName/TailscaleIPs/CurrentTailnet` 生成 `macbook.tailnet.ts.net:4732`。iOS 端直接把建议 host + token 填进 `Settings > Server`。

### 9.6 品味观察

~5000 行看似多，**核心逻辑全是复用的薄壳**，真正新增的是三层粘合：`#[path]` 模块拼接、JSON 参数解析样板、daemonctl 生命周期管理。**为什么值得**：移动端无法本地跑 codex，若不这么做就得为 iOS 单独重写整个 backend——"一套 core + 一份协议"同时服务桌面与 iOS，行为天然一致。代价：桌面端多了一类"进程 + 端口 + 版本漂移"运维复杂度，TCP 明文 token 依赖 Tailscale 私有网络而非自身加密。

---

<h2 id="ch10">第 10 章 前端架构：feature-sliced + 零状态库</h2>

### 10.1 规模与组织

`src/` 共 603 个 ts/tsx/css 文件、约 127K 行；`src/features/` 496 文件 / 103K 行，25 个 feature，采用 feature-sliced 的 `components/hooks/utils` 子目录约定：

| feature | 文件/行 | 职责 |
|---|---|---|
| app | 141 / 27,205 | 启动、布局、侧栏、控制器（最重） |
| threads | 70 / 20,636 | 聊天线程状态机 |
| settings | 42 / 12,111 | 设置页 |
| git | 59 / 9,099 | diff/log/branch/GitHub |
| workspaces | 36 / 6,931 | 工作区管理 |
| composer | 32 / 6,270 | 输入框 + 自动补全 |

### 10.2 入口与路由

- **无路由库，纯状态驱动**。`App.tsx`（64 行）用 `useWindowLabel()`（读 Tauri 窗口 label）分流——`about` 窗口懒加载 `<AboutView>`，否则 `<MainApp />`。
- `main.tsx`：Sentry 初始化 + `app_open` 计数 + 移动端手势/视口兼容。
- `MainApp.tsx`（1882 行）**巨型组合根**：无 Context，通过 ~70 个 hook 的返回对象逐层传 props。视图切换靠 `activeTab` state（`home|projects|codex|git|log`）+ `centerMode`（chat/diff）+ `showHome`。

### 10.3 状态管理选型（品味核心）

**零外部状态库**：无 redux/zustand/jotai/mobx（已 grep 验证）。纯 `useReducer` + `useState` + `useRef`，通过 props 显式下钻。

- `threadReducer`（1179 行，6 个 slice，无第三方）：threadLifecycleSlice（493 行）/ threadItemsSlice（333 行）/ threadConfigSlice / threadQueueSlice / threadSnapshotSlice / common。约 40 个 action（`ensureThread`/`upsertItem`/`appendAgentDelta`/`appendToolOutput`/`appendReasoningSummary`/`markProcessing`…）。
- `useThreads.ts`（938 行，主聚合）内部用 **ref 镜像 state**（`itemsByThreadRef` 等）供事件回调同步读取——解决"闭包捕获旧状态"的经典问题。
- 事件到 reducer 的桥：`useThreadEventHandlers` → `useThreadTurnEvents` / `useThreadItemEvents` / `useThreadApprovalEvents` → 各回调 dispatch。

**为什么不用外部 store**（品味判断）：
1. 单 workspace 桌面应用，状态图收敛到"一个 activeWorkspace + 一堆 threads"，reducer 足够；
2. 事件密集流式更新（delta 级），需要 ref 镜像 + dispatch，外部 store 收益不大；
3. feature-sliced 本身提供模块边界，避免全局 store 的耦合；
4. 代价是 MainApp 巨型 props 传递和大量 useMemo/useCallback 胶水。

### 10.4 组件库与样式

**无 Tailwind、无 Radix、无 shadcn**。依赖仅 `lucide-react`（图标）、`react-markdown`+`remark-gfm`、`prismjs`、`@xterm/xterm`、`@tanstack/react-virtual`、Sentry。UI 全手写：CSS 变量 tokens（`ds-tokens.css`）+ 语义化类名，组件是普通 function component 拼出来的 `ModalShell/ToastShell/PanelShell`。

### 10.5 与 kanban 前端对比

架构同构（后端进程 + Tauri/浏览器前端 + 事件流 fanout + reducer 驱动 UI），但 CodexMonitor 的**状态管理明显更重**——reducer 1179 行/40 actions、事件解析 hook 数千行、ref 镜像同步、多 workspace 键控状态。差异根源：kanban 卡片状态是粗粒度快照（fetch 后整体替换），CodexMonitor 要承载**流式 delta 渲染**（agentMessage 逐 delta append、tool 输出流、reasoning 流）+ 审批队列 + token/rate-limit 实时面板 + subagent 级联归档。但架构层反而更轻：无 store 中间件、无 selector 库、无异步 action，复杂度靠 feature-sliced 目录和巨量单测压制。

---

<h2 id="ch11">第 11 章 前端功能特性：Composer / 听写 / diff / 用量</h2>

### 11.1 Composer 自动补全（完全自主实现）

`useComposerAutocompleteState.ts` 组装三组 triggers：`$`（skills+apps）、`/`（8 个硬编码 slash 命令 + prompts）、`@`（files）。**数据全部走 RPC**：skills→`skills_list`、apps→`apps_list`、prompts→`prompts_list`（均经 daemon 转发到 codex app-server）；files→`list_workspace_files`（Rust 直接扫盘，上限 500）。触发判定/子序列打分/高亮**全部手写**，UI 为 `ComposerSuggestionsPopover` 定位在 textarea 上方，无第三方补全库。`$` 选中 app 生成 `app://id` mention 绑定；`/prompts:` 支持 `findNextPromptArgCursor` Tab 跳转参数位。

### 11.2 Queue vs Steer（交互打磨的典范）

`ComposerSendIntent = "default"|"queue"|"steer"`。`resolveSendMessageOptions`：`canSteerCurrentTurn = isProcessing && steerEnabled && activeTurnId`。steer → RPC `turn_steer`；queue/不支持时 → `send_user_message`（turn/start，消息入队由 app 侧 ComposerQueue 管理）。默认模式取设置 `followUpMessageBehavior`；**Shift+Cmd/Ctrl+Enter 发相反模式**（`oppositeSubmitIntent`）。失败区分 `steer_failed` 且处理 stale-steer 错误，运行时按钮文案在 Steer/Queue 间切换。

### 11.3 图片附件

三入口均落为**本地路径数组**（`useComposerImages` 按 thread draftKey 存）：picker 用 tauri `open`；drag-drop 用窗口级 `onDragDropEvent` + 本地 DOM drop（web 端 FileReader 转 data URL）；paste 由 `useComposerImageDrop.handlePaste` 读 clipboardData。**传给 codex 是 data URL 字符串数组**：`normalizeImagesForRpc` 对 remote/mobile 模式调用 Rust `read_image_as_data_url` 逐个转换。

### 11.4 听写（Whisper）

Rust 侧 **whisper-rs** 本地推理，`real.rs` 定义 ggml-tiny/base/small/medium/large-v3 从 HuggingFace 下载；download/cancel/remove/status 走 daemon RPC，进度事件推前端。waveform 是前端自绘 36 条动态 bar（`DictationWaveform`，订阅 level 事件）。

### 11.5 用量与通知

- **用量**：Home usage = `local_usage_snapshot(30天)`（Rust 读 codex home 数据，`useLocalUsage` 5 分钟轮询）；侧栏 credits meter = `account/rateLimits/updated` AppServerEvent → RateLimitSnapshot → `getUsageLabels`。
- **通知**：纯前端驱动，订阅 AppServerEvents（turn 完成/审批请求）；系统通知用 `@tauri-apps/plugin-notification`；Rust 仅 `send_notification_fallback`——macOS debug 下用 **osascript AppleScript**（非 notify_rust）。声音用本地 mp3 资源 + HTMLAudio。均有最小间隔/窗口聚焦抑制。

### 11.6 品味观察

**倾向自主实现**：补全引擎、prompt 历史、rank 打分、waveform、usage view model 全手写；仅把重活交给库（`@pierre/diffs`、xterm、whisper-rs、tauri 插件、react-virtual）。**交互打磨**：stale-steer 容错、drag 位置 devicePixelRatio 归一化、代码 fence 展开、`/prompts:` Tab 跳参、hold-to-talk、follow-up 提示条动态说明 Steer 可用性、mobile 触屏布局分支、每条 RPC 写 DebugEntry 可追踪。

---

<h2 id="ch12">第 12 章 配置三权分立：settings.json / workspaces.json / config.toml</h2>

| 文件 | 内容 | 归属 |
|---|---|---|
| `settings.json`（app data 目录） | AppSettings：codexBin/codexArgs、backendMode、remote backend host/token、快捷键、defaultAccessMode、uiScale/theme、global_worktrees_folder | CodexMonitor 自身 |
| `workspaces.json` | WorkspaceEntry 列表（id/name/path/kind/worktree/settings） | CodexMonitor 自身 |
| `config.toml`（`$CODEX_HOME`） | codex 自身：features（steer/collaboration_modes/unified_exec/apps）、personality、model | **codex 是 source of truth** |

**同步策略**（`settings_core.rs`，70 行 + `codex/config.rs`）：5 个与 config.toml 同步的字段（collaboration_modes_enabled/steer_enabled/unified_exec_enabled/experimental_apps_enabled + personality）在**读写两个方向都双向同步**——`get_app_settings_core` 从 config.toml **覆盖**内存值（codex 权威）；`update_app_settings_core` 反向写回 config.toml（通过 `config_toml_core.rs` 的 toml_edit）+ 写本地 storage。

**品味观察**：尊重"codex 的配置归 codex"——App 只同步自己 UI 暴露的那几个开关，不做全量镜像，避免两套配置打架。第 4 章已证实键名与官方完全吻合。

---

<h2 id="ch13">第 13 章 工程化与测试：AGENTS.md 分层纪律 + 单测密度</h2>

### 13.1 AGENTS.md（6KB 部落知识）

- 领域逻辑 → shared/*；命令 → 薄适配层；前端 IPC 只经 services/tauri.ts；事件 fanout 只在 services/events.ts。
- 前端 feature-sliced：`src/features/{app,threads,...}`，编排在 hooks/* 与 bootstrap/*。
- daemon 与 app 复用 shared core，方法名/负载对齐（remote parity）。
- `docs/codebase-map.md`：任务导向的文件查找（"if you need X, edit Y"）——**这是为 agent 开发的导航文档**。

### 13.2 测试密度

- Rust 侧：`git_ui_core/tests.rs` 437 行、tray.rs 6 单测、files io.rs 6 个 symlink 逃逸测试、worktree/workspaces 各有测试——**纯逻辑层因闭包注入而可测**。
- 前端：每个 hook 都有 `*.test.ts(x)`（useSettingsDefaultModels、useThreads、composer 等）。
- 集成：`main.test.tsx`、`smoke` 等。

### 13.3 与 codex / kanban 的工程文化对照

- **codex**：bazel monorepo、126 crate、AGENTS.md 长而全（含 app-server API 最佳实践）。
- **kanban**：GritQL 静态检查 + husky + dogfood（用 kanban 开发 kanban）。
- **CodexMonitor**：分层纪律写入 AGENTS.md（agent 可读可执行）、docs/codebase-map.md 导航、双平台（桌面 + daemon）共用 core 的架构测试、`.codex/` 目录（项目自己用 codex 开发）。

**共性**：三个项目都被 **agent 优先开发范式** 塑造——AGENTS.md 不是文档而是"给 agent 的工程契约"，代码库结构要为 agent 的可导航性服务。

---

<h2 id="ch14">第 14 章 开发思路与品味：Dimillian 的十个选择</h2>

> 本章是全文重心。Dimillian（Thomas Ricouard）以 SwiftUI 开源组件闻名（ACarousel 等），却为一个"编排 AI agent 的工具"选择了 **Tauri（Rust + Web 前端）**。这个选择本身就透露了大量信息。以下十个选择，每个都标注了源码证据。

### 选择一：用官方协议直连，而非黑盒抓取

CodexMonitor 没有走"spawn codex 的 TUI 然后解析屏幕"的路（那是 kanban 对 codex 做的事），而是 `codex app-server` + stdio JSON-RPC。**这显示了对"上游生态"的尊重**：官方提供插座（app-server 协议是给 IDE 设计的），就用插座；宁可绑定 codex 生态，也不做脆弱的屏幕适配。第 4 章证实所有 22 个方法全部是官方协议方法、握手参数逐项吻合、config 键名一致、auth JWT 解析对应、rules 语法正确——**这是一份"契约级对齐"的代码**，背后是逐字段读官方源码的耐心。

### 选择二：shared-core + 薄命令 + 闭包注入

领域逻辑（git、worktree、codex、settings、usage）全部收进 `shared/*` 纯逻辑层，Tauri 命令层只做"本地 or 转发 daemon"的薄壳，平台副作用（git 命令、fs、spawn）一律闭包注入。**这是非常 Rust 的架构观**：类型化的依赖注入 + 纯函数可测。效果：app 与 daemon 双进程共用一套 core，逻辑零漂移；单测可以直接构造内存态测试核心（git_ui_core/tests.rs 437 行）。

### 选择三：读用 libgit2，写用 CLI，GitHub 用 gh

不教条——读操作要"快且类型安全"（libgit2 纯内存），写操作要"行为与 git 完全保真"（CLI），GitHub 直接复用 `gh`（不重复造 API 客户端）。**每个场景选最合适的工具**，而不是统一抽象。这是老练的工程判断：libgit2 的写路径坑多（配置差异、hooks），读路径稳。

### 选择四：worktree 是一等对象，不是任务副产物

对比 kanban（worktree 是卡片的影子，trash 即删），CodexMonitor 把 worktree 做成 `WorkspaceEntry{kind: Worktree}` 的完整实体：可增删改查、改名（带回滚）、改上游、继承 agents.md。**产品意图决定数据模型**——它是"工作区管理产品"，worktree 是给 agent 的独立执行环境（独立分支 + 独立 codex home）。

### 选择五：前端零状态库 + feature-sliced

无 redux/zustand，纯 useReducer + ref 镜像 + 显式 props 下钻；无 Tailwind/Radix，CSS 变量 tokens + 手写组件；UI 侧只用 6 类库。**"复杂度靠目录结构和纪律压制，不靠库"**。流式 delta 渲染需要 ref 镜像 + dispatch（useThreads.ts 938 行），外部 store 在这里收益不大；feature-sliced 的 25 个 feature 目录提供了足够强的模块边界。

### 选择六：能自己写的就自己写

补全引擎（触发判定/子序列打分/高亮）、prompt 历史、rank 打分、waveform（36 条动态 bar）、usage 聚合（840 行 JSONL 解析）、frontmatter 解析、rules 去重、锁文件——**全部手写**，只把重活交给库（xterm、whisper-rs、@pierre/diffs、react-virtual）。**依赖极简主义**：前端 600 多个文件，运行时依赖只有 19 条，其中 UI 相关的仅 6 类。

### 选择七：macOS 优先，但不止 macOS

托盘仅 macOS（tray.rs 701 行，非 macOS 空实现）、窗口外观用 objc2-app-kit 直接调 NSAppearance、图标解析调 defaults/sips——**原生平台 API 直接调，不抽象**；同时 Windows opt-in（separate tauri config 避免 macOS-only 窗口效果）、iOS 远程模式。这是"主平台做到极致，其他平台务实跟进"的策略。

### 选择八：iOS 远程 daemon——一套 core 两份形态

iOS 无法本地跑 codex，就起一个桌面 daemon（TCP JSONL JSON-RPC）让 iPhone 连 Tailscale 用。`#[path]` include 复用主程序模块、daemonctl 用端口反查 PID + 版本比对 + 强杀安全闸门管理生命周期。**这是"为多端形态做架构投资"**——一份核心 + 一份协议，桌面/iOS 行为天然一致；代价是 daemon 进程/端口/版本漂移的运维复杂度（daemonctl 1414 行里大半在防"杀错进程"）。

### 选择九：agent 优先开发

AGENTS.md 是"给 agent 的工程契约"（分层规则、remote parity、feature-sliced 约定）；docs/codebase-map.md 是"if you need X, edit Y"的任务导航；仓库自带 `.codex/` 配置——**这个项目自己就是用 codex 开发的**（与 kanban 的 dogfood 同构）。代码库结构为 agent 的可导航性服务，这是 2025-2026 新一代 AI 原生项目群的共同特征。

### 选择十：防御性编程 + 交互打磨

canonicalize + symlink 逃逸校验（6 个测试）、400KB 读取截断、严格 UTF-8、Arc::ptr_eq 防竞态、stale-steer 容错、断线 DISCONNECTED_MESSAGE——**工程上的保守**；同时 Shift+Cmd+Enter 反转 Queue/Steer、hold-to-talk 波形、`/prompts:` Tab 跳参、follow-up 提示条动态说明——**产品上的进取**。保守的工程 + 进取的交互，是成熟独立开发者的典型气质。

### 一句话总结品味

> **用最少的依赖、最干净的架构分层，把官方协议用到位，做成一个"给 codex 的指挥台"——工程上克制保守，产品上打磨入微，架构上为多端形态提前投资。**

---

<h2 id="ch15">第 15 章 总结：三个最独特的设计与取舍</h2>

### 三件事（如果只记三个设计）

**1. 官方 app-server 协议直连，而非 PTY 抓屏**
这是 CodexMonitor 与 kanban 最本质的分野。kanban 要兼容 7 种 Agent 所以必须黑盒适配；CodexMonitor 只服务 codex 所以走官方插座（LSP 式握手 + 类型化事件 + 双向控制）。**取舍**：深而不广——放弃"任意 agent 可插"，换来"codex 的每一层能力都拿到"（steer 打断、审批应答、sandboxPolicy 精确传递、account/rateLimits 实时）。

**2. shared-core 一套核心，桌面/iOS 两份形态**
领域逻辑收进纯逻辑层（闭包注入副作用），app 与远程 daemon 用 `#[path]` include 共用同一批 `*_core`。**这是为 iOS 远程模式做的架构投资**——不这么做就得为移动端重写整个 backend。代价是 daemon 运维复杂度（进程/端口/版本漂移）。

**3. 前端零状态库 + 流式 delta 状态机**
无 redux/zustand/Tailwind/Radix，纯 useReducer + ref 镜像 + 手写 CSS tokens，前端 600 多个文件、19 条运行时依赖（UI 库仅 6 类）。事件驱动状态机（app-server 事件 → hub fanout → reducer slice）承载流式渲染。**取舍**：MainApp 巨型 props 胶水换来了零中间件复杂度——单 workspace 桌面应用的明智选择。

### 与 codex / kanban 的一页对照

| 维度 | codex（官方） | kanban | CodexMonitor |
|---|---|---|---|
| 角色 | Agent 本体 | 多 Agent 编排看板 | codex 专用桌面指挥台 |
| 集成路径 | — | PTY + hooks（黑盒） | app-server 协议（官方插座） |
| 状态模型 | rollout 文件资产 | 卡片 4 列状态机 | workspace × thread 事件流 |
| worktree | 无 | 一卡一 worktree | worktree 为一等 workspace |
| 前端 | TUI | React 看板 | React feature-sliced |
| 哲学 | 本地优先 CLI | 浏览器控制面 | 桌面 + iOS 远程 |

### 适合谁 / 不适合谁

- **适合**：重度 Codex 用户（桌面端体验远胜终端）、想多 agent 并行且要 worktree 隔离的开发者、iOS + Tailscale 移动办公场景、研究"官方协议集成"范式的读者。
- **不适合**：非 codex 用户（绑定生态）、需要浏览器访问的团队（无 web 版）、轻量用户（安装 Rust toolchain + codex + gh 门槛不低）。
- **与本系列的连接**：它是 **OpenAI Codex 源码分析**的"加深版实战印证"（协议、rollout、config、auth 全部对得上号），也是 **Kanban 源码分析**的"官方协议对照组"。

### 🔍 源码指路（回源码核对）

| 想知道 | 看这里 |
|---|---|
| app-server spawn 与握手 | `src-tauri/src/backend/app_server.rs`（L749 spawn、:419 握手） |
| codex 方法调用全集 | `src-tauri/src/shared/codex_core.rs`（1033 行） |
| 事件分类路由 | `src-tauri/src/backend/app_server.rs`（extract_related_thread_ids / resolve_workspace_for_cwd） |
| shared 领域核心 | `src-tauri/src/shared/`（31 文件 11,197 行） |
| worktree 核心 | `src-tauri/src/shared/workspaces_core/worktree.rs`（642 行） |
| git 混合策略 | `src-tauri/src/shared/git_ui_core/`（libgit2 读 + CLI 写） |
| rollout 用量统计 | `src-tauri/src/shared/local_usage_core.rs`（840 行） |
| 远程 daemon | `src-tauri/src/bin/codex_monitor_daemon.rs` + `codex_monitor_daemonctl.rs` |
| 前端事件 hub | `src/services/events.ts`（createEventHub 懒订阅） |
| 前端状态机 | `src/features/threads/hooks/threadReducer/`（1179 行 6 slices） |
| Composer 补全 | `src/features/composer/`（useComposerAutocompleteState.ts） |
| codex 协议定义 | `参考项目/codex/codex-rs/app-server-protocol/src/protocol/common.rs` |
| codex 握手实现 | `参考项目/codex/codex-rs/app-server/src/request_processors/initialize_processor.rs`（:70–101） |
| codex 最佳实践 | `参考项目/codex/AGENTS.md`（:104 app-server 破坏性变更面） |
