TUI 应用层
KimiTUI 协调器与控制器分工、反向 RPC 的模态协调、slash 命令注册表、流式渲染,以及只经 SDK 的引擎边界。
apps/kimi-code/src/tui/ 是把 pi-tui 组装成实际应用的一层。apps/kimi-code/AGENTS.md 给出了入口链:
src/main.ts -> src/cli/commands.ts -> src/cli/run-shell.ts -> SDK KimiHarness -> src/tui/kimi-tui.ts结构
tui/
├── kimi-tui.ts KimiTUI 协调器(4190 行)
├── tui-state.ts 全局 UI 状态
├── controllers/ 控制器(按关注点拆分,15 个)
├── commands/ slash 命令的声明、解析与注册表
├── components/ chrome/ dialogs/ editor/ media/ messages/ panes/
├── reverse-rpc/ 反向 RPC(审批、提问)
├── theme/ 主题与颜色令牌
├── constant/ utils/ TUI 专用常量与工具
├── banner/ 启动横幅
├── goal-queue-store.ts 待办目标队列
└── easter-eggs/ 彩蛋KimiTUI 被定位为协调器:持有状态、负责启停顺序、布局与编辑器接线、会话生命周期和 slash 命令分发,但不该堆积事件路由与渲染逻辑——那些归控制器。它从 v0.29.1 的 3085 行长到了 4190 行,可见这条约束需要持续维护。
tui-state.ts:99 在创建状态时决定用哪套渲染器:
const fullscreen = process.env['KIMI_CODE_TUI_FULL_SCREEN'] === '1';
const ui =
fullscreen
? new TuiAltScreen(terminal, undefined, undefined, {默认是主屏的 TuiMainScreen,环境变量打开后才用全屏的 TuiAltScreen;此时鼠标被接管,OSC 8 链接点击与 Windows 右键粘贴都改由应用自己处理。
控制器
controllers/ 按关注点拆开,每个只管一件事:
| 控制器 | 职责 |
|---|---|
session-event-handler.ts(1300 行) | 按事件类型分发 SDK 会话事件 |
streaming-ui.ts(910 行) | 流式渲染 |
session-replay.ts(899 行) | 恢复会话时的回放,复用实时渲染路径 |
editor-keyboard.ts | 编辑器键盘、退出快捷键、外部编辑器 |
subagent-event-handler.ts | 子代理事件与 swarm 进度 |
subagent-activity-store.ts | 后台子代理的活动记录,供任务浏览器查看 |
tasks-browser.ts | 后台任务浏览 |
auth-flow.ts | 登录流程 |
clipboard-image-hint.ts | 剪贴板出现新图片时提示可以粘贴 |
btw-panel.ts | /btw 旁路提问面板 |
另有 notify、cache-hint-controller、plugin-update-notifier、staging-leases、survey-controller 等较新的控制器。
session-event-handler.ts 的核心是一张分发表(:278 起,节选):
case 'turn.started': this.handleTurnBegin(event); break;
case 'turn.ended': this.handleTurnEnd(event, sendQueued); break;
// ...
case 'assistant.delta': this.handleAssistantDelta(event); break;
case 'hook.result': this.handleHookResult(event); break;
case 'thinking.delta': this.handleThinkingDelta(event); break;
case 'tool.call.started': this.handleToolCall(event); break;子代理事件单独交给 SubAgentEventHandler,是因为它们要挂到父工具卡片下折叠显示——README 说子代理在 isolated contexts 里跑,就是为了让主对话保持干净,UI 上也不能把子代理的每次工具调用都平铺出来。
反向 RPC
reverse-rpc/
├── approval/ 审批:adapter / controller / handler
├── question/ 提问:controller / handler
├── base-controller.ts 基于 Promise 的面板控制器基类
├── modal-coordinator.ts 模态协调
└── types.ts 面板数据形状这是 SDK 审批与提问回调在 TUI 侧的落地——引擎请求审批或提问时,需要在终端弹出交互,再把用户的选择转回 SDK 响应。
协调分两层。ReverseRpcController 保证同一类请求一次只显示一个面板,其余按到达顺序排队——多个并行子代理同时要审批时正是这种情况;用户选了「本会话都允许」,队列里动作相同的审批会被自动放行。ReverseRpcModalCoordinator 再管审批与提问之间的互斥(modal-coordinator.ts:68):
private show(entry: ReverseRpcModalEntry): void {
const active = this.active;
if (active === null) {
this.active = entry;
entry.show();
return;
}
if (active.owner === entry.owner) {
this.active = entry;
entry.show();
return;
}终端没有真正的窗口层级,只能靠协调器串行化,避免两个模态互相覆盖或界面错乱。
slash 命令
commands/registry.ts 的 BUILTIN_SLASH_COMMANDS 列出 40 余个内置命令,每条带名字、别名、描述、优先级、可用时机(always 或 idle-only)与参数补全。命令清单能反映产品面:
/login /logout /provider /model /effort 认证与模型
/settings /reload /experiments /theme 配置
/new /sessions /fork /title /undo /copy 会话操作
/permission /yolo /auto /plan 权限与计划模式
/goal /swarm /tower /tasks /btw 长程与协作
/mcp /plugins /add-dir 扩展与工作区
/web /remote-control /usage /status 其它动态命令另有两路:skill 生成 /skill:<name>(内置 skill 直接用名字,见 Skills),插件命令生成 /<插件>:<命令>(见插件与市场)。按 write-tui 的约定,commands/ 只负责声明、解析与类型,真正的执行在 KimiTUI 的命令处理段里分发。
流式渲染
streaming-ui.ts 是体验的关键。模型流式返回时要:
- 增量追加文本,不重绘整屏(靠 pi-tui 的差分渲染),刷新按 50ms 节流(
STREAMING_UI_FLUSH_MS)。 - 思考内容与正文分开展示。
- 工具调用实时显示参数预览与进度。
- 压缩、子代理、后台 agent 的状态同样在这里汇总进 transcript。
它与 pi-tui 的关系是:pi-tui 提供「怎么高效画」,streaming-ui.ts 决定「画什么、何时画」。
状态与主题
tui-state.ts 是唯一的全局 UI 状态形状;goal-queue-store.ts 把 /goal next 排队的目标存进会话目录的 upcoming-goals.json。theme/ 是颜色的唯一来源:colors.ts 的 ColorPalette 定义语义令牌,theme-schema.json 描述自定义主题,内置 skill custom-theme 让用户能对话式定制;CI 守卫禁止直接使用 chalk.red 这类具名颜色。
与引擎的边界
根 AGENTS.md 的硬约束:
apps/kimi-code: the CLI / TUI application. It consumes core capabilities through@moonshot-ai/kimi-code-sdkand must not depend directly on engine packages.
src/tui/ 下确实只认 SDK(KimiHarness、Session 与事件类型),没有直接 import 引擎包。这让 TUI 与引擎能独立演进,也让同一套 TUI 逻辑理论上可以对接远程引擎(比如通过 kap-server)。严格说,CLI 的 print 模式与配置校验(src/cli/v2/)、插件市场工具(src/utils/plugin-marketplace.ts)目前仍直接引用 @moonshot-ai/agent-core-v2 的导出,kimi web 则直接启动 kap-server。
AGENTS.md 还要求写 TUI 时用仓库自带的 skill:
When writing or modifying its terminal UI, use the
write-tuiskill (.agents/skills/write-tui/SKILL.md).
仓库把自己的 TUI 编写规范做成了 skill——架构地图、新功能落点、测试位置、主题机制与对话框设计规范(DESIGN.md)都在里面,给 AI 助手看,也给人看。这是 agent-assisted 开发的一个有意思的实践。
第 4 部分结束。下一篇进入minidb。