npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@heixui/dsh-session-suite

v0.10.1

Published

Session checkpoints for DSH: rewind a conversation with its files and memory, permanent session deletion, and shareable exports

Readme

dsh-session-suite(会话套件)

为 DeepSeek Harness(DSH)提供会话检查点能力:把对话 连同工作区文件和会话记忆一起回滚到任意一条消息,永久删除会话,把会话导出成图片或单文件网页, 并且维护一套跨会话、跨工作区的长期记忆。

这是一个 DSH 插件包(Host + Web Client),对标 Trae 的会话操作体验,已在 desktop profile 上验证加载。

| 能力 | 位置 | |---|---| | 回滚到某条消息(对话 + 文件 + 记忆) | 每条最终助手消息的 ↺ 按钮,输入框上方的行内确认条 | | 删除某一轮(只动对话与记忆) | ↺ 旁边的 🗑 按钮 | | 删除选中的一段对话 | 会话 … 菜单 → 删除对话片段(可选轮次) | | 永久删除会话 | 会话 … 菜单 → 删除任务(二次确认弹窗) | | 分享选中的片段 | 会话 … 菜单 → 分享会话:勾选轮次后生成图片 / 网页 / Markdown / JSON / 复制文本 | | 快照管理 | 会话 … 菜单 → 会话快照,以及 设置 → 会话快照 | | 长期记忆(全局 + 工作区双库) | 设置 → 长期记忆;【记住:…】;memory 工具 | | 用 WorkBuddy / Trae 积分跑模型 | 不在本包内 —— 见 用 WorkBuddy 和 Trae 的积分 |

长期记忆

两个隔离的库(global 与按工作区划分的 workspace)、四因子重要性评分(频率 · 新近度 · 情感强度 · 独特性)、 动态衰减加冷归档、相似聚类并压缩成摘要、以及从重复标签里提炼规则型记忆。

最关键的一点:隐式记忆不会偷偷入库。每轮结束后由一次轻量模型调用提出候选,进入设置页的待确认队列, 只有你点「保留」才真正写入;而 【记住:…】 / 【新增全局记忆:…】 这类显式指令会跳过队列直接写入。

评分公式、阈值、生命周期、指令、存储结构都在 docs/long-term-memory.md。

各操作分别动到什么

| 操作 | 对话 | 会话记忆 | 工作区文件 | |---|---|---|---| | 回滚(↺) | 截断到该消息 | 裁剪到该处 | 恢复到该轮快照 | | 删除一轮(🗑) | 提问 + 回答 + 中间的步骤一起移除 | 裁剪到该处 | 不动 | | 删除对话片段 | 从选中的第一轮(含它的提问)截断 | 裁剪到该处 | 不动 | | 删除任务 | 从磁盘移除 | 移除 | 不动(文件夹保留) |

只有回滚会把文件写回去,其它删除都不碰工作区。会话日志是只追加的,所以「截断」只能是「保留前缀 + 原来的会话退休」——选中范围之后的内容会一起消失。

分享

分享弹窗会列出每一轮对话(你的提问 + 回答预览),默认全选,取消勾选即可排除。然后选输出形式:

  • 图片:把选中轮次画成一张 PNG(用 canvas 直接画,不依赖任何第三方库);
  • 网页文件:单文件 HTML,内联样式、跟随明暗主题、可直接打印成 PDF;
  • Markdown / JSON:贴进 issue 或喂给别的工具;
  • 复制文本:纯文本进剪贴板。

两种富格式都会真正渲染 Markdown,而不是原样吐出:标题、粗体/斜体、行内代码、代码块、有序/无序列表、引用、管道表格、链接。Host 侧把日志解析成带内联样式的 block(lib/markdown.js),HTML 渲染器和 canvas 渲染器吃的是同一份结构,所以网页和图片不会出现两种排版。

截图会跟着一起导出:附件不在会话日志里,Host 通过 attachments.imageHostPath 取到原文件,按预算内联成 data URL,网页因此仍是单文件。图片里截图按整卡宽度绘制(限高、保持比例);网页里每张截图是一个按钮,点开用内置查看器按原始分辨率看——页面只带一小段内联脚本,不发任何外部请求。

另外要说清楚:位图(PNG/JPG)里的图片不可能点开。需要「点开看原图」就分享网页文件,而不是导出的 PNG。

导出到底包含什么

