Skip to Content
开发者指南Daemon共享 UI Transcript 层

共享 UI Transcript 层

当前状态packages/cli/src/ui/daemon/daemon-tui-adapter.ts 作为遗留实验性 CLI 端适配器仍保留在 main 分支上。本文档描述更新的 SDK 端共享 UI transcript 层:可复用的 daemon 事件规范化与 transcript 原语,任何 UI 宿主均可消费,包括 Web、TUI、IDE 和 IM 渠道。CLI TUI、渠道和 VS Code IDE 的迁移为后续工作。

概述

packages/sdk-typescript/src/daemon/ui/ 为 SDK 添加了 ui/* 子包。它通过可复用原语将 daemon SSE 事件流转换为可渲染的 UI transcript 块:

  • 规范化normalizer.ts):将 daemon 线路 schema 的 43 种已知事件类型(参见 09-event-schema.md)映射为 37 种对 UI 友好的 DaemonUiEventType 语义事件,如 assistant.text.deltatool.updatesession.metadata.changed
  • 状态机transcript.tsstore.ts):纯 reducer 加可订阅 store,将 UI 事件映射为有序的 DaemonTranscriptBlock[]
  • 渲染器render.tsterminal.tstoolPreview.ts):将 transcript 块渲染为 HTML、终端文本和工具预览字符串。宿主可直接使用或替换。
  • 一致性测试conformance.ts):跨宿主一致性测试,用于渠道、TUI 和 IDE 界面迁移到这些原语时保持一致。

第一个生产消费方是 packages/webui/src/daemon/#4328 )。其 React DaemonSessionProvider 和 transcript 适配器让 Web UI 可直接连接 daemon HTTP+SSE,而不仅仅渲染宿主 postMessage 流量。CLI TUI、渠道基础层和 VS Code IDE 可在后续复用同一层;../daemon-ui/MIGRATION.md 记录了 v2 增量迁移指南。

职责

  • 将 43 个 daemon 线路事件规范化为稳定的 UI 词汇表(DaemonUiEventType),使渲染器无需检查 rawEvent.data
  • 以 daemon 单调递增的 SSE eventId 作为主排序键,确保不同客户端以相同顺序渲染 transcript。
  • 使用纯 reducer 生成 transcript 块,并提供选择器用于查询待处理权限、当前工具、审批模式、工具进度和子 agent 子块。
  • 提供基础 HTML 和终端渲染器,同时允许宿主自定义渲染。
  • 暴露公共常量,如 DAEMON_PLAN_TOOL_CALL_ID,供计划面板使用。
  • 保持线路兼容性的可加性:未知事件类型规范化为 debug,而非被丢弃。

架构

包结构

文件导出用途
packages/sdk-typescript/src/daemon/ui/index.ts子包入口桶公共入口点
ui/types.tsDaemonUiEventType、各类型 DaemonUiEvent* 接口、DaemonTranscriptBlockDaemonTranscriptStateDaemonUiToolProvenanceDAEMON_PLAN_TOOL_CALL_ID类型定义
ui/normalizer.tsnormalizeDaemonEvent(evt) -> DaemonUiEventgetSessionUpdatePayload(evt)线路到 UI 的映射
ui/transcript.tscreateDaemonTranscriptState()appendLocalUserTranscriptMessage()reduceDaemonTranscriptEvents()rebuildDaemonTranscriptBlockIndex()、选择器状态机与选择器
ui/store.tscreateDaemonTranscriptStore(initial?)可订阅的 reducer store
ui/toolPreview.tscreateDaemonToolPreview(toolEvent)工具调用摘要文本
ui/render.tsDaemonHtmlRenderOptionsDaemonRenderOptions、渲染函数HTML 与通用渲染
ui/terminal.ts终端专用渲染TUI 渲染准备
ui/conformance.ts跨宿主一致性测试套件迁移一致性测试
ui/utils.ts辅助工具,如 DaemonUiContentPart内部共享工具函数

DaemonUiEventType 词汇表

ui/types.ts 定义了 37 种 UI 事件类型,按领域分组。

聊天流(阶段 1)

  • user.text.deltauser.image.deltauser.shell.commandassistant.text.deltaassistant.donethought.text.delta
  • tool.updateshell.outputuser.shell.output
  • permission.requestpermission.resolved
  • model.changedstatuserrordebug

会话元数据

  • session.metadata.changedsession.approval_mode.changed
  • session.available_commandssession.state_resync_requiredsession.replay_complete

提示生命周期(跨客户端)

  • prompt.cancelledfollowup.suggestion

工作空间(Wave 3-4)

  • workspace.memory.changedworkspace.agent.changed
  • workspace.tool.toggledworkspace.settings.changedworkspace.initialized
  • workspace.mcp.budget_warningworkspace.mcp.child_refused
  • workspace.mcp.server_restartedworkspace.mcp.server_restart_refused

认证流程(Wave 4 OAuth)

  • auth.device_flow.startedauth.device_flow.throttledauth.device_flow.authorized
  • auth.device_flow.failedauth.device_flow.cancelled

normalizeDaemonEvent 将 43 个 daemon 已知线路事件映射到此词汇表。未知、未建模或格式错误的事件类型规范化为 debug,并保留 rawEvent 供宿主诊断使用。

Reducer 与选择器

// 创建初始状态。 const state = createDaemonTranscriptState(); // 应用 SSE 事件序列。 const next = reduceDaemonTranscriptEvents(state, daemonUiEvents); // 选择器。 selectTranscriptBlocks(state); // 所有块 selectTranscriptBlocksOrderedByEventId(state); // 按 eventId 排序;推荐使用 selectPendingPermissionBlocks(state); selectCurrentTool(state); selectApprovalMode(state); selectToolProgress(state, toolCallId); selectSubagentChildBlocks(state, parentBlockId); isSubagentChildBlock(block); formatBlockTimestamp(block); formatMissedRange(state); // state_resync_required 后的"你错过了 X"文本

Store

createDaemonTranscriptStore() 提供订阅和 dispatch 功能:

const store = createDaemonTranscriptStore(); store.subscribe(() => render(store.getState())); store.dispatch(uiEvents); // 内部运行 reducer

Web UI 的 DaemonSessionProvider 基于此 store 构建 React context。

流程

单个 SSE 事件端到端

宿主可以在 (E) 处停止并实现自己的 reducer,也可以消费 (G) 和提供的选择器。Web UI 使用完整的 (B) -> (H) 路径。已迁移的 TUI 可消费 (G) 并使用 Ink 特定组件渲染。

state_resync_required

session.state_resync_required 映射为 transcript 中的”错过范围”标记。UI 代码可调用 formatMissedRange(state) 渲染如”错过事件 X-Y”的文本。reducer 会继续应用后续事件,但将受影响的块标记为 resyncRecovery: true,以便渲染器添加视觉提示。环形缓冲区驱逐和 state_resync_required 语义详见 10-event-bus.md

消费方

packages/webui/src/daemon/

已在 #4328  中落地。

文件导出
DaemonSessionProvider.tsxReact <DaemonSessionProvider />useDaemonSession()useDaemonTranscriptStore()useDaemonTranscriptState()useDaemonTranscriptBlocks()useDaemonPendingPermissions()useDaemonActions()useDaemonConnection() hooks;DaemonConnectionStatusDaemonConnectionStateDaemonSessionContextValue 类型
transcriptAdapter.ts将 SDK DaemonTranscriptBlock 适配为 Web UI 的 UnifiedMessage,包括 markdown 流式 chunk 合并和工具调用摘要
index.ts子包入口桶

Web UI 现在可以直接连接 daemon HTTP+SSE 并渲染 transcript。旧的 ACPAdapter 宿主 postMessage 路径仍然可用。

后续迁移

../daemon-ui/MIGRATION.md 提供了 Web 聊天和 Web 终端适配器的 v2 增量指南。其中明确指出 CLI TUI、渠道基础层和 VS Code IDE 未在该 PR 中迁移;每项将在后续 PR 中迁移,并使用一致性测试套件保持渲染一致性。

与遗留 daemon-tui-adapter.ts 的关系

维度遗留 CLI DaemonTuiAdapter新共享 transcript 层
packages/cli/src/ui/daemon/packages/sdk-typescript/src/daemon/ui/
公共接口DaemonTuiAdapterDaemonTuiUpdateDaemonTuiSessionClientDaemonUiEventTypereduceDaemonTranscriptEvents、选择器
适用范围仅限 CLI Ink TUIWeb、TUI、IDE 或 IM UI
状态形态TUI 本地更新联合类型纯 transcript 块列表加状态字段
排序createdAteventId(daemon 单调递增,跨客户端一致)
未知线路类型reduceDaemonEventToTuiUpdates 中被丢弃规范化为 debug 并保留
测试单包单元测试全局一致性测试套件,用于跨宿主一致性

依赖关系

配置

  • 无运行时配置。Reducer 和选择器均为纯函数。
  • 宿主可选择渲染器:HTML(render.ts)、终端(terminal.ts)或自定义渲染。
  • 调试时,render.ts 支持 includeRawEvent: true,可在渲染输出中包含原始线路帧。

注意事项与已知限制

  • daemon-tui-adapter.ts 仍然存在。它是 CLI 包的遗留实验性适配器。新代码应优先使用 SDK ui/*normalizeDaemonEventreduceDaemonTranscriptEventsDaemonTranscriptBlock
  • CLI TUI、渠道基础层和 VS Code IDE 尚未迁移。它们仍维护各自的渲染逻辑。docs/developers/daemon-client-adapters/ 目录仍有 ide.mdchannel-web.md 和历史 tui.md 草稿;较新的 web-ui.md 涵盖 Web UI 适配器设计。
  • eventId 是主排序键createdAt 保留为已废弃的别名(clientReceivedAt)。新代码应使用 selectTranscriptBlocksOrderedByEventId(state)MIGRATION.md 展示了从 createdAt 排序切换到 eventId 排序的代码差异。
  • 未知线路类型规范化为 debug。它们不再像旧适配器那样被丢弃。渲染器默认不显示 debug;宿主必须主动选择显示它。
  • Bundle 体积ui/* 子包通过 @qwen-code/sdk/daemon 作为 ESM 子路径导出,不引入 React 或 DOM 依赖。只有当 Web UI 消费方使用 DaemonSessionProvider 时,React 集成才会被加载。

参考资料

  • packages/sdk-typescript/src/daemon/ui/types.tsDaemonUiEventType 词汇表)
  • packages/sdk-typescript/src/daemon/ui/transcript.ts(reducer 与选择器)
  • packages/sdk-typescript/src/daemon/ui/normalizer.ts(线路到 UI 的映射)
  • packages/sdk-typescript/src/daemon/ui/store.tsrender.tsterminal.tstoolPreview.tsconformance.ts
  • packages/sdk-typescript/src/daemon/index.tsui/* 重导出块)
  • packages/webui/src/daemon/DaemonSessionProvider.tsxtranscriptAdapter.ts
  • 上游文档:../daemon-ui/README.md../daemon-ui/MIGRATION.md../daemon-client-adapters/web-ui.md
  • 相关 PR:#4328 (v1 transcript 层与 Web UI provider)、#4353 (v2 统一完整性后续)
Last updated on