DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

RyeSama /

RyeSama/dsh-union-view

Verified

DSH Web GUI 的联合会话视图:把多个对话并排摊在屏幕上,只读不改;需要时用 /weld 把它们的历史焊成一个新对话

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@e91eb1af

联合视图(dsh-union-view)

把好几个对话并排摊在屏幕上一起看;需要的话,还能把它们的历史装进一个新对话接着聊。

当前版本 0.4.0 · 更新记录 · MIT

先说这东西解决什么

假设你最近在干一件事,但聊得很散:

  • 上周定需求,在 A 对话里
  • 中间反复试错,在 B 对话里
  • 最后结论和踩过的坑,记在 C 对话里

现在你想回顾整件事。怎么办?点开 A 翻一遍,退出来,点开 B 翻一遍,退出来,点开 C 翻一遍。翻到 C 的时候,A 里说的什么已经忘了一半。

联合视图就是为这个做的:勾上 A、B、C,三份对话同时摊在同一个屏幕上,左右对照着看。

┌ 对话   GAL视窗   [联合视图]   轨迹 ┐
│ 联合视图  3 个会话 · 1500 条消息 · 只读                    │
│ ─── 定需求 ───┬─── 试错 ───┬─── 结论 ─── │
│ 你 14:02      │ AI 15:31   │ AI 23:44    │
│ 我想做…       │ 报错了:…  │ 所以最终…   │
└───────────────┴────────────┴─────────────┘

三种摆法

浏览器里点一下就能换,随时切:

摆法 什么时候用
时间线 想知道"当时到底先发生什么、后发生什么"
分栏 想知道"同一时间,两边各自在干嘛"
轮次对齐 想知道"同一个问题,两边分别怎么回答的"

时间线 分栏 轮次对齐

最重要的一件事:它只是"看"

用联合视图的时候,它只读不写:

  • 原对话里一个字都不会被改
  • 不会把几份对话合并、搬来搬去
  • AI 完全不知道你在这么看 —— 就像你把三份打印稿摊在桌上比对,桌上那台电脑(AI)并不知道

所以它很安全,随便试:看完关掉,世界和之前一模一样。

消息太多时它也不会卡死——每个视角只摆最近一段,上面会提示"更早的 N 条已折叠"。

它只显示你和 AI 说的话

对话记录里其实混着三种你不关心的东西,联合视图全部滤掉:

滤掉的 长什么样 为什么要滤
AI 的思考过程 一大段"我需要先……所以我打算……" 又长又碎,把真正的结论淹了
工具调用 [调用 read_image]、[调用 pwsh] 一行一条,几十条连着刷屏
系统注入 Time sampled while preparing turn 12…、<AEGIS_DSH_ROUTING_BOOTSTRAP>、Current working directory… 这些是宿主塞给 AI 的,不是你说的话,混在你的对话框里看着莫名其妙

所以一屏看下去只剩干净的对话。代价是:如果那个对话里 AI 光在调工具没怎么说话,会显得内容很少——它本来就没说几句话。

如果你想让 AI 真的看见:/weld

会写东西的一步。落在你自己机器上:新建一个对话,原对话只读。只是并排看的话不需要它。

上面说了,联合视图 AI 是看不见的。要让它真的看见,用 /weld。

最省事的用法:在联合视图里勾几个对话,工具栏右边填「每个取最近 N 轮」,点**「合并为新会话」**。它替你把这些填进输入框并提交,下面那行提示写着结果。等一两秒,左边对话列表最上面会多出一个「未命名」,点开就是合并好的新对话。

也可以手打:

/weld 3f2a1b 9c8d7e --turns 20

(3f2a1b、9c8d7e 是两个对话的编号前缀,--turns 20 表示每个对话只取最近 20 轮)

不打参数直接 /weld 回车,会列出最近 12 个对话的编号、时间和第一句话,照着抄就行。

它会真的造出一个新对话,把这两个对话的历史装进去。这个新对话:

  • 出现在你的对话列表里,点开就能看
  • 可以接着聊 —— 这时候 AI 是真的看得见这些历史了
  • 原对话一个字没改(只是被读了一遍)
  • 思考过程和工具调用也不带进去,只装你和 AI 说过的话