会话日志里大部分不是对话:运行环境、技能目录、时间采样、子代理回执、工具调用,以及模型在工具之间写的过渡性说明。这些都不该进导出:

  • 只有 source.kind === "user" 的消息才算「你说了什么」,user 角色下的其它事件全部丢弃;
  • 默认勾选 只导出最终回答:每轮保留你的提问 + 最后那条答复;
  • 取消勾选则导出每个助手片段,包括工具调用之间的过程说明;
  • 富格式里永远不出现工具输出,Markdown / JSON 也走同一套取舍。

同一轮里的多个助手片段会合并成一张卡片,只有工具调用、没有正文的步骤直接丢弃,不会导出成一堆空标签。

真正的「在线链接」需要一台服务器托管内容;这个包用可离线打开的网页文件来替代,而不是生成一个只有本机能打开的链接。

安装

从 npm 安装:

dsh plugin --profile desktop install @heixui/dsh-session-suite

发布用的是带作用域的包名,因为不带作用域的 dsh-session-suite 在 npm 上属于另一个项目 (chc-1986/dsh-session-suite),不是这个。作用域只是 npm 上的包身份——插件 id、存储目录、 bundle 名仍然是 dsh-session-suite,所以旧版本留下的数据照样能找到。

从本仓库检出目录安装:

# 从本仓库检出目录安装
dsh plugin --profile desktop install .

或者让运行中的 Harness 安装(plugin_manager 就是这样做的):

plugin_manager({ action: 'install_bundle', target: 'D:/path/to/dsh-session-suite' })

安装后刷新页面即可。整个套件只有一个 Host 行(session-suite),配置写在 cordis.patch.yml;Client 部分由 package.json 的 dsh.client 字段自动发现。

回滚是怎么实现的

DSH 的会话日志是只追加的,而且是唯一事实来源,所以回滚不能删除事件。套件把它做成一次原子的三段式操作:

  1. 保护:先给工作区和记忆账本拍一个临时 undo 快照(10 分钟有效)。
  2. 对话:sessionController.fork({ sessionId, atSeq }) 把事件前缀完整分叉成新会话(边界包含该消息、 沿用同一个 cwd 与 agent preset)。
  3. 文件:恢复该轮开始时记录的快照——改过的文件写回、轮次里新建的文件删除、会话开始前就存在的文件不动。
  4. 记忆:裁掉晚于目标序列的记忆条目。
  5. 退休:把原会话归档(侧边栏隐藏,日志保留),整个过程不销毁任何数据。

「撤销回滚」会把这五项全部还原:文件、记忆、被归档的会话,以及分叉出来的子会话。

快照在磁盘上

