# dsh-conversation-tree [English](./README.md) | 中文 为 DSH Web 增加不可变的对话分支和消息级整树浏览: - **等待中的消息**:在原气泡的复制按钮旁直接修改或撤销发送。编辑在原气泡中展开,附件保持可见;保存会原位更新这条待执行输入。 - **重新生成**:在 AI 回复自己的操作栏中,从所选轮次之前创建子会话,按原顺序重新投递该轮全部人工消息,包括后续追加指令及附件;隐藏的运行时上下文不会重放。 - **编辑提问**:点击后,原用户气泡会直接变成紧凑的内联编辑器,自动回填原文并提供模型选择,不再弹窗。保存时会保留该消息的非文本附件,只替换首条人工消息,保留后续追加消息不变,并在子会话中继续生成。主动停止生成后仍可编辑,包括没有收到 `turn/end` 事件的中断步骤。 - **编辑回答**:点击后,原 AI 消息会直接变成同样的内联编辑器。保存时复制到所选轮次,把子会话中的最后一条回答替换为新文本,不调用模型。 - **内联切换**:在发生分叉的提问或回答旁显示 `‹ 2 / 5 ›`,可直接切换同一消息的上一个/下一个分支;在 AI 操作栏里位于原生“创建分支”按钮右侧。 - **消息树**:共享历史只显示一次;编辑提问、重新生成回答、编辑回答都会在被替换的具体消息处分叉。点击任意消息路径即可打开背后的会话。 - **一个逻辑会话**:仅由本插件创建的分支在侧栏中折叠为一行。切换插件分支只会改变这一行当前打开的版本;DSH 原生的 **Fork Session** 仍会在侧栏中创建独立会话。 - **实时继续对话**:可在任意分支上继续发送消息,当前聊天会立即更新;正常轮次结束后不需要刷新页面。 每个不可变分支在底层仍保存为物理子 Session,因此不会重写源历史;这只是实现细节。插件通过持久化来源标记,把客户端侧栏折叠严格限制在插件创建的分支上;它不会真正归档或删除任何版本,从插件分支继续执行原生 Fork 时也仍会显示独立会话。所有插件分支仍可通过内联切换和消息树访问。子 Session 会记录 `parentSession`,继承源会话的工作目录与 preset,并采用内联编辑器中选中的 provider、模型及该模型的默认推理强度;重新生成则采用源会话当前的模型选择。两种路径都不会复用目标旧轮次的模型设置,也不会修改源会话。 创建分支不会修改宿主默认模型。在 DSH 0.1.5-rc.1 上,插件临时包装 `agentDefaultModel.saveSelection`,通过 Node 异步局部作用域仅抑制本次分支模型选择的默认值保存。原生会话模型选择、选择器、推理强度及后续普通模型切换保持正常行为,并发的普通默认值保存仍会执行。操作结束或失败后会移除包装,且不会覆盖其他插件随后安装的包装。不具备该服务接口的宿主会报告模型选择错误;该集成已针对 DSH 0.1.5-rc.1 验证。 v0.4.3 及更早版本创建的分支没有来源标记。更新后,这些既有分支会安全地显示为独立侧栏 Session;新创建的插件分支会正常折叠;插件不会为更早的无标记分支补写来源。 ## 等待中的消息 回答进行中,已被主机接收、显示为等待气泡的追加消息,会在复制按钮旁显示“修改等待中的消息”和“撤销发送”图标,尺寸、颜色和间距沿用原生操作栏。触屏下三个按钮统一使用 44 px 点击区域。编辑器沿用原生气泡颜色,保留附件卡片;点击“取消”或按 Escape 恢复原视图。“保存”保留消息身份、队列位置、附件和当前会话的模型选择;撤销通过 DSH 原生队列删除接口只移除选中的待处理消息,并执行附件回执清理。 这些操作不会创建分支或中断正在生成的回答。若消息已出队或已被其他客户端修改,过期保存会被拒绝,打开的草稿仍可复制。未保存草稿仅保留在当前编辑器中,刷新页面后不会恢复。入口仅针对普通会话中主机已接收的人工追加消息,不包含本地发送中的回显、运行时上下文或子代理会话。DSH 普通下一轮消息队列继续使用原生操作入口。 ## 要求 - DSH `0.1.5-rc.1` - Web profile(下面以 `web` 为例) `0.8.0` 支持 DSH `0.1.5-rc.1` 的 V3 会话格式与原生持久化句柄。DSH `0.1.2-rc.1` 请继续使用插件 `0.6.0`。新分支保留精确的继承前缀,在继承边界后存储自身来源标记,再通过原生 Agent 接口恢复。重放人工消息前会清除截断点继承的待处理输入,避免产生重复轮次;编辑回答时会清空旧回答嵌入的模型流。 冷会话导航会打开并关闭只读句柄,不激活会话。对于包含本插件精确 version-1 来源标记的 V0–V2 历史日志,插件通过受限的 JSONL 迁移适配器保留标记,由 DSH 转换核心事件和序号,再按 V3 重新验证结果。只读操作仅在内存中准备转换;原生写入会发布独立的 V3 文件,并保留旧文件原字节。未知或格式异常的外部事件仍被拒绝。此适配器依赖 DSH 0.1.5 的 JSONL generation hook,未来存储版本需重新验证。自定义 RPC 路由保留宿主的身份认证和 trusted-host 检查。 ## 安装 ```sh dsh plugin --profile web add github:Unintendedz/dsh-conversation-tree#v0.8.0 ``` 随后重启正在运行的 DSH Web 服务。进入任意已完成的对话后,编辑入口位于对应消息下方;点击后,该条原消息会直接替换为已回填文本和模型选择器的内联编辑器。重新生成仍位于 AI 回复下方;有多个版本的消息会在创建分支按钮右侧显示内联分支计数器。顶部的 **树** 页签用于浏览共享前缀只出现一次的完整消息树。 ## 更新 安装新版本 tag 后重启服务: ```sh dsh plugin --profile web add github:Unintendedz/dsh-conversation-tree#v0.8.0 ``` ## 卸载 ```sh dsh plugin --profile web remove dsh-conversation-tree ``` ## 开发 ```sh npm install npm run build npm test ``` 将 `DSH_NATIVE_ROOT` 指向已安装的 DSH 包根目录(包含其 `node_modules` 的目录),可运行真实 Session、JSONL 迁移、Session Controller 和 Cordis 集成测试。测试应设置全新临时 `DSH_HOME`;仅加载包代码并使用合成数据,不启动现有 profile。 宿主入口为 `lib/index.js`,浏览器源码为 `src/client.jsx`、`src/pending-input.jsx` 和 `src/tree.js`。`npm run build` 使用锁定版本的 esbuild 生成 `lib/client.js`。 树视图会根据子会话的父会话、seed 和对话事件还原每个持久化分叉。连续历史始终保持在同一列,只有真正发生分叉时才增加缩进,因此长对话不会越画越窄。当前只纳入普通会话,主动排除 DSH 内部的 subagent 会话;损坏的父子引用与循环关系会被安全截断,不会阻塞页面。 DSH 0.1.5 没有公开的等待气泡操作槽位。插件通过限定在当前会话内的 DOM 适配器,把 React portal 挂入原操作栏和文字容器;将完整的原生追加消息列表与主机队列对应,排除本地发送回显,拒绝数量不一致或行身份变化的映射,并在每次点击时重新检查对应会话。适配器不读取 React 内部数据,也不通过消息文字定位;升级 DSH 时需重新验证。