dsh-session-copy
Verifieddsh-session-copy · v1.3.1 · MIT · Web UI
One copy button in the session header: deep link, whole conversation or just the messages you pick as Markdown, or the working directory. · 会话标题栏一键复制:深度链接、整段对话或勾选的消息 Markdown、工作目录。
Install
dsh plugin add dsh-session-copy Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Creators
Readme
dsh-session-copy
English | 中文

DSH Web 会话标题栏里的一个按钮。点开它,自己选要复制什么:深度链接、整段对话 Markdown、只复制勾选的消息,或工作目录。标题栏于是不再有一排平铺按钮和工具栏抢位置。
为什么做它
会话标题栏是一整条拥挤的图标区,而把一次对话复制出来原本要占掉其中三个位置。这个插件把三件事收进一个 28px 的按钮,菜单里还是那几项操作——标题栏除了一个图标之外什么都没多。
真正费功夫的是复制 Markdown。会话日志并不是聊天记录,而是一条混杂着工具调用、重试、被裁掉的行、注入上下文,以及跑久了之后出现的压缩检查点的事件流。这个插件在宿主侧把对话从这条流里重建出来,交回干净的 Markdown。
你会得到什么
| 菜单项 | 写进剪贴板的内容 |
|---|---|
| 复制深度链接 | @[标题](dsh-session:<base64url(会话 id)>),即标准 DSH 会话提及。粘到任何输入框里,宿主就会把该会话的快照带进来,其他对话因此可以引读本次会话。 |
| 复制为 Markdown | 整段对话渲染成干净 Markdown:每条消息一节,保留每一步助手回复,工具输出与思考过程折叠掉,压缩位置明确标注,代码围栏成对闭合。 |
| 选择消息复制… | 打开消息列表,勾选哪几条就只复制哪几条——就是 QQ/微信多选聊天记录那个手势。 |
| 复制工作目录 | 该会话的 cwd,取自会话列表投影,缺失时回落到宿主快照。 |
每次操作都会回一条小提示——复制 Markdown 时还带上字符数,让你在粘贴到别处之前就知道这是一段 400 字还是 40 万字。
只复制对话的一部分
一次长会话通常只有一小段值得分享:那次排查、那个决定、那句答案。选择消息复制… 会列出每一条消息的角色、一行预览和字数,勾选哪几条就只复制哪几条。

