跨会话引用 · dsh-cross-session-ref
DeepSeek Harness 的持久化 UI 插件:把任意会话里的「问答卡片」拖到另一个会话。之后在这个会话里正常提问时,被拖入的卡片(问题原文 + 当时的回答 + 附件)会被动态拼进 Prompt,成为模型可用的上下文——但不会在聊天记录里变成虚假的用户气泡。
A DeepSeek Harness plugin that lets you drag Q&A cards across sessions and injects them (question, original answer, attachments) into the next prompt without polluting the transcript.

它解决什么问题
DSH 的一个会话只能看见自己的上下文。当你需要「把 A 会话里已经问清楚的结论,拿到 B 会话里继续用」时,通常只能复制粘贴,附件还要手动重传。
这个插件把这件事变成拖一下:来源会话的问答保持原样,目标会话下一轮提问自动带上这些内容;用完可以右键移除,也可以同时引用到多个会话。
功能
- 实时同步:Host 监听
session/created、session/event、session/title、session/disposed等事件并用 SSE 推送;新建会话、发新问答、改标题、删除会话都会即时反映到面板,没有轮询快照。 - 会话方块:等高、等长(210 × 80),显示 DSH 生成的 AI 总结标题(
session/title),标题在方块内水平垂直居中;点击展开/收起,展开时方块播放scale(0.95) → scale(1.05) → scale(1)的弹性动画。 - 问答节点:统一宽度 200px、高度随文字;默认显示前 20 字摘要(两行截断),含附件时显示 📎 与数量。
- 颜色可辨:12 个色槽由 Host 统一分配并持久化到
colors.json,同一会话的卡片同色、不同会话异色;跨会话拖入的卡片保留来源会话的颜色,便于确认它摘自哪里。 - 悬浮预览:悬停卡片显示完整【问】【答】与附件名,排版紧凑。
- 全卡片拖拽:卡片任意位置按住即可拖(6px 阈值防误触);幽灵以按下点为缩放中心跟随鼠标、
grabbing光标,并禁用浏览器原生拖拽图;拖到目标卡片之间显示虚线虚影;靠近面板/卡片行边缘自动平滑滚动。 - 非破坏性:拖入只是「引用」,源会话的卡片原封不动;右键引用卡片可「从本会话移除」。
- 动态注入:被引用的卡片不会出现在聊天主体;下一轮提问时作为「本会话更早的对话记录」注入,只注入一次,已注入过的卡片不会重复。
- 容错:客户端有错误边界(渲染异常显示错误而不是白屏);Host 对账会清理被删除会话的方块与失效引用。