%DSH_HOME%/storages/dsh-session-suite/
  sessions/<sessionId>/index.json        # 检查点索引
  sessions/<sessionId>/manifests/*.json  # 路径 -> {hash, size, mtime}
  sessions/<sessionId>/blobs/<ab>/<hash> # gzip 压缩的文件内容,按内容寻址
  memory/<sessionId>.json                # 会话记忆账本
  memory/_global.json                    # 跨会话长期记忆
  undo/<id>.json                         # 回滚撤销包
  • 增量:manifest 只记录路径与哈希,相同内容只存一份;未变化的文件靠上一份 manifest 的 (size, mtime) 指纹直接跳过,不再读取。
  • 配额:每个会话保留最近 10 个自动检查点;锁定的一律保留;临时撤销包不占配额,10 分钟后清理。
  • 忽略清单:node_modules、.git、缓存、构建产物、日志、锁文件、二进制文件都不记录;config.ignore 可追加自定义规则。
  • 上限:单文件超过 2 MiB、单次超过 4000 个文件会跳过,避免拖慢对话。

配置

- insert:
    - id: session-suite
      name: dsh-session-suite
      config:
        keep: 10               # 每个会话保留的自动检查点数量
        autoCheckpoint: true   # 每轮开始时自动拍快照
        maxFileBytes: 2097152
        maxFiles: 4000
        sweepIntervalMs: 300000
        ignore: []             # 追加的忽略规则

目录结构

index.js            Host 半部:注册路由、按轮拍快照、清理定时器
client.js           Client 半部:菜单行、消息操作、确认条、弹窗、设置页
lib/api.js          受保护的 Fetch 路由(POST api/session-suite)
lib/service.js      会话操作:rollback / undo / delete / share / deleteMessage
lib/checkpoints.js  内容寻址的增量快照存储
lib/memory.js       会话记忆账本(支持按序列裁剪)
lib/share.js        会话日志 → Markdown / 文本 / JSON
locale/{en,zh}.json 插件管理器的展示信息与文案
tools/              自测脚本与预清理脚本(不随插件打包)
docs/               需求与 API 笔记

0.4.0 改了什么

本次重构的是记忆引擎,会话相关功能未变。

| 方面 | 改前 | 改后 | |---|---|---| | 重复记忆 | 46 条里同一主题占 7 条 | 维护幂等(连续 6 轮验证);存量重复已合并进冷库 | | 常驻层 | 2 条扁平记忆,混在「相关」区块里 | 最多 5 张主题卡片,独立分区标注 | | 情景记忆 | 没有 | kind: 'episode',带发生时间、情境、结果;衰减加倍 | | dense 重排 | 实现了但默认关,权重未验证 | 可选,默认关;权重经实测校准(8 条改写查询 MRR 0.535→0.760,recall 不变) | | 隐私分级 | 只有 scope | public/internal/secret;凭据自动识别;secret 永不注入、永不导出 | | 隐式队列 | 关键词闸门误杀 7/10 合法候选 | 闸门已移除;空队列会说明原因 |

embedding 是可选增强,默认不下载任何东西。 不装模型就是纯词法检索,功能完整;装了才有跨语言与改写召回。 实测效果与安装方式见 docs/memory-architecture.md 和 tools/download-embedding.mjs。

0.4.1 改了什么

新增侧边栏入口,修掉几个「看起来能用、实际没生效」的问题,并把界面上说不清的数字说清楚。

| 方面 | 改前 | 改后 | |---|---|---| | 入口位置 | 只能在 设置 → 会话快照 / 长期记忆 里找,三步才能到 | 左侧栏「会话套件」图标,点开即用;设置页那三个重复入口已移除 | | 记忆总数 | 面板显示 17,设置页显示 51 —— 同一句话两个数 | 面板遍历所有已登记工作区;每个数字都写明统计口径,并给出分工作区明细 | | 归档恢复 | 点了没反应,磁盘上的标记纹丝不动 | 修了两个 Host 服务参数形状不一致的问题,恢复真正生效 | | 快照数量 | 预算 10 张,列表却有 16 张,无从解释 | 区分「自有 10 张」与「继承 6 张」,继承项打徽章并说明为何不计入预算 | | 隐式提取模型 | 只能选「关闭」和「用当前模型」,看不到自己配的模型 | 从 Host 的 llm 服务枚举真实可用模型;默认关闭,不选就不消耗 token | | 长列表 | 配置区被五十条记忆挤到看不见 | 列表独立滚动,配置区始终可见 | | 会话记忆 | 一个从未写入的残缺区块,旁边就是 dsh 自带的轨迹 | 已移除 —— 轨迹是权威且不可改,手写笔记只会误导 |

关于记忆总数为什么会有多个数字:长期记忆分「全局库」和「各工作区的库」。 longterm.list 不带 cwd 时只返回全局库,带上才是全局 + 该工作区。面板没有工作区概念, 于是只显示了全局那部分。现在面板会遍历所有已登记工作区,并在界面上直接列出每个库的条数。

测试

全部自测脚本都在临时目录里跑,不会碰真实的 DSH 数据。

node tools/selftest.mjs                    # 快照 / 忽略规则 / 恢复 / 淘汰 / 记忆
node tools/selftest-memory-dedupe.mjs      # 维护幂等,重复不再生成
node tools/selftest-resident-cards.mjs     # 常驻卡片分组与预算
node tools/selftest-episode-memory.mjs     # 情景记忆类型与衰减
node tools/selftest-memory-privacy.mjs     # 隐私分级与导出过滤
node tools/selftest-implicit-queue.mjs     # 隐式候选不再被误杀
node tools/selftest-memory-conflicts.mjs   # 矛盾对只入队,绝不自动裁决
node tools/selftest-pain-distill.mjs       # 失败轮自动提炼教训,去重且封顶
node tools/selftest-p2p3-wiring.mjs        # 两个功能每一层都真的接上了
node tools/probe-dense-rerank.mjs          # dense 重排的实测对比(需要模型)

0.10.1 改了什么

0.10.0 上线后对着真实记忆库审计发现的五个缺陷,全部有实测证据。

| 方面 | 之前 | 现在 | |---|---|---| | 跨 store 矛盾 | 扫描在「每个 store 的循环内部」,所以全局规则与工作区规则互相矛盾时,两条都注入同一个提示词、且永不入队(实测:相似度 0.737,两侧都注入,队列为空) | 扫描额外把全局库与各工作区库对比;裁决时把输的一方写回它自己所在的 store | | 队列读取 | conflicts(cwd) 用工作区过滤共享队列文件后把过滤结果写回,于是「不是我的」被当成「已失效」——在任一工作区打开记忆页,就删掉其他所有工作区待裁决的矛盾(实测 1 → 0) | 只清理本次真正检查过的条目,其他工作区的原样写回 | | 教训里带凭据 | 失败信息里回显 sk-… 会让整条提炼出的教训被标 secret,而 secret 永不注入——教训(防止该错误的知识)消失了,凭据却留在库里 | 捕获时脱敏成 [已脱敏],教训保持可注入 | | 解析不到工作区 | remember({scope:'workspace'}) 没有 cwd 会抛错,hook 捕获后只记日志,教训静默丢失 | 回退到全局库——一条到处都适用的教训本就该放那里 | | 每轮开销 | 痛苦轮 hook 在每次 turn/end 重读并重解析整个会话日志:4 万事件实测 JSON 解析 186ms,而检测只要 1.15ms | 只读最后一轮窗口,开销降低 94% |

另有两个小的:自查纠错线索仍会误伤散文(「实际上全部生效了…算错了」);反转裁决后旧赢家同时带 supersededBy 和 frozen,让一条已退役的记忆看起来像被用户钉住的。

0.10.0 改了什么

两个功能出自同一个毛病:记忆库对错误的东西很自信。

| 方面 | 之前 | 现在 | |---|---|---| | 互相矛盾的记忆 | 被纠正的旧观念原样留在库里,和新观念一起注入,模型只能按新旧掷硬币 | 成对检测后入队,由人来选哪条为真;输的那条标 superseded,赢的冻结 | | 纠错被吞 | 长记忆只改一个从句,相似度 0.88 就会走「重复」分支:旧说法被强化,新说法被静默丢弃 | 反极性的近重复不再被吞,两边都留在库里交给队列裁决 | | 失败的轮次 | 一轮搞砸了什么都不会留下——模型当时正在救火,最不会写教训 | 从该轮自己的事件日志自动提炼教训,按失败主题去重,每库封顶 12 条 | | 聚类 | 矛盾对会被合并成一句「必须…;绝不…」 | 待裁决的矛盾对不聚类;教训也不聚类 | | 密钥误标(P2a) | 光出现「密钥 / token / secret」就算凭据,实测 8 条误标全部是误判,被静默封存 | 只认凭据实体(前缀、分隔符、17 位号、卡号、手机号、邮箱) |

为什么矛盾不自动裁决。 「同主题、反极性」同样能匹配:被撤回的旧观念、加了条件的规则、计划与实情。检测器分不清这三种,猜错就等于悄悄改写用户的信念。所以检测自动,裁决留给人。

立场是按从句比的,不是按整条。 真实记忆有 100–300 字、包含多个从句;实测本库 64 条活跃记忆里有 29 条同时含「要求」和「否定」,所以「这条是不是在禁止」对它们没有答案。第一版遇到这种就放弃判断,45 个人造矛盾里漏掉 20 个。改成按句号/分号切句、逐句比对后,改一个从句就能被发现,其余部分不必变。

为什么教训提炼不调模型。 本套件试过七次「每轮后台调一次小模型」,一次都没触发过,其中一次直接把 Host 搞崩;而且它对每一轮都要花钱,包括没出问题的那些。改成读该轮自己的事件日志:不调模型、不联网、不花 token,这才让它有资格每轮都跑。

已知边界

  • 回滚不改写日志,而是「分叉到目标消息 + 归档原会话」。这是只追加日志支持的唯一形态,也正是撤销能够成立的原因。
  • 快照覆盖的是文件,不覆盖外部副作用:数据库状态、运行中的进程、网络调用都不在其中。
  • 跨会话记忆是本套件自己的账本(memory/),不是 DSH 内核能力;因为由套件拥有,所以能一起回滚。
  • HTTP 路由受 DSH Connection 保护(Host/Origin 校验 + 浏览器会话),只有已鉴权的 Web GUI 能访问。

许可证

MIT。