- 筛选能把几百行收窄到你找的那几条;全选只作用于当前筛选出来的结果,所以「把所有和解析器有关的都复制走」是两次点击。
- 计数实时显示已选多少条,复制出的文档开头会写明
> Excerpt: 12 of 340 messages selected,粘出去的片段因此不会被误当成被截断的整篇导出。 - 列表列的是模型当前仍持有的内容:压缩检查点会单独成为一行,要不要带上它由你决定。
- 勾选只发生在面板里——不点「复制所选」什么都不会写进剪贴板,按 Esc 直接关掉。
安装
从 npm 安装
dsh plugin add dsh-session-copy
在桌面版内部安装
桌面版有自己的插件管理器:请在 设置 → 插件 里安装,而不是用命令行(应用运行时 CLI 指向的是另一个 profile)。装完请重启 DeepSeek Harness——Loader 在启动时组装插件列表,刷新页面不够。
从这个源码目录安装
node scripts/install.mjs --check # 只体检,不改动
node scripts/install.mjs # 安装或刷新到当前 profile
node scripts/install.mjs --uninstall
从发行版 tarball 安装
适合不想从源码构建的宿主,也就是插件目录用的那条路径:
dsh plugin --profile web add \
https://github.com/2310770803/dsh-session-copy/releases/latest/download/dsh-session-copy.tgz
资产名刻意不带版本号:releases/latest/download/ 只在请求时解析 latest,文件名是照字面取的,所以带版本号的名字在下次发版后就会静默 404。发布流程会在每个 tag 上重传 dsh-session-copy.tgz,让这个链接始终有效。
Markdown 是怎么生成的
下面三个决定,就是「能用」和「坏了」的分界线,每一个都替换掉了一个真实的 bug:
保留每一步助手回复,而不是只留最后一条。 一个回合不是一条回复——一个 agentic 回合是二十个模型步骤夹着工具调用。只保留每回合最后一条 assistant/message,在真实会话上丢掉了 41% 的助手正文(26,657 字里只留 15,717 字),某个回合的 29 条中间消息更是全部消失。现在按顺序全部输出。
压缩是被标注,而不是被吞掉。 DSH 压缩会话时会写一条 compact-checkpoint 消息和一个 compaction/summary 事件,而压缩前的回合不再属于模型可见内容。把它们当作「注入上下文」丢掉,会让长会话导出时历史凭空缺失——读起来就是「复制坏了」。它们现在渲染成显式的会话已压缩标记,于是有意的摘要永远不会被误当成丢失的内容。
代码围栏在整篇文档上配平,而不是逐条消息。 一条消息若在围栏内结尾,过去会被补一个闭合围栏,结果下一条消息的围栏就全错位了;有一份 21KB 的导出因此出现 34 个游离的围栏标记。现在围栏按整篇文档追踪,粘出去的内容渲染出来是什么样,写下来就是什么样。
选择只被当作下标信任。 面板把展示过的位置回传给宿主;越界、重复、非整数的下标一律丢弃(而不是让后面的项整体前移、结果落到另一条消息上),并回报丢了几项。这样一次复制永远不可能在无人察觉时粘到别人的那一轮对话。
读取同样有边界、也不跟版本绑死:
- 优先用
sessionQuery.readSurface(当前模型可见面),而不是readSession(含被compaction/prune遮蔽行的完整原始日志);可见面无法折叠时回落到readSession。 - 响应上限 220,000 字符,按小节边界截断——因为真实会话里出现过 434KB 的文档,超出了单次剪贴板写入可靠接受的范围。
- 剪贴板写入依次尝试宿主写入器、
textarea+execCommand(长文本和窗口失焦时唯一可靠的那条)、异步 Clipboard API,并整体重试两轮。DSH 自带的写入器一旦navigator.clipboard.writeText抛错就直接返回false且不降级,只委托给它是个陷阱。
在 DSH 里:插件市场
仓库发布了一份单插件插件市场索引——就是 DSH 插件 → 发现 与 plugin_market 工具读取的格式:
- 先在「设置 → 插件」里打开市场所需的
@deepseek-ai/dsh-experimental-plugin-market-bundle(DSH 自带,默认关闭); - 插件 → 发现 → 索引源 添加
https://cdn.jsdelivr.net/gh/2310770803/dsh-session-copy@main/index.json; - 搜「session copy」即可安装。
索引固定的是具体 commit 而不是分支,所以装下来的版本可复现。node scripts/build-index.mjs 依据 package.json 与 locale/ 重新生成 index.json,node scripts/check-index.mjs 用宿主自己的解析器校验结果;索引指向的必须是它前面那个 commit,因此锁定的 SHA 不会等于该文件自身的提交。
node scripts/search-index.mjs 回答的是另一个问题——在「发现」里输入一个词,到底能不能搜出这个插件。它跑的是宿主自己的搜索类,所以通过意味着「发现」的列表真的会收敛到这个插件,而不只是 JSON 格式合法:
node scripts/search-index.mjs --market <dir> --validator <dir>
两个 <dir> 分别是已安装 DSH 里的 @deepseek-ai/dsh-plugin-market 与 @deepseek-ai/dsh-plugin-market-index,可从 resources/app.asar 的 dsh/node_modules/@deepseek-ai/ 下解出。
插件同时收录在 awesome-dsh-plugin 目录中,条目就是该仓库里的一个 YAML 文件:data/plugins/2310770803__dsh-session-copy.yml。
供应链
版本由 GitHub Actions 构建,并以 npm 的**信任发布(trusted publishing)**发布:工作流用短时有效的 OIDC 令牌认证,仓库里不存在任何长期 npm token。每个版本都附带 SLSA provenance 证明,npm 上的 tarball 可追溯到构建它的 commit 与 workflow。发布是幂等的——对已发布的版本重跑发布只会跳过,不会报错。
兼容性
| 宿主 | DSH 0.2.1-rc.2 及更新版本(@deepseek-ai/dsh-* 对等依赖锁定在预发布分支上) |
| 界面 | DSH Web 与桌面版的 Web 视图 |
| 权限 | 无——宿主路由只读,且限定同源访问 |
所有能力都做了特性探测。若将来 DSH 去掉共享的 Menu 原语,三个操作会改用自绘弹层,而不会退化成三个平铺按钮;若某个服务改名,受影响的部分会降级,而不是在启动激活审计里抛错。
显示元数据
「设置 → 插件」里的卡片不加载客户端代码就能读出本包的元数据:
locale/en.json与locale/zh.json—meta.title/meta.description;- 缺失时回落到
package.json的name/description; - 卡片图标来自
package.json的icon(./icon.svg)。
目录结构
| 路径 | 作用 |
|---|---|
src/plugin.js |
宿主半边:在 webServer 上注册只读快照路由。 |
src/lib/route.js |
/api/session-copy.snapshot 处理器、同源校验与体积限制。 |
src/lib/markdown.js |
会话事件 → Markdown 渲染。 |
lib/client.js |
浏览器半边,以 loader 的闭包工厂格式预构建发布。 |
cordis.patch.yml |
把 session-copy 这一行插进 Web profile。 |
locale/、icon.svg |
插件列表里的标题、说明与图标。 |