Skip to content

交互模式(TUI 接入) ​

这一层回答:agent 事件怎么变成屏幕上的内容,用户输入怎么变成 prompt、steer 或 abort。

两个包的分工 ​

text
packages/tui                      通用终端 UI 框架
  组件:Container / Box / VStack / HStack / Text / Editor / Markdown /
        ScrollView / SelectList / SettingsList / Image / Loader
  基础设施:Terminal、TUI、keybindings、colors、terminal-image、差分渲染

packages/coding-agent/src/modes/interactive   应用层
  布局、事件映射、消息与工具组件、footer、状态指示器、扩展 UI

packages/tui 完全不知道 agent 的存在,只提供渲染和输入原语。

接线 ​

text
subscribeToAgent()
  -> session.subscribe(handleEvent)

handleEvent() 每次先把 footer 标记为需要重绘、把事件交给 program status reporter,然后按 event.type 分支。

事件到 UI 的映射 ​

事件UI 行为
agent_start清空 pending tools,恢复默认 Escape handler
turn_start终端进度、working 指示器
queue_update刷新待投递消息显示
entry_appended按 entry 类型渲染 custom entry、custom message、compaction 摘要或缓存预热用量
session_info_changed更新标题和 footer
thinking_level_changed更新 footer 和编辑器边框颜色
message_startuser/custom 直接入 chat;assistant 创建流式组件
message_update更新流式组件;出现 toolCall 块时创建工具组件并更新参数
message_end完成渲染;错误或中止时把 pending tools 标为错误
tool_execution_start复用或创建工具组件,标记开始(嵌套调用跳过)
tool_execution_update用部分结果更新组件
tool_execution_end写入最终结果和 durationMs,从 pending 移除
agent_end关进度、清流式状态和 pending tools
agent_settled检查是否有待执行的退出请求
compaction_start / compaction_endcompaction 状态指示器,Escape 改为取消压缩
auto_retry_*重试状态指示器
bash_execution_update由 bash 执行回调自己渲染

关键字段:

text
chatContainer        消息和工具行的容器
streamingComponent   当前流式 assistant 消息组件
streamingMessage     对应的消息对象
pendingTools         toolCallId -> ToolExecutionComponent

entry_appended 收到 compaction 时会重建 chat:清空容器,按新的 buildContextEntries() 重新渲染保留历史,再按顺序插入压缩摘要和之后的 entry。

工具渲染 ​

ToolExecutionComponent 会查扩展注册的 ToolDefinition renderer,没有就退到默认渲染。message_update 阶段可能在参数还没流完时就提前创建组件,所以有 updateArgs() 和 setArgsComplete() 两个阶段。

嵌套调用(来自 codemode 之类的 ctx.executeTool())带 parentToolCallId,不进入顶层 pending map,画在父工具行内部。

用户输入 ​

提交路径大致是:

text
Enter
  以 "/" 开头     -> 扩展命令
  "!" / "!!"      -> bash(!! 不进模型上下文)
  compaction 中   -> 队列,等压缩结束
  streaming 中    -> prompt(text, { streamingBehavior: "steer" })
  空闲            -> onSubmit -> onInputCallback 或 pendingUserInputs

Alt+Enter         -> followUp(streaming 时)
Escape            -> abort(retry / compaction 阶段会换成对应 handler)

队列显示来自 session.getSteeringMessages() / getFollowUpMessages(),restoreQueuedMessagesToEditor() 可以把排队消息还原回输入框。

扩展如何接 UI ​

ctx.ui(ExtensionUIContext)提供:

text
状态与通知         setStatus / notify
对话框             select / confirm / input / editor / custom
编辑器             pasteToEditor / setEditorText / getEditorText / setEditorComponent
布局               setFooter / setHeader / setTitle
主题               theme / getAllThemes / getTheme / setTheme

另外扩展可以注册快捷键、工具渲染器、消息渲染器、Markdown transformer 和补全提供者。补全列表会把扩展命令、prompt template、skill 一起合并进去。

易错点 ​

  • message_update 会乐观地创建工具组件;失败或中止时由 message_end 统一补错误结果。
  • compaction 的 entry_appended 会重建整个 chat 容器,不要在其中假设已有组件的引用还稳定。
  • session.isStreaming 涵盖 run 和 run 之后的续跑阶段,判断「能不能发消息」用 isIdle 更合适。
  • 嵌套工具调用在事件层是完整的,但顶层 UI 只显示父行。

建议阅读顺序 ​

  1. packages/tui/src/index.ts 了解框架导出了什么
  2. packages/coding-agent/src/modes/interactive/interactive-mode.ts
    • 字段声明部分
    • subscribeToAgent() / handleEvent()
    • 提交流程 editor.onSubmit
    • handleFollowUp() / handleDequeue()
  3. 需要时再看具体组件
    • components/tool-execution.ts
    • components/assistant-message.ts
    • components/footer.ts