界面说明
| 位置 | 元素 | 说明 |
|---|---|---|
| 左侧 | 会话方块 | 白底黑框,等高等长;显示 AI 标题;运行中的会话在方块右侧显示绿点;点击展开/收起 |
| 右侧 | 问答节点 | 白底、彩色描边(会话色);圆形色点 + 两行摘要 + 📎;插入的卡片带 ↳ 来自「会话名」 |
| 节点之间 | 短引线 | 10px 横线 + 4px 圆点,颜色跟随卡片,不会因窗口宽度被拉伸 |
| 悬停 | 预览浮层 | 完整【问】【答】、附件名,最大宽度 480px |
| 右键 | 菜单 | 仅引用卡片有此菜单:「从本会话移除」 |
安装
要求 DSH Desktop 0.2.x(在 0.2.1-rc.3 上测试),并已有可用 profile。
方式一:从 npm 或 GitHub 安装
# 从 npm(已发布 0.2.1)
dsh plugin --profile web add dsh-cross-session-ref
# 或直接从 GitHub 仓库
dsh plugin --profile web add github:kanmanJO/dsh-cross-session-ref
dsh web --profile web
也可以直接在插件市场里搜「跨会话引用」安装。
dsh plugin会把参数转发给该 profile 的 pnpm。Desktop 的 profile 由桌面端的命令运行时管理,CLI 会拒绝直接操作desktopprofile——桌面用户请用方式二。
方式二:本地安装(Desktop 推荐)
git clone https://github.com/kanmanJO/dsh-cross-session-ref(或下载 zip 解压)。- 打开 DSH 的「插件」页面,选择安装本地插件(bundle),指向解压后的
dsh-cross-session-ref目录。 - 重载插件或重启 DSH,然后
Ctrl+Shift+R硬刷新页面。
安装后侧边栏会出现「跨会话引用」图标,点击进入整页面板。
方式三:装到自己的工作区里开发
git clone https://github.com/kanmanJO/dsh-cross-session-ref
cd dsh-cross-session-ref
npm run check
npm test
用 DSH 的插件面板把该目录作为本地 bundle 安装即可;它是 junction/软链进 profile 的,改源码后重载插件 + 刷新页面即生效。
5 分钟验收清单
- 侧边栏出现「跨会话引用」图标,点开能看到当前工作区的会话方块(不含子会话、已删除、已归档会话)。
- 开两个窗口,在 DSH 新建会话 → 方块自动出现;在主界面发一条消息 → 该会话节点实时增加。
- 点方块 → 弹性展开;同一会话的节点同色,换一个会话 → 颜色不同。
- 按住任意卡片拖到另一个会话的方块/节点之间 → 有半透明幽灵、虚线虚影、边缘自动滚动。
- 松手后,被拖的卡片出现在目标会话里且保持来源会话的颜色;源会话里的卡片不变。
- 在目标会话提问 → 模型能引用到被拖卡片的内容与附件;聊天记录里它是一条灰色上下文行,不是用户气泡。
- 右键该卡片 →「从本会话移除」→ 节点消失,下一轮不再注入。
使用
- 点会话方块展开问题节点。
- 按住任意卡片拖到另一个会话的方块或节点之间,松手完成引用(松手位置就是虚线框提示的位置)。
- 在目标会话正常提问,被引用的卡片作为上下文参与本轮回答。
- 撤销引用:右键该卡片 →「从本会话移除」。
- 同一张卡可以同时引用到多个会话;源会话内容不受影响。
注入是怎么实现的
Host 半监听 agent/pre-step(waterfall,注册时 prepend: true):
- 只在一轮的第一步(
step === 1)工作,避免同一轮多步重复注入。 - 取出该会话的全部引用,读取源会话的
sessionQuery.readSurface(),把每个user/message(source.kind === 'user')和紧随其后的assistant/message折叠成一张卡片;问题文本取 content 中的text块,附件取file/image块。 - 过滤掉已经注入过的卡片:扫描会话日志里
source.kind === 'cross-session-ref'消息的source.cards;发生 compaction 时清空缓存重新计算。 - 构造一条
source.kind === 'cross-session-ref'的 user 消息,插到本轮用户消息之前。内容以「本会话更早的对话记录」呈现:
模型读到的顺序是「更早的记录 → 用户最新消息」,因此会把卡片内容当成既有上下文,而不是一条待回答的新问题。(以下是本会话中更早的一段对话记录:这些问答都已经发生过,也已经有回答。请把其中的信息当作你已经知道的本会话上下文;不要重新回答其中的问题,也不要向用户确认它指的是哪一段,继续处理用户最新的那条消息即可。) 用户:<问题> 助手:<当时的回答> (这条消息附带的文件:a.png、b.md) - 注入消息在聊天记录里显示为灰色的「上下文行」,不是用户气泡——这是 DSH 对未知
source.kind的默认渲染(contextProducer的 default 分支),所以不需要伪造用户消息。 - 附件以
{ type: 'file', attachment: { name, bytes, attachmentId } }/{ type: 'image', ... }块原样携带,模型可正常读取;纯文本总预算 60000 字符,单条问答截断到 20000 字符。
数据与接口
插件不改变 DSH 的会话日志格式,只新增两类本地文件:
| 路径 | 内容 |
|---|---|
<DSH_HOME>/cross-session-ref/links.json |
引用关系(目标会话、来源会话、卡片 id、位置、加入时间) |
<DSH_HOME>/cross-session-ref/colors.json |
会话 → 颜色槽位分配 |
DSH_HOME 依次取 DSH_HOME 环境变量 → DSH_PROFILE_DIR 的父目录 → ~/.dsh。
Host 自建同源 HTTP 路由(仅本机页面使用,不监听额外端口):
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /cross-session-ref/state |
全量快照(会话、卡片、引用) |
| GET | /cross-session-ref/stream |
SSE 实时帧(snapshot / sessions / cards / links) |
| GET | /cross-session-ref/health |
健康检查 |
| POST | /cross-session-ref/link |
建立引用(body: targetSessionId、sourceSessionId、cardId、index) |
| POST | /cross-session-ref/unlink |
删除引用(body: linkId) |
| POST | /cross-session-ref/refresh |
强制刷新缓存并广播 |
会话被删除后,引用会在下一次对账(约 6 秒内)被清理。
隐私与安全
- 所有数据只留在本机
<DSH_HOME>/cross-session-ref/,插件不发起任何外部网络请求。 - 注入内容来自你自己会话的日志,注入目标也只是你自己打开的会话。
- 路由由 DSH 的
webServer.register({ kind: 'prefix' })注册,和主界面同源;没有额外端口、没有跨域暴露。
兼容性与边界
- 依赖 DSH 内部服务:Host 侧
webServer、sessionQuery、workspaceRegistry;Client 侧sidebar.panellist/mainslot。DSH 大版本升级后可能需要适配。 - 12 个色槽用完后循环复用(同色会话用明暗区分),会话特别多时颜色不再唯一。
- 面板只展示当前工作区的会话:排除 subagent 子会话、已归档会话、以及被其它插件删除但仍残留在内存里的会话。
- 只对「继续提问」生效,不会改写历史消息;删除引用也不会撤回已经注入过的历史上下文。
- 会话标题来自 DSH 的
session/title事件;标题还没生成时显示「新会话」。
常见问题
Q:侧边栏没有出现图标?
A:确认插件已安装并启用,然后硬刷新页面(Ctrl+Shift+R);仍不行就看 DSH 的插件日志。
Q:方块里标题是空的 / 显示 session-xxxx?
A:说明该会话还没有 AI 标题。发一条消息或改一次标题后会实时刷新。
Q:拖进去了但模型说没看到?
A:注入发生在「下一步提问」时(step === 1)。请在目标会话再发一条消息;也可以用 GET /cross-session-ref/state 确认该会话的 cards 里有 inserted: true 的节点。
Q:为什么聊天里有一条灰色「上下文」行? A:那是注入消息的渲染形态,不是用户气泡。它记录了这一轮带进了哪些卡片。
Q:怎么彻底卸载?
A:在 DSH 插件页面禁用/卸载本插件,然后删除 <DSH_HOME>/cross-session-ref/ 目录即可(引用与配色都在这)。
开发与自测
npm run check # node --check index.js client.js
npm test # 假 ctx + 临时 DSH_HOME 跑 33 项离线检查
自测覆盖:问答折叠与附件、同会话同色 / 异会话异色、实时事件建卡、引用去重、来源色保留、源会话不变、注入只一次、注入位于用户消息之前、注入文案不再提来源会话、step 2 不注入、unlink 后可再注入、links.json / colors.json 落盘、删除会话后清理引用。
仓库结构:
dsh-cross-session-ref/
├── index.js # Host 半:会话索引、引用存储、SSE、pre-step 注入
├── client.js # Client 半:侧边栏图标 + 整页面板(React,无框架依赖)
├── cordis.patch.yml # bundle 挂载补丁
├── locale/{zh,en}.json # 面板标题/描述
├── icon.svg # 侧边栏图标
├── tests/selftest.mjs # 33 项离线自测
├── docs/screenshots/ # README 截图
├── PUBLISH.md # 发布到 GitHub 的 3 步
├── publish.ps1 # 一键发布脚本(检查 → 自测 → commit → push)
├── CHANGELOG.md
└── LICENSE (MIT)
发布到 GitHub
见 PUBLISH.md:装好 Git、建一个空仓库,然后
powershell -ExecutionPolicy Bypass -File .\publish.ps1
CI 会在每次 push 后跑语法检查与自测。
许可证
MIT © 2026 kanmanJO
No comments yet. Be the first to write one.