✅ 现在这条路是通的:dsh 打开新对话时会校验日志结构,所以新对话不再由我们拼,而是先建一个空对话、把合并好的内容一条条交给 dsh 自己写进去 —— 结构天然合法。视图那部分一直是稳的。

打个比方:

联合视图 /weld
类比 把几份文件摊在桌上,自己比对 把几份文件装订成一本新的,递给 AI
AI 看得见吗 看不见 看得见
会多出一个对话吗 不会 会
原对话会变吗 不会 不会

为什么一定要 --turns:两个几百条消息的对话,加起来一万多条,AI 一次吃不下。所以必须截断,只取最近若干轮。

新对话的标题会显示「未命名」,因为原来的标题带引用关系,截断后对不上,干脆不要了。改个名字就行。

新对话里到底装了什么

只有你和 AI 说过的话。剥掉的是这四类:

剥掉的 为什么
AI 的思考过程 又长又碎,而且它本来就不该算"说过的话"。内容块和 data.stream 里的逐 token 回放都要剥,漏一处就会从重建的节点里冒回来
工具调用与工具结果 [调用 read_image] 这类记录,几十条连着刷屏,对接着聊没有价值
图片卸载、上下文压缩 这些是原对话的窗口管理动作,引用已经指向不存在的位置,带过去会让日志校验失败
会话开头重复的初始化事件 每个源都有一份,装几份进去结构就乱了

代价:如果某个源对话里 AI 光在调工具、没怎么说话,装进新对话的内容就会很少——原文本来如此,不是丢数据。

怎么知道对话的编号

通常在联合视图里点按钮就够了,不用记编号。真要手打,/weld 不带参数回车会列出来;也可以去 .dsh/sessions/ 目录下看,每个对话是一个以 session- 开头的文件夹,文件夹名就是编号。

安装

用插件管理器:

dsh plugin install dsh-union-view

装完重启 DSH Web(或刷新页面),对话页面的标签栏会多出一个「联合视图」。

从源码装

pnpm 用不了的时候走这条路:

node scripts/build-client.mjs     # 把界面代码打包成 .dsh-plugin/client.js
node scripts/install-local.mjs    # 装进 DSH

第二行脚本做三件事:把整个项目复制到 ~/.dsh/plugins/dsh-union-view、在 DSH 的 node_modules 里建一个目录联接(不需要管理员权限)、把插件名登记进配置。会先备份配置文件,可以反复跑。

用法

  1. 打开任意对话,点顶部的「联合视图」标签
  2. 点「选择会话」,勾上你想看的对话(可以多选,下次会自动记住)
  3. 换摆法看
  4. 「系统事件」开关:打开后能看到更细的过程(工具调用、轮次边界),平时关着更清爽

它不做什么

列清楚,免得误会:

  • 不能在里面发消息:它是只读窗口。要接着聊,切回原对话,或者用 /weld 造一个新对话
  • 不会自动更新:新开的对话不会自己冒出来,点一下「刷新」
  • 不显示思考过程:只显示你和 AI 的正式发言,AI 的推理过程不带
  • 看不到已折叠的历史:如果某个对话之前被压缩过(上下文太长时 DSH 会折叠旧内容),联合视图看到的也是折叠后的样子
  • 不做对比和评分:它只是并排显示,不计算差异、不判断谁对谁错

给想改代码的人

展开技术细节

界面部分怎么挂上去

注册在 DSH 的 conversation.view 槽位(id: 'union',order: 6),和官方 gal-view 是同一个位置。

关键点:src/client.js 里必须写

export const inject = ['slots', 'sessions', 'uiConversation']

少写一个,DSH 就不会把对应服务挂到插件身上,ctx.sessions 永远是 undefined(插件会显示"宿主未暴露 sessions 服务")。package.json 里的 dsh.client.inject: ["slots"] 只负责让界面代码被加载,两回事。

数据从哪来

const ref = ctx.sessions.retain(sessionId, { source: 'unionView' })
await ref.ready
const chat = ctx.uiConversation.binding(ref.binding).target('chat')
const nodes = chat.getSnapshot().legacy.nodes   // 对话节点数组

