# Harness UI 产品设计 > **【已被取代——历史档案】** 本文是 07-18 世代定稿,其产品结论已由 2026-07-21 figma 定稿+插件化 RFC 重设。现行权威=`missions/tasks/20260721-1520-web-plugin-rfc/` 的 plugins.md(业务规格+布局九规则,本文「两栏+侧板」已改三栏 details 第一公民)+ figma-analysis/(视觉圣经);接口=api-contracts.md v3。本文仅作决策演变考古用。 > 状态:**2026-07-18 定稿,供最终 review**。技术实现见《[UI 技术架构](ui-tech.md)》。关键决策的演变过程见文末附表;完整裁决档在 `missions/tasks/20260718-final-review/`。 ## 0. 本轮范围硬约束(2026-07-18 用户定) - 一期只实现 **Web localhost**;**Electron 为架构同构预留**(协议、注入接缝、进程模型设计完毕并保留,一期不写实现);TUI 只留协议位置。 - 通信协议按 Web / Electron / TUI **跨端一致一次定全**;实现按批次并发填(多 worktree 并行)。 - 连接模型下限承诺:**单 WebView 连接 + 刷新恢复绝对可靠**(承诺清单见 §4);多 WebView 并连、读写分离明确不做,不为其复杂化。 - 工期按 AI 天估算:不为工期砍协议/架构完整性;做不完的功能保留架构位、实现留空。 ## 1. 这是什么,解决什么问题 给 DeepSeek Harness 做一套**自己的图形界面**:用户在里面选历史会话、与模型持续对话、观察工具执行、处理审批与提问、配置模型。 **现状问题**:harness 目前只能通过编辑器(ACP 协议,如 Zed)或终端接入,没有独立图形界面——用户无法在自己的界面里浏览历史 session、看流式对话、处理审批、管理 Provider/模型配置。 ## 2. 产品形态 | 形态 | 启动方式 | 关页/关窗后 | 状态 | |---|---|---|---| | **localhost Web** | 命令 **`dsc web`**,浏览器直接访问打印的地址(免登录) | 关页/刷新不停 server 或任务;刷新原页恢复 | **一期实现** | | Electron | Electron 启动并监管专属本地 sidecar;renderer ≡ WebUI | 关最后一个窗口即退出并停 sidecar;有活动工作先确认 | **架构预留**(同构设计完毕,见 ui-tech §9/07 层,一期不实现) | - `dsc web` **监听 `0.0.0.0`**、控制台打印 `http://127.0.0.1:<端口>`。局域网内其他设备因此事实可达,但一期不为 LAN 做设计(免登录、单客户端、不承诺,见 §17)。 - `dsc web` 进程与(未来的)Electron sidecar 是各自独立的 Host 实例,不共享、不跨形态接续任务。 ## 3. 核心原则(用户能感知的) - **Host 是权威,界面是可重建的呈现**:离开页面、刷新、临时断网**不会停掉已接受的任务**。只要 `dsc web` 进程在跑,任务继续;重连后恢复。 - **发送(插话/排队)、停止是不同的显式操作**,不含糊;一切发送/控制能力**遵循 harness core 现有 API**(steer/queue/cancel 的 1:1 映射,见 §8),不造 core 没有的能力。 - 一期核心体验由第一方界面完整提供;插件从真实、局部的扩展点开始。 ## 4. 单客户端与刷新恢复(下限承诺) - **一个 Server 同时只允许一个活动界面 Client**。一个 Client 里可打开、切换多个 session;第二个页面/前端在连接阶段被明确拒绝(提示"已被占用",不泄露占用者信息),不影响当前 Client。 - **刷新**同一页面:用原 Client 身份重连接管,不取消任务,从 Host 权威状态重建。 **刷新/重连后必须恢复的(承诺清单)**: 1. session 列表(含运行状态、待审批/待问答计数)与当前焦点 session。 2. 已打开 session 的完整已持久对话历史。 3. **进行中消息的已生成部分(partial)**——刷新后正在流式生成的回复不是半截空白,而是先显示已生成内容、随后增量继续推。 4. Host 存活期间的待处理审批卡片与提问卡片,可继续回复。 5. Provider 设置与模型列表。 **明确不承诺的(已知边界,如实告知用户)**: - **排队中尚未开轮的输入,刷新后暂不可见**(任务本身不丢:Host 内的队列继续持有,开轮后以正式消息出现在时间线)。原因:不在 harness 队列前复制第二套状态;未来 harness 提供队列项 ID 后升级(见 ui-tech §13)。 - 断线期间的逐 token 增量不补发,以 partial 快照收敛。 - **Host 进程重启后**:只恢复已持久化的事实;重启前 pending 的审批/提问变为只读「已中断」记录,不能再回复。 ## 5. 信息架构 一期四个主要区域: - **Session 列表**:创建、打开、重命名、排序、筛选、分页,显示运行状态和待处理计数。 - **对话区**:历史、流式响应、工具卡片、审批卡片、提问卡片、任务终态。 - **输入区**:编辑草稿、选模型、发送(插话/排队)、停止。 - **设置区**:唯一活动 Provider、API Key、模型候选、连接测试。 插件状态和错误作为诊断展示;界面不承担插件管理(安装/升级/启停)。 ## 6. Session 列表 - 支持:**创建**(新会话)、打开、**重命名**、显示运行状态与待处理数量(审批 + 提问)。 - **排序/筛选/翻页由 server 驱动**(按最近活动时间稳定游标分页),session 多也不卡;列表来自轻量摘要,不需要加载完整历史。 - **标题规则**:新建为「新会话」→ 第一条进入历史的用户消息首个非空文本行**一次性**生成标题 → 用户手动重命名后**永不再自动覆盖**。 - 列表的「最近活动时间」只随消息定稿等完整边界推进,**不随逐 token 输出抖动**(避免列表频繁跳动)。 - 归档、置顶、分组、硬删除不进一期。 ## 7. 对话与历史 - 打开 session 先展示最近一段时间线,向上滚动分页加载更早历史。 - 一期不做虚拟列表,但滚动行为要做对:历史插入锚定、流式增长、用户离底、重新跟随。 - 消息正文支持**流式 Markdown**(GFM + 代码高亮 + **数学公式 KaTeX**);Mermaid 一期后置(实现时按「未闭合显示源码→闭合后渲染→失败回退源码」的流式降级模式)。 - **多个打开的 session 同时在后台接收更新**:切换只改焦点,后台 session 继续收运行状态、结果、审批与提问更新。 - **system/上下文注入等非人类交互内容不进普通时间线**:折叠进「诊断」视图查看,普通时间线只保留真实的人类交互与模型回复。 - 同一轮(turn)内模型的多个 step 按轮分组呈现;思考过程与正式回复分开、可折叠。 ### 工具调用卡片 **卡片类型以 harness core 现有呈现能力为准,core 没有的不自造**(已核源码:core 的 `presentCall/presentResult` 现成提供的就是下述集合)。统一呈现模型,一次工具调用一张卡片,状态**原地**从"进行中"变"完成/失败":`generic` / `terminal` / `diff` 三类卡片 + `locations` 作为正交的位置附件(不是第四种卡片);未识别类型回退通用展示。第一方 renderer 和 unknown fallback 始终存在(自定义 renderer 扩展点仅预留,见 §13)。 ## 8. 输入、发送与停止 输入框内容是**本地草稿**;发送对接 harness agent 现成的两条队列,是对底层能力的忠实映射: | 操作 | 行为 | |---|---| | **插话(steer)** | 把输入插入正在进行的这一轮(纠偏/追加) | | **排队(queue)** | 排成后续独立一轮 | | 空闲时发送 | 立即开一轮执行 | | **停止** | 整体终止当前任务(清空排队 + 中断当前),不提交新输入 | - **三条纪律**:UI 不假设「一次发送对应一个 turn」(开轮可能一次消化多条排队输入);不提供「取消中」中间态(停止即请求,以 Host 权威状态为准);一期无单项取消/撤回/重排(未 send 的草稿本地可丢)。 - 排队项刷新后的可见性边界见 §4。队列上限由 Host 配置,超限/状态不允许时显示 Host 返回的明确拒绝原因。 ## 9. 模型选择 - 每个 session 保存所选 model ID;所有 session 共享同一个活动 Provider。 - 选择器同时给 Catalog 候选和手写输入,可保存 Catalog 没有的 model ID。 - 切换从**下一个请求边界**生效:已开始的流继续用原模型。 ## 10. 审批 审批请求由 **Host 持有**:**Host 存活期间,页面刷新/重连后可恢复**待处理审批并继续回复;**Host 重启后**变为只读「已中断」记录。 按位置分层提示: - 当前 session:时间线内**审批卡片**(按稳定锚点插位,不按到达顺序)。 - 其他 session:列表 badge + 应用内可点击 toast。 - Web:用户已授权时增加系统通知;(Electron 预留:后台时系统通知。) 通知不强制切换 session;同一审批按稳定 ID 去重。仅 Host 提供 `expiresAt` 时显示倒计时(到期判定归 server,断线不暂停)。被其他 actor/policy/超时解决后,卡片保留只读终态;迟到回复显示冲突并刷新权威结果,不覆盖已完成的决定。 ## 11. 用户提问(`ask_user_question`)——独立于审批 模型可向用户提问(可多项、可多选)。独立资源、独立卡片,**不复用审批**: - 一次提问包含多个问题项时**整批一次原子回答**,必答项必须全部覆盖。 - 终态四种:已回答 / 已取消 / 已超时 / 已中断(Host 重启)。 - 恢复口径与审批一致:Host 存活期刷新/重连可恢复继续回答;Host 重启后只读「已中断」。 - pending 提问计入 session 待处理数,并参与「退出/更新是否 busy」的判定。 ## 12. Provider 设置 一期一个 Host 全局活动 Provider。设置页含:连接配置、API Key 查看修改、模型候选 + 手写 model ID、保存前连接测试、Catalog 来源/stale/错误。 - **保存 = 先用表单配置连接远端真实测试**(判据:真实拉起一次最小模型流并见到合法结束,不是 HTTP 200 就算通过):成功才落盘并切换;失败保留表单、现状不变。 - **两类失败分开**:连接/凭据探测失败**阻止保存**;仅模型发现失败**仍可保存**,模型列表保留上次成功并标 stale。 - **同时只允许一个保存事务**(第二个保存请求立即得到"忙"提示,不排队);保存一旦被接受,**不随页面断开而中止**,结果回来后在界面上如实呈现。 - 运行期间可改配置:已开始的模型流用旧配置跑完,后续请求用新配置。 - API Key 明文存本地用户目录 owner-only 文件(`~/.dsc/`);界面常态只显示"已配置",不回显明文;日志/事件/诊断统一脱敏。 ## 13. 插件体验 - 一期只做两件事:**① 现有服务端(Node 侧)插件列表的展示**(加载状态与错误,不提供安装/移除/启停);**② 把 Browser Cordis Context 的能力地基搭好**(前端运行时以 Cordis root Context 组织,为未来 UI 插件留好挂载面)。 - **一期不做 UI 插件**:自定义 tool renderer、侧栏功能页两个扩展点只留架构位(slot seam 预留、不实现注册);页面/路由/设置面板/输入框/列表/通用 action 亦不开放。 - 插件由开发者通过 `cordis.yml`/CLI 引用本地路径或已装包;一期本地插件视为完全可信,不做隔离。 ## 14. 退出与故障体验 - **Web**:关页/刷新/断网 ≠ 停任务;重连按 §4 承诺清单恢复。`dsc web` 进程收到 SIGINT/SIGTERM 走有序停机:停止接收新工作 → 取消进行中任务 → **落盘所有 session** → 退出;停机判定 busy 的口径与下述活动判定一致。 - **活动判定**(Web 停机与未来 Electron 退出共用):活动 = 进行中对话 + 待审批/待提问 + **后台任务(bash 等)running/stopping**;「Agent 空闲 ≠ 可退出」;任一来源状态**读不到时按"未知"处理并保守确认,不伪装空闲**。 - 单个 session 重新同步期间:保留可见内容、显示"正在重新同步"、暂禁该 session 操作;其他 session 不受影响。 - Host 崩溃:重启只恢复已持久化事实,不承诺恢复崩溃时正在执行的任务。 ## 15. 一期验收行为 | 场景 | 预期 | |---|---| | Web 刷新 | 同一 Client 身份重连接管;任务继续;列表/历史/**进行中消息 partial**/待审批/待提问全部恢复 | | Web 关页 | `dsc web` 进程与已接受任务继续运行 | | 第二个页面连接 | 连接阶段明确拒绝(不泄露占用方),不影响当前 Client | | 运行中发送 steer | 输入插入当前轮 | | 运行中发送 queue | 排成后续轮;刷新后暂不可见但不丢失,开轮后出现在时间线 | | 停止 | 清排队 + 中断当前任务;UI 状态以 Host 为准 | | 新建/重命名 session | 立即生效;重命名后标题不再被自动覆盖 | | Provider 探测失败 | 表单保留、活动配置不变;仅发现失败则可保存、列表标 stale | | Provider 保存期间刷新 | 保存事务继续完成,重连后看到终态 | | 单 session 事件缺口 | 只重同步该 session | | 收到 ask_user_question | 独立提问卡片,整批原子回答;非当前 session 走 badge/toast | | 收到审批(非当前 session) | badge + toast,不强制切换 | | 切换 session | 只改焦点,后台 session 继续收流 | | 服务端插件异常 | 插件列表如实显示错误状态,不影响对话主功能 | | `dsc web` SIGINT | 有序停机:取消、落盘、退出 | ## 16. 一期范围与后续规划 | 能力 | 一期 | 预留/后续 | |---|---|---| | 形态 | Web localhost | **Electron(架构已同构预留)**、TUI、LAN/远程、多 Client | | 会话列表 | server 分页、创建、重命名、状态/计数 | 分组、置顶、归档、硬删除、拖拽 | | 对话 | 流式渲染、tool 卡片、思考折叠、审批/提问卡片、turn 分组、滚动不变量、**partial 恢复** | 消息编辑/分支、虚拟列表、逐 token 精确续传 | | 发送 | steer/queue/停止 | 排队项列表可见、单项取消/撤回(等 harness 队列项 ID) | | 审批/提问 | Host 存活期可恢复;重启只读中断 | 跨重启恢复(等 harness 持久化支持) | | tool 展示 | 进行中→完成/失败 | 运行中进度(等 harness tool progress 事件) | | Provider | 单活动 Provider、test-before-save、busy gate | 多 Provider/Profile、keychain | | 插件 | 服务端插件列表展示 + Browser Cordis Context 地基(扩展点仅预留) | tool renderer/侧栏页实装、第三方隔离、双端 PluginPort、插件市场 | | Markdown | GFM+高亮+KaTeX | Mermaid(含流式降级)、流式三件套移植 | ## 17. 非目标(一期不提供) 机器级 daemon / 局域网 / 远程 Host;多 Client 协作与并发观察;TUI 实现;多 Provider / Profile / 多设备同步;API Key keychain / 静态加密;插件市场 / 界面内插件管理 / 不可信插件隔离;session 归档/置顶/分组/硬删除/队列重排;消息虚拟化;Host 崩溃后运行中任务恢复;排队项的刷新后可见性(见 §4 边界)。 ## 18. 附:关键决策演变(一次看全过程,正文不再重复) > 每行 = 一个决策在三轮设计(本仓两轮 + 与平行仓 harness-1 综合)中的走向。详情:`missions/tasks/20260718-final-review/`。 | 决策 | 早期方案 | 最终结论 | 变化原因 | |---|---|---|---| | 一期形态 | Electron + Web 同期 | 只 Web localhost,Electron 架构预留 | 用户 2026-07-18 收窄范围;架构同构保留,后补零改业务 | | Web 登录 | 一次性链接换 HttpOnly cookie | 裸 localhost 免鉴权 + AuthGate 接缝 | 用户拍板:单机开发体验优先;鉴权设计收档备用 | | 排队项刷新可见 | 曾考虑 server 镜像队列 / Submission 投影 | 接受"刷新后暂不可见"边界 | 两仓独立终裁同向:不在 harness 队列前复制第二套状态 | | 刷新恢复 partial | 一期不恢复进行中消息(要么定稿要么空白) | 恢复已生成部分(快照含 partial) | 纯 server 内存可实现,直接抬高刷新体验下限 | | 发送语义 | 曾评估 queue+中断+撤回编辑全家 | steer/queue/停止三操作 + 三条纪律 | 忠实映射 harness 现成能力,不造第二队列 | | 审批/提问恢复 | 曾承诺"可持久恢复" | Host 存活期可恢复;重启只读中断 | harness 审批/提问是内存态,跨重启需改 core(留迁移位) | | 提问(ask_user_question) | 曾并入审批 | 独立资源、独立卡片、整批原子回答 | 两类交互终态语义不同,不共用 | | session 列表 | 本地全量筛选 | server 驱动分页/筛选/排序 | session 量大时的性能与一致性 | | system 消息 | 投成普通消息气泡 | 折叠诊断视图 | 普通时间线只留真实人类交互 | | Markdown 范围 | (harness-1 为 Mermaid 无公式) | KaTeX 一期、Mermaid 后置 | 纯范围取舍,维持本仓选择 | --- ## 附:产品术语 | 术语 | 含义 | |---|---| | Session(会话) | 一次完整对话记录,可保存、回看、继续 | | Host / Server | 跑 harness、持有权威状态的本地进程 | | Client | 一个连到 Server 的界面实例;一期一个 Server 只允许一个活动 Client | | 插话(steer)/ 排队(queue)/ 停止 | 运行期间的发送与控制操作(插入当前轮 / 排后续轮 / 整体终止) | | partial 恢复 | 刷新/重连后先显示进行中消息已生成的部分,再继续增量 | | turn(轮) | 一次任务执行;一轮内模型可有多个 step,UI 按轮分组 | | 审批 / 提问 | 两类独立的用户交互请求;Host 存活期可恢复,重启后只读中断 | | 请求边界 | 模型请求起点;模型/Provider 切换从下一边界生效 | | slot(坑位) | 预留给插件注入组件的位置(tool 卡片、侧栏功能页) | | degraded | 插件 Browser 能力失败、Node 能力仍在跑的降级状态 |