dsh-plugin-tlmemory
跨会话、跨工程的长期记忆:turn/end 时非阻塞派发无感静默沉淀,写入本机 SQLite(FTS5 trigram 全文索引,含增删改同步触发器)供后续回合召回;记忆按项目作用域与全局树分层存储,并自带内嵌 Web 看板(插件自身起 HTTP 服务,非独立进程)用于浏览、图谱与受控删除。
安装
dsh plugin --profile web add dsh-plugin-tlmemory
沙箱测试结果
| 阶段 | 结果 |
|---|---|
| 1 安装 | ✓ |
| 2 列装 | ✓ |
| 3 挂载 | ✓ |
| 4 启动 | — |
四阶段在隔离沙箱里跑:安装(有网)→ 列装 → 挂载 → 启动(后三步断网)。
README
dsh-plugin-tlmemory
DeepSeek Harness (DSH) 原生无感记忆与自省管理插件。
为 DeepSeek Harness 提供会话级静默记忆提炼持久化、SQLite FTS5 全文检索,以及与 DSH 官方 Shell(如 SSH、任务看板)100% 深度融合的一级主视口记忆看板。
1.0.0 起适配 DSH 0.2.1-rc.2:新增设置页「记忆」面板(dsh.client 契约 + external: ["react"]),八个配置项全部有默认值、零必填,保存后立即生效。
✨ 核心特性
- 原生设置页(1.0.0 新增):DSH 设置页左栏「记忆」直接读写本插件配置(库路径、看板端口、召回条数、自动提炼与间隔)。字段全部标记为 volatile,保存即热生效、无需重启;读写走宿主
remote.settings并带 revision 乐观并发,多窗口同时改会提示冲突并回填最新值;外部(补丁层 / 另一窗口)改动经settings/document-updated实时反映到面板。 - 自动进化 + 零审核(1.3.0 新增):默认不设人审——沉淀直接
confirmed入库,疑似指令式规则只记一条warn日志便于追溯,不再进待确认区等人点确认;矛盾检测发现旧结论被新结论推翻时,旧条自动置superseded(退出召回、原文留档、merged_into记录取代者、可逐条restoreSuperseded回滚)。同一轮还会做确定性目录归并(mergeSimilarDirectories):同父目录下名字近义(二元组 Dice ≥ 0.55)的兄弟目录合成一棵,只改挂载位置与路径前缀,不动任何 content,零 LLM 成本、可重复执行。想恢复人工把关,把requireReview打开即可。 - 记忆自动归并(1.1.0 新增):历史沉淀容易「一个结论一条记忆」——同一主题被切成十几条一两句话的碎叶。开启后每轮结束(及每 N 轮)做一次确定性聚类:路径逐段相似(段数相同、每段字符二元组 Dice ≥ 0.55,例如「部署运维」≈「部署与运维」)或名称相似 ≥ 0.35 且关键词重叠 ≥ 2 的记忆归为一簇,每簇 2–8 条交给模型合成一条完整、可读的结论(含条件、理由与边界),原文以
merged_from留档可追溯;被并掉的叶子置merged_into后不再召回、在看板显示「已并入」。全部走保守口径 —— 宁可少合,也不把不同主题搅在一起。默认开启(enableAutoMerge: true,每轮 compaction 最多折叠 8 簇),不想要可在设置页关掉;想先看会合并成什么,用tlmemory_consolidate的dry_run或pnpm --filter dsh-plugin-tlmemory preview:merge。 - 记忆归属不再改道(1.2.0 修正):旧版遇到「不在宿主已登记工作区里的目录」时,会把这条记忆改道写进
workspace.json的第一个工作区(workspaceIds[0],往往是一个与当前工作毫无关系的旧仓库),装配期还会按「同名对齐 / 白名单首位」把存量记忆整体搬过去。后果是各工程的记忆被逐条搬进同一棵树 —— 用户侧表现为「某个工程莫名其妙攒了一千条,自己真正在用的工程只有几条」。1.2.0 起记忆永远留在它自己的作用域:名单外目录至多让看板多出一个工程,绝不再把 A 工程的记忆塞进 B 工程;装配期的自动归并一并移除(db.mergeOrphanScopes只保留给显式修复调用)。看板默认落点也从「记忆最多的工程」改成「最近活跃的工程」,避免一打开就撞进历史错账树。 - 无感静默沉淀:基于 DSH 官方
session/event契约监听turn/end(completed) 事件,静默后台提炼关键工程结论、用户偏好与决策规则,不抢占推理上下文。 - 全局一级视口:与 DSH 内置「SSH / 任务看板」同级,常驻左侧导航栏核心区,一键切换主视口并支持
‹ 返回会话快捷回退。 - 多工程记忆选择(按 DSH 工作区对齐):顶部下拉框列出所有有记忆数据的工程,并把不可读的
repo:<hash>反解为可读工程名,同时标注所属工作区(工程名 [工作区名] (N条)),切换即换树。归属判定对存量数据友好:同一目录的历史 hash 变体(盘符大小写、正/反斜杠写法差异)全部认账,工程名与某个合法工作区同名的存量工程也自动对齐到该工作区。 - 有记忆的工程绝不隐藏 / 绝不剔除(硬性铁律):工作区白名单只用来清理没有任何节点的空壳登记(例如以用户主目录启动而临时生成的空工程、宿主已删除的历史废弃目录)。只要某个工程在库里还存着节点,无论它的
scope是否出现在~/.dsh/storages/workspace.json的名单里,都照常保留并展示 —— 白名单永远不会成为「隐藏或删除用户记忆」的理由。 - 零记忆工程自动清理 / 工程名全局唯一:没有任何记忆文件的工程(含遗留的空目录骨架)会被自动删除,仅豁免宿主当前正在打开的合法工作区 —— 它零记忆时也常驻下拉框(显示为
工程名 (0)),选中即进入「暂无记忆,点击上方「+ 新建记忆」开始沉淀」的空状态,作为「当前工程就绪、可随时沉淀」的心智锚点;同名工程(不同路径的同名仓库、目录改名遗留的旧作用域)自动追加scope短标识收敛为唯一名,手工重命名撞名时明确报错。 - 作用域读取容错:请求一个不存在或已失效的工程时,
GET /api/nodes回 HTTP 200 + 空记忆树 + 可读提示(而不是 404),看板按空态渲染;下拉框在任何状态下都可以唤起,且选中的工程若不在当前清单里会自动回退到最近活跃的工程(1.2.0 起;旧口径是「第一个有记忆的工程」,会永远落进条数最多的历史错账树),不会出现「暂无工程记忆 + 下拉点不开」的死锁。 - 树状图谱视图:与「📋 目录列表」一键互切的横向 / 竖向多叉树(工具条按钮一键反向、选择本地记忆),SVG 贝塞尔连线、拖拽平移与滚轮缩放、一键居中;检索时命中节点青色发光、整条父级路径连线加粗、未命中节点淡化,点击叶子直接唤起 Markdown 详情抽屉。默认横向:「同层兄弟」沿纵向铺开,每张卡只吃
52+22px(竖向要208+22px),真实记忆树这种「宽而浅」的形状因此能放大 1.5~1.8 倍(实测同一作用域 25%→42%、37%→68%、49%→89%);「一键居中」另有可读保底(0.6)——装不下整棵树时不再缩成 25% 的一团灰,而是把根节点钉在左上角、由用户拖拽浏览局部。 - 主题自适应穿透:采用透明通道透传 DSH 官方主题底色,深色/浅色皮肤实时自适应无缝融合。
- Markdown 详情抽屉:记忆卡片列表仅展示关键简介,点击从右侧平滑滑出详情抽屉,支持安全沙箱渲染的完整 Markdown 阅读与一键复制。
- SQLite FTS5 全文检索:底层持久化采用 SQLite FTS5 分词索引,支持毫秒级全文精确检索。
- 受控删除(Agent 侧):新增
tlmemory_delete工具 —— 沉淀错了的记忆模型可以直接删掉,但必须显式confirm: true、只允许删叶子(目录级联删留给看板)、默认只能删当前工程作用域(删 global 或其它工程需显式allow_cross_scope: true);tlmemory_query的每条结果现已附带节点id,模型据此精确定位,同名多条会返回候选而不会猜着删。 - 反思提炼容错:模型先输出推理散文、或被
maxTokens截断时不再整轮丢弃 —— 字符串感知的平衡扫描定位真正的 JSON 收尾、截断处回退到最后一个完整条目并补齐闭合括号,裸数组 / 单条对象 / 散文夹多个对象都能归一;解析彻底失败会同时写宿主logger.warn与~/.dsh/tlmemory-extract.log,不再无声吞掉。 - 单一宿主自洽:通过 Cordis 补丁由 DSH Web 宿主自动托管 HTTP 服务(端口 4890),无需手动运行单独后台守护进程。
📦 安装方式
本包是 DSH 原生 bundle 形态:package.json 的 dsh.bundle.patch 指向包内自带的
cordis.patch.yml。被列进 profile 的 dsh.profile.bundles 即自动激活,不需要再往
profile 的 cordis.patch.yml 写任何挂载条目。
为什么必须是 bundle(0.7.0 的修复):桌面端(Electron carrier)与
@deepseek-ai/dsh-plugin-manager只认dsh.bundle.patch这一条激活通道 ——
安装时会以not-a-bundle(<包名> declares no dsh.bundle)直接拒绝,
归并时也只会把它当成「普通依赖」而不写进 dsh.profile.bundles。
0.6.x 及更早版本只声明 dsh.client,在桌面端根本无法被挂载。
为什么换掉 better-sqlite3(0.8.0 的修复):桌面宿主是用 Electron 自带的 Node
跑起来的(process.execPath+ELECTRON_RUN_AS_NODE=1,实测 Node v24.18.1 /
NODE_MODULE_VERSION 149),而 pnpm 是在系统 Node(v24.12.0 / ABI 137)下编译
原生模块的 —— 于是宿主挂载插件时 dlopen 直接失败:
`was compiled against a different Node.js version using NODE_MODULE_VERSION 137.
This version of Node.js requires NODE_MODULE_VERSION 149.`;
插件激活不了、4890 端口没人监听、看板一直显示「服务未启动」。
0.8.0 起持久层改用 Node 内置的 node:sqlite(零原生依赖,跨 Node / Electron 稳定),
因此 运行需 Node >= 22.13(node:sqlite 自 v22.13.0 / v23.4.0 起不再需要
--experimental-sqlite),安装时也不再需要allowBuilds放行原生模块。
方式一:一条命令安装并自动挂载(推荐)
dsh plugin --profile web add dsh-plugin-tlmemory
dsh plugin add 会自动完成:装依赖 → 把包名归并进 dsh.profile.bundles(幂等)。
因为是 bundle 形态,不会再往 cordis.patch.yml 追加挂载条目;0.8.0 起包内已无原生
模块,安装过程不会再有 Ignored build scripts 需要放行。
另外 dsh plugin --profile web list 可查看挂载状态,dsh plugin --profile web remove <包名>
可一键卸载。
桌面端(DSH 桌面应用):profile desktop 由 Electron 应用独占管理,
dsh --profile desktop …与dsh plugin --profile desktop …都会被拒绝
(error: profile "desktop" is managed exclusively by the Electron application)。
请直接在桌面端的插件管理界面安装 / 启用 dsh-plugin-tlmemory —— 它同样只按
dsh.bundle 归类,因此 0.7.0 起可以被正常识别为可激活的插件层。
桌面端看板仍显示「服务未启动」?(0.8.1 的修复):桌面端主窗口文档来自
dsh-app://app/…(Electron 自有协议),渲染层 Origin 是dsh-app://app,属非回环 HTTP。
0.8.0 及更早的 CORS 白名单只回写 127.0.0.1 / localhost,于是外壳对看板的跨源探针被
CORS policy 拦掉、读不到响应,被误判成离线 —— 但宿主侧其实已经起来了(可自行验证:
curl http://127.0.0.1:4890/api/health返回{"ok":true,"service":"tlmemory"})。
0.8.1 起白名单一并放行dsh-app:协议;null/file:/ 其它自定义协议仍然拒绝。
升级后请重启桌面应用。
记忆归属修复(0.8.2):三处写入路径的行为变更 ——
1. 宿主工作区白名单改为实时读取(旧版在插件装配那
…(truncated)
以上内容来自 npm 上的 README,我们只做格式化,不改内容。
版本
npm 上共发布:32,最后更新 2026-11-02