纯客户端读取,只用 retain 的引用计数,不调 activate、不 prompt、不 fork。

怎么铺满整个对话区

根节点加 data-union-view,useEffect 往上找第一个 position !== 'static' 的祖先(实测是 .wSkVaW_body),给它加上 data-union-fill,定位规则写在注入的 CSS 里。

踩过的坑:定位一定要写在 CSS 里,不能写内联 style——内联的 position: relative 会盖掉 CSS 里的 absolute,面板会按内容高度一路涨到十几万像素。

/weld 怎么写入新对话

DSH 有个 ctx.sessions.create(),但它只准备内存里的会话对象,不落盘、不进侧栏账本(源码注释写得很直白:persistence is a plugin concern)。

手写日志这条路走不通:宿主对从磁盘读回的日志有很严的结构校验(turn 必须连续、引用不能悬空、assistant 消息要带 stream),照着报错猜了四轮结构,一次都没过。

现在的做法是让 dsh 自己写:

  1. sessionPersistence.create({ version, id, createdAt, cwd, isSeeded: false, delegationDepth: 0 }) 建日志文件和写句柄。这一步不能省:持久化插件靠 writers.get(session.id) 路由 session/event,没有写句柄时 flush 会静默返回——账本有 id、磁盘没文件。
  2. sessions.create() 建活会话,把合并好的事件一条条 append()。seq 由宿主自己发,天然连续,每一步还有 surfaceManager.validateNext 校验。
  3. handle.flush() 落盘。

好处是报错从「turn/start does not open the expected turn」这种无从下手的话,变成了「image/offload: target seq 87 is not a current surface node」——直接点名事件类型和序号。

走这条路顺带踩到三个坑:图片卸载/上下文压缩这类事件带着指向 surface node 的引用,合并后必然悬空(直接丢弃);assistant/message.data.stream 不能整段删(重建节点时会 for..of,字段没了就报 stream is not iterable),要留空数组;header 载荷不要带 type(会被判 unexpected field type),但必须带 version。

侧栏账本:对话列表不是扫目录得来的,是读 ~/.dsh/storages/workspace.json 里的 sessionIds 数组。所以最后还要调 workspaceRegistry.attachSession(newId),它会校验 header 里的 cwd 和真实路径一致,通过后才把编号插进数组。

改 workspace.json 只能用 JSON 解析器改。用正则删数组元素会留下孤立逗号,JSON 一坏,整个界面会变成「暂无会话」。

目录结构

src/client.js       界面(联合会话视图、三布局、合并按钮)
src/merge.mjs       合并逻辑(纯函数,不碰磁盘)
.dsh-plugin/index.mjs   Node 半边(注册 /weld 命令、写入新对话)
scripts/build-client.mjs   打包界面代码
scripts/install-local.mjs  本机安装
scripts/cleanup-test.mjs   清理测试会话(从账本和磁盘一起删)
scripts/e2e-verify.mjs     自动验收:三布局截图(需要浏览器调试端口)
scripts/probe-weld.mjs     自动验收:打开合并出来的新对话,检查有没有加载错误

已知限制

  • 只读视图最多渲染最近 400 条消息(分栏每列独立计算);轮次对齐最多 120 轮
  • 消息正文按纯文本显示,保留 **、反引号原样——这是有意的,转录要保真
  • 没有跨对话搜索、没有导出
  • /weld 生成的新对话,第一批请求的缓存要从零建立(新对话全新前缀,这个省不掉)
  • /weld 每个源只取最近 N 轮:两个几百条消息的对话加起来上万条,不截断会顶满窗口
  • /weld 生成的对话标题显示「未命名」:原标题带引用关系,截断后对不上,干脆不搬
  • /weld 是数百毫秒到几十秒的操作(几千条事件逐条交给宿主写),中间没有进度条,等侧栏冒出新对话即可
  • 同一个对话同时只能有一个写入者:如果目标对话正被另一个 DSH 实例占用,命令会报 session/writer-held,换一个空闲对话提交即可

许可

MIT

—/ 5

No ratings yet

Verified DSH bundle

Commit e91eb1af61e0

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout