Appearance
交互模式(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、状态指示器、扩展 UIpackages/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_start | user/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_end | compaction 状态指示器,Escape 改为取消压缩 |
auto_retry_* | 重试状态指示器 |
bash_execution_update | 由 bash 执行回调自己渲染 |
关键字段:
text
chatContainer 消息和工具行的容器
streamingComponent 当前流式 assistant 消息组件
streamingMessage 对应的消息对象
pendingTools toolCallId -> ToolExecutionComponententry_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 只显示父行。
建议阅读顺序
packages/tui/src/index.ts了解框架导出了什么packages/coding-agent/src/modes/interactive/interactive-mode.ts- 字段声明部分
subscribeToAgent()/handleEvent()- 提交流程
editor.onSubmit handleFollowUp()/handleDequeue()
- 需要时再看具体组件
components/tool-execution.tscomponents/assistant-message.tscomponents/footer.ts