TUI 应用层

KimiTUI 协调器与控制器分工、反向 RPC 的模态协调、slash 命令注册表、流式渲染,以及只经 SDK 的引擎边界。

作者 David更新于 第 25 篇(共 33 篇)

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 是体验的关键。模型流式返回时要:

  1. 增量追加文本,不重绘整屏(靠 pi-tui 的差分渲染),刷新按 50ms 节流(STREAMING_UI_FLUSH_MS)。
  2. 思考内容与正文分开展示。
  3. 工具调用实时显示参数预览与进度。
  4. 压缩、子代理、后台 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-sdk and 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-tui skill (.agents/skills/write-tui/SKILL.md).

仓库把自己的 TUI 编写规范做成了 skill——架构地图、新功能落点、测试位置、主题机制与对话框设计规范(DESIGN.md)都在里面,给 AI 助手看,也给人看。这是 agent-assisted 开发的一个有意思的实践。

第 4 部分结束。下一篇进入minidb。

本页目录