Repository navigation
Releases
v1.3.4 — 预算不再被默认值顶掉
v1.3.4 — 预算不再被默认值顶掉
这一版修一件真踩过的「配置骗人」,外加第一次发到 npm
① 「库配置」那一档从来没起过作用 —— 因为解析链中间那层的 schema 默认值把它顶掉了
注入预算是逐层覆盖的:config.maxBytes(插件配置)→ memory.config.json#injectBudget(库配置)→ 3072。
但插件配置的 schema 上写着 maxBytes: z.number().default(3072) —— 于是"没配插件、配了库"这种最常见的情形下,
中间那一档永远是 3072,库配置那一档是死配置:写 8000 不生效,写 1000 也不生效,界面上还看不出任何异常。
现在预算只有一个解析函数(bin/mem.mjs 的 resolveBudget()),插件注入 / 面板显示 / CLI 三个入口共用同一份:
- schema 上的默认值去掉了 —— "没设" 与 "设成 3072" 不再是同一件事,未设才会往下走;
cordis.patch.yml(本包自带的那份)不再写maxBytes—— 装上来的插件默认就把决定权交给记忆库自己;- 面板那条只读路由只回显真正被显式配置的值,不再把默认值当"你配的"显示出来;
- 顺带修了一条测试保真度问题:插件测试过去直接
Config({...}),绕过了内核的 schema 归一化,
所以"默认值顶掉下层"这类 bug 在测试里根本看不见;现在补了dshConfig()走同一套归一化 —— 十有八九
就是它让这个 bug 活了这么久。
改库配置的效果现在一句话能验:memory/config.json 里写 injectBudget: 8000,重启后面板上显示的就是 8000
(以前永远是 3072,除非你去改插件配置)。
② 第一次发到 npm(dsh-memory-delta)
以前发版只走 GitHub / Gitee / 插件市场;npm 那一步卡在登录上,真因是**当时用的浏览器(Edge)**登不进 npm,
换 Firefox 就好了。这一版补上:
npm i -g dsh-memory-delta # 装的是 mem 这个 CLI(零依赖,同一份记忆库与检索实现)
cd ~/.dsh && dsh plugin --profile web add dsh-memory-delta # DSH 里也可以直接按包名装发布走 npm 的 Trusted Publishing(OIDC):仓库里不放任何 token,GitHub Actions 在 Release 发布时
凭 id-token 换短期凭据,并自动带上 provenance 证明(仓库里新增 .github/workflows/publish-npm.yml,
版本已在 npm 上时自动跳过,避免重跑变红)。
数字
- 测试 1293 断言全绿:run 295 / planner 53 / search 56 / due 93 / session-format 17 /
peer-range 28 / hook 66 / plugin 327(+10,全部围绕预算解析链与那条保真度归一化)/ client 358
(仓内不装 semver 时 peer-range 的语义断言会安静 SKIP 成 5 条,带真 semver 跑就是 28 条) - 两套内核真机预检各 21/21(社区版 0.1.7-rc.2、官方版 0.2.0-rc.2 各自从安装目录解出来的真实内核)
- 安装包
dsh-memory-delta-1.3.4.tgz= 190,528 字节(17 个文件、无图片;
npm shasumb5ad2dcca37ecab89a5f754aabeb0d0844c6c70e/ SHA-256173dcd66f39a0e87cb89b872795e4202b82a881917219d103679116ef507297f) - 文档:README(中英)补 npm 安装路径;
docs/faq.md/docs/faq.zh.md改掉"预算怎么来的"那几条
(以前描述的默认值行为就是错的)
安装 / 升级
cd ~/.dsh && dsh plugin --profile web add "github:lpf20200901/dsh-memory-delta#v1.3.4"
# 或者npm 包名:dsh plugin --profile web add dsh-memory-delta升级后要完全重启 DSH(宿主侧改动与客户端 bundle 都在重启后生效)。
如果你以前在插件配置里手写过
maxBytes,它会继续优先;想改回"听库配置的"就把那一行删掉。
兼容性:支持 DSH 0.1.7-rc.1 及以后(覆盖 0.1、0.2 线);0.3 线暂不支持,装上会被内核拒载并给出提示。
English
One real "the config lies to you" fix, plus the first npm release.
(1) The "store config" tier never had any effect — a schema default sat in the middle of the resolution chain.
Budget resolution is layered: config.maxBytes (plugin config) → memory.config.json#injectBudget (store config)
→ 3072. But the plugin's schema declared maxBytes: z.number().default(3072), so in the most common setup —
no plugin config, store config set — the middle tier was permanently 3072 and the store config was dead:
setting 8000 or 1000 changed nothing, with no visible symptom.
All three entry points (plugin injection, panel display, CLI) now share one resolver, resolveBudget() in
bin/mem.mjs. The schema default is gone ("unset" and "explicitly 3072" are no longer the same thing),
the bundled cordis.patch.yml no longer writes maxBytes at all, and the read-only panel route only shows
values that were genuinely configured. The plugin tests also gained dshConfig() so they go through the kernel's
real schema normalisation instead of bypassing it — which is very likely why this bug survived.
(2) First release on npm (dsh-memory-delta): npm i -g dsh-memory-delta installs the zero-dependency mem
CLI, and DSH can install the plugin by package name. Publishing uses npm Trusted Publishing (OIDC) — no token
in the repo, a short-lived credential minted from id-token when a GitHub Release is published, with automatic
provenance (.github/workflows/publish-npm.yml, which skips when the version is already on the registry).
1293 assertions green (plugin 317 → 327), preflight 21/21 on both kernel generations,
tarball dsh-memory-delta-1.3.4.tgz = 190,528 bytes, 17 files, no images.
v1.3.3 — 重启不再重灌一遍记忆
v1.3.3 — 重启不再重灌一遍记忆
这一版修的是三件「界面上看不出来」的事
① 差分状态的侧车从不落盘 —— 于是每次重启都把整份常驻记忆重灌一遍
不是"影响很小"的老毛病,而是取值通道选窄了:旧实现只认 process.env.DSH_HOME,拿不到就当"脱离 DSH 单独跑"
降级成不持久化。但 $DSH_HOME 是内核的覆盖变量 —— 官方桌面版的 home 就是默认的 ~/.dsh,
启动器没有任何理由设它,于是 stateDir() 返回 null、saveState 直接 return。
(社区版写得出来,是因为它的 home 是 %APPDATA%\dsh-desktop\harness、非默认,启动器把变量设上了 ——
所以以前的现象是"两个版本行为不一样"。)
现在按顺序解析:config.stateDir(新配置项,支持 ~)→ $DSH_MEMORY_DELTA_STATE_DIR(测试用)→
内核服务 ctx.dshHomePath(app boot 用 ctx.provide 提供的官方通道;内核自带 profile 就写
!!js dshHomePath('storages'))→ $DSH_HOME → ~/.dsh。永远给得出目录,那条"null = 不持久化"的
静默分支删掉了;服务那层做了防御(抛异常 / 不是函数 / 返回值不像我们问的那三段时安静退回下一层)。
顺带纠正一条我们自己记错的因果:上一个版本把"官方版不加载"归因于 peer 范围。不对 ——
两代内核的启动期预检都传 { includePrerelease: true },旧范围在真预检下其实两边都能过;
那次是测错了(搜了压缩的会话文件)。逐条线写范围仍然保留,但性质是防御性收窄。
真正该记住的差别是:0.1.7 线把 peer 当建议(只告警),0.2.x 线不匹配会整行 disabled ——
内核抬 minor 之前要复核 peer 范围。
② 客户端有两处"兜底路径自己会炸"(代码审查抓到的)
- 动态包的 ctx 门面读没暴露的属性是抛异常、不是返回
undefined(dynamic ctx does not expose "inject"),
而typeof ctx?.inject那行在 try 之外 —— 于是"门面上没有 inject 就退回轮询"这段兜底,
恰好会在门面上把apply弄炸; - 兜底轮询的定时器没挂在 fiber 上:那 12 秒窗口里条目被热更/禁用时,注册会在已释放的 fiber 上
调ctx.effect抛INACTIVE_EFFECT,而且是在 timer 回调里抛 = 没人接。
两处都收口了(读取进 try、轮询整段进 try,失败安静收工),并补了会红的对照组。
③ 一处文档级纠正:test/peer-range-tests.mjs 里"插件正是这样在 0.2.0 内核上被静默禁用"这句话
写反了因果,已改正(见 ①末尾),并新增两条"真相对照"断言防止后人再推错。
数字
- 测试 1283 断言全绿:run 295 / planner 53 / search 56 / due 93 / session-format 17 /
peer-range 28 / hook 66 / plugin 317 / client 358 - 对照组(退回去新断言必红):解析器退回旧写法 → 7 条红(含"没有
$DSH_HOME时侧车也要落盘"那条端到端断言);
客户端两处保护退回旧写法 → 3 条红(门面抛异常 / 没排重试 /INACTIVE_EFFECT) - 真机「写入 → 回读」闭环(内核 0.2.0-rc.2):重启后状态文件按会话 id 落盘
(<home>/storages/dsh-memory-delta/inject-state/<会话>.json,196 字节),
再重启一次后loadState读到基线 → 零注入,注入次数停在 3(不再重灌全量) - 两套内核真机预检各 21/21(社区版 0.1.7-rc.2、官方版 0.2.0-rc.2 各自的内核)
- 安装包
dsh-memory-delta-1.3.3.tgz= 187,317 字节(17 个文件、无图片;
npm shasum664edfd2b0fe38c68e594452e73ed0f19afd8c9d/ SHA-25623ce8844ee38e691b5e56546539a13af583edd5f4d385c5fbc13166877d5adaa) - 文档:
docs/design.md§九 改写成"已修 + 真因",新增 §十(本次审查的 P1/P2 与那条被纠正的因果)
安装 / 升级
cd ~/.dsh && dsh plugin --profile web add "github:lpf20200901/dsh-memory-delta#v1.3.3"升级后要完全重启 DSH(宿主侧改动与客户端 bundle 都在重启后生效)。
差分状态文件是新增的:升级后第一次重启仍会注入一次全量(还没有基线),从第二次起才零注入 —— 这是设计如此。
English
Three fixes, none of them visible in the UI.
(1) The differential state side-car was never written — so every restart re-injected the whole standing memory.
The old code resolved persistence through process.env.DSH_HOME alone and degraded to "no persistence" when it was
missing. But $DSH_HOME is the kernel's override variable: the official desktop build's home is the default
~/.dsh, so its launcher never sets it — stateDir() returned null and saveState returned immediately.
(The community build could write because its home is %APPDATA%\dsh-desktop\harness, i.e. non-default.)
It now resolves config.stateDir → $DSH_MEMORY_DELTA_STATE_DIR → the kernel's ctx.dshHomePath service →
$DSH_HOME → ~/.dsh, always yields a directory, and defensively falls through when the service is unusable.
Also corrected: the previous release blamed the peer range for "the official build does not load".
Both kernel generations pass { includePrerelease: true }, so the old range actually satisfied the real
preflight — that diagnosis was a measurement error on my side. The per-line range stays as a defensive narrowing;
the difference that matters is that the 0.1.7 line treats peers as advisory while 0.2.x disables the whole row.
(2) Two client fallbacks could throw on their own fallback path (found by code review): reading ctx.inject on a
dynamic-package façade throws instead of returning undefined, and that read sat outside the try; and the
bounded-poll timer was not tied to the fiber, so a registration after disposal threw INACTIVE_EFFECT from inside a
timer callback. Both are contained now, with red control groups.
1283 assertions green (peer-range 21 → 28, plugin +14, client +8), three control groups verified,
real-machine write→read-back closed on kernel 0.2.0-rc.2 (restart #2 injects nothing), preflight 21/21 on both
kernels. Tarball dsh-memory-delta-1.3.3.tgz = 187,317 bytes, 17 files, no images.
v1.3.2 — 页签不会再悄悄消失
v1.3.2 — 页签不会再悄悄消失
这一版修的是两处「静默失败」——界面上都看不出任何异常
① 侧边栏「记忆」页签不出现(装了 better-sidebar,页签就是没有)
v1.3.1 特意去掉了对 better-sidebar 的硬依赖 —— 硬依赖会在没装它的机器上把整个界面拖进安全模式。
副作用是那个服务可能晚于我们就绪,而旧代码只查一次、查不到就永久放弃注册。
现在改成等服务:优先用 cordis 的服务等待原语 ctx.inject(['betterSidebar'], cb)(better-sidebar 自己的
接入文档也是这么做的),门面上没有 inject 时退回有界轮询(40 × 300ms,最多约 12 秒)。
顺带给页签补上 description —— better-sidebar 0.19+ 起宿主不再给兜底描述,不填就只剩标题。
② 在别的内核上被启动期预检整行禁用
peer 原来写成 >=0.1.2-rc.1 <0.3.0,它的 tuple 是 0.1.2;而内核版本是 0.1.7-rc.2 / 0.2.0-rc.2 这种
预发布版本。按 semver 的 tuple 规则(带预发布的版本只被「同一 tuple 且自带预发布标签」的比较器接纳),
在没有强制开 includePrerelease 的检查器下,三种内核全判 false → 整行被禁用,
而它只在 stderr 留一行 disabling profile plugin …:界面上完全看不出来。
现在逐条线写:^0.1.7-rc.1 || ^0.2.0-rc.1 —— includePrerelease 开与不开都通过。
数字
- 测试 1254 断言全绿:run 295 / planner 53 / search 56 / due 93 / session-format 17 / peer-range 21 / hook 66 / plugin 303 / client 350
- 新增
test/peer-range-tests.mjs(21 条):静态护栏要求"每条支持的内核线都在范围里有自己的预发布比较器"
(这条规则静态就能抓住上面的 ②)+ 语义在includePrerelease两种模式下都跑 + 两条把规则本身钉住的反向对照 - 客户端测试 +9 条回归:服务未到时先不注册但要排重试、轮询补注册、
inject补注册到派生子 ctx、description必填 - 安装包
dsh-memory-delta-1.3.2.tgz= 181,618 字节(17 个文件,无图片;npm pack的 shasum2e8e3e73a94a394316fd003a89202192e11c3868) - 真机验证:社区版内核
0.1.7-rc.2与官方版内核0.2.0-rc.2上都验过「加载 + 注入」;
官方版那边还用从它自己app.asar里抠出来的真内核跑了完整预检(21/21) - 文档:
docs/design.md新增「九、已知问题」—— 差分状态的侧车自 09-30 后未落盘(低优先级:
进程内差分照常,影响仅是重启后重灌一次全量记忆)
安装 / 升级
cd ~/.dsh && dsh plugin --profile web add "github:lpf20200901/dsh-memory-delta#v1.3.2"升级后要完全重启 DSH(客户端 bundle 与宿主侧改动都在重启后生效)。
English
Two silent failures fixed — neither was visible in the UI.
(1) The sidebar 「记忆」 tab was never registered when better-sidebar's service arrived after our client had
initialised. v1.3.1 dropped the hard dependency on purpose (on a machine without better-sidebar a hard dependency
dragged the whole UI into safe mode), which makes a late service the normal case — while the old code checked once and
gave up forever. The client now waits: ctx.inject(['betterSidebar'], cb) first (the pattern better-sidebar's own
integration guide recommends), with a bounded poll (40 × 300 ms, ~12 s max) as fallback, and it now declares the
description that better-sidebar 0.19+ expects on a tab descriptor (the host no longer substitutes a fallback).
(2) The row was disabled by the boot-time compatibility preflight on other kernels. The peer range
>=0.1.2-rc.1 <0.3.0 has the tuple 0.1.2, while the hosts are prereleases of 0.1.7 / 0.2.0. Under semver's
tuple rule a checker that does not force includePrerelease rejects all three hosts, and the row is disabled with
nothing but a disabling profile plugin … line on stderr. It is now written per line —
^0.1.7-rc.1 || ^0.2.0-rc.1 — which passes with includePrerelease both on and off.
1254 assertions green (new test/peer-range-tests.mjs, 21, plus 9 client regressions). Verified on kernels
0.1.7-rc.2 and 0.2.0-rc.2, the latter with its own real kernel extracted from app.asar (full preflight 21/21).
Tarball dsh-memory-delta-1.3.2.tgz = 181,618 bytes, 17 files, no images.
v1.3.1 — 不再拖垮会话了
v1.3.1 — 不再拖垮会话了
一句话:这是一版纯修复,而且是"必须马上升"的那种。 装了 v1.3.0 及更早版本的
DSH ≥ 0.1.7(社区版 Desktop 0.10.0)会让每个会话第一轮就失败;更早的内核上则是
含该记录的会话永久打不开。仓库文件格式没变,老库零迁移。
为什么是"紧急"
问题不在记忆库,而在我们写进 DSH 会话日志的那个字段(消息的 source.kind)——
它被内核的会话格式逐代收紧,而本插件以前写的形态两代都不合法:
| 内核 | 我们当时写的 | 后果 |
|---|---|---|
| ≤ 0.1.6(格式 v3 及以前) | {kind:'memory'}(自定义 kind) |
v2→v3 迁移只认 15 种来源,遇到它就抛 cannot safely transform unclassified message source 并拒绝整次迁移(源日志按设计不动)→ 含这条记录的会话永久打不开(本机 37 条会话日志里 14 条中招) |
| ≥ 0.1.7(格式 v4) | {kind:'plugin', plugin:'dsh-memory-delta'}(v3 时代的"插件包装") |
v4 规则反过来:kind 必须是生产者自有 kind(非空、且不能是 plugin)。编码器每次写日志都抛 format v4 message requires a producer-owned source kind → 每个会话第一轮「本轮运行失败」,新建会话文件只剩 196 字节表头,标题生成与缓存一起失灵 |
同一个字段,两代格式的要求正好相反 —— 所以 v1.3.1 写的是两代都认的形态
{kind:'plugin:dsh-memory-delta'}(它正好等于 v4 迁移给第三方插件分配的 kind)。
这一版修了什么
- 会话格式 v4 兼容(P0):
memorySource()→{kind:'plugin:dsh-memory-delta'}。
写出去和读回来是同一个形态,重启后仍认得出自己发过的消息。 - 读回兼容四种历史形态:现行形态 / v3 时代的
plugin包装 / 裸包名 / 更早的'memory'。
老会话日志里这些都还在 —— 认不出来会让"提醒去重、清理排队"失准。 - 差分状态搬出消息:以前把每条的
{id,hash}塞在消息的source.entries里,而 v0 白名单
只允许 plugin source 带kind/plugin/form/sections/summary—— 又是一种"整条会话打不开"。
现在状态在侧车文件$DSH_HOME/storages/dsh-memory-delta/inject-state/<会话id>.json,
而且只在那条消息真的进了上下文之后才落盘(排队后被清掉的不记账,否则状态虚增、
模型会永久少看一批记忆)。 - 不再硬依赖 better-sidebar:客户端原来声明
dsh.client.inject = ['dsh-better-sidebar'],
而 DSH 的客户端声明没有"可选依赖"写法。没装 better-sidebar 的机器上,这个条目会永远
pending(waiting for service: betterSidebar),web boot 把 pending 当失败 →
整个 Web 界面起不来、直接进安全模式(实测踩到)。现在改成运行时可选查找:
拿得到就注册「记忆」页签,拿不到只跳过页签 —— 记忆工具与注入照常工作。 - 新增契约测试套件(
test/session-format-tests.mjs,17 条):直接import内核的
@deepseek-ai/dsh-session-format-v3-to-v4,用它自己的assertV4RowAdmission验我们写出去的
来源 —— 现行形态必须过、退役的plugin包装必须被拒、四种历史读回形态都认得出,
另外静态护栏要求全仓只有memorySource()一个构造点(不许别处手写kind)。
找不到内核模块时打印 SKIP 跳过(别的机器/CI 上没有 DSH 安装是正常的)。 - 顺手两个小修:取回(restore)时清掉
category(常驻条目不该带着"退场理由");
面板条目行只显示文件名(原来显示路径尾巴,会一路带进公开截图)。
数字
- 测试:1224 断言全绿(run 295 / planner 53 / search 56 / due 93 / session-format 17 /
hook 66 / plugin 303 / client 341),npm test零依赖 - 安装包:
dsh-memory-delta-1.3.1.tgz= 179,622 字节(17 个文件,无图片、无调试脚本) - 本机真机验证:社区版 DSH Desktop 0.10.0(内核 0.1.7-rc.2)里持续正常收发,
其会话日志(session.v4.jsonl.zstd)里本插件写的来源实测为plugin:dsh-memory-delta - 本机 14 条被写坏的历史会话日志已就地修复(副本与原件都留有备份,可逐条回滚)
兼容性
- 记忆库文件格式没变:老库不用迁移,CLI 参数没变,装上即用
- 老会话日志里的历史来源形态都能读回来(提醒去重、清理排队照旧)
- 侧车状态文件是新的(
$DSH_HOME/storages/dsh-memory-delta/);拿不到$DSH_HOME时
降级为"不持久化"(进程内照常差分,重启后重灌一次全量,正确性不受影响)
装 / 升级
# 市场里更新,或直接:
cd ~/.dsh && dsh plugin --profile web add "github:lpf20200901/dsh-memory-delta#v1.3.1"升级后需要重启 DSH(插件是复制安装,宿主侧改动重启才生效)。
English summary
v1.3.1 is an urgent fix. Earlier versions wrote an illegal source.kind into DSH session logs:
on DSH ≤ 0.1.6 the custom 'memory' kind made the v2→v3 migration refuse the whole session
(14 local logs became permanently unopenable); on DSH ≥ 0.1.7 (format v4) the v3-era
{kind:'plugin', …} wrapper makes the encoder throw on every write, so every session failed on
its first turn. v1.3.1 writes {kind:'plugin:dsh-memory-delta'}, which both generations accept,
moves the diff state out of the message into a side-car file, drops the hard dependency on
better-sidebar (its absence used to fail web boot into safe mode), and adds a contract test suite
(17 assertions) that checks our written source against the kernel's own assertV4RowAdmission.
The memory-store file format is unchanged: no migration, just install and restart DSH.
v1.3.0 — 归档终于有了秩序
dsh-memory-delta v1.3.0 — 归档区重做:三个分类,点开主题才见条目
一句话:旧结论不再是一坨平铺的列表了 —— 「已归档」按"为什么退场"分成三类,
分类下面是主题行,点开主题才列出条目;顺手修掉四个真问题。
这一版是什么
v1.2.0 之后攒了 16 个提交,主体是把「已归档」从一个平铺列表重做成三层结构,
外加一批"看起来能用、其实会误人"的毛病。库与写入格式完全兼容,装上就能用,
现有条目不用迁移。
「已归档」现在是三层
已归档(archive)
已蒸馏 (20) ← 分类:为什么退场。三个是常驻的,空的也显示 0 条
DSH 插件开发 (16) ← 主题行:点开才列出条目
环境与沙箱 (3)
已废弃 (18)
· gitignore-… ← 没写主题的条目直接列在分类下,一眼能看到
开源与发布 (5)
暂时不用 (0) ← 暂时用不上、但可能再启用的那些
三个默认分类,各自的存在理由就写在分组头上:
| 分类 | 什么意思 | 谁落它 |
|---|---|---|
已蒸馏 |
结论已经搬进文档、或被新版本顶上 —— 留着只为留个出处 | 自动:mem supersede / promote --supersedes / archive --superseded-by |
已废弃 |
彻底没用了、不会再启用 | 手动归档的默认值 |
暂时不用 |
这个工作区暂时用不上,但换个业务/项目可能又要 | 手动归档时选它 |
- 归档时可以自建分类:确认条里给两个预设(
已废弃/暂时不用),输入框直接写一个新的即可,
批量归档同样能选。 - 想再用就是条目上的「取回」→「提升」(回候选层,再确认一次才生效)。
- 「暂时不用」与「已废弃」的差别只在"还会不会回来":两者
status都是expired,
靠category区分 —— 所以它不牵动校验 / 取代链 / 取回那三处。
主题回到一级
主题名现在就是一个字符串(同名即一组)。之前试过"名字里写 父/子 就分两级",
在真实数据上反复出问题(父分组的条数算谁、收起收谁),整套父子逻辑已删掉。
名字里真写了 / 也只是普通字符,老数据零迁移。
修掉的四个真问题
- 整类从界面上消失:以前"分类下没有直接条目就不出分类头",而真实库 40 条归档里 39 条都写了主题
—— 「已蒸馏」连分组头都没有,看着像这一类没了。现在分类头无条件出现,条数报整类。 - 「已在用」空了,两份规范也跟着消失:
全局规范/工作区规范是跟条目列表并列的内容,
却被空态分支一起换掉了。现在空态只替换列表那一部分。 - 归档分类的候选混进不该选的东西:下拉里曾经出现
已蒸馏(那是自动归类,选了等于"自己声称蒸馏过了")、
已过期(旧名,会裂出第二个近义分组)、其它退场(内部兜底值)。现在只给已废弃/暂时不用
加上你自己建过的分类。 - 界面上的小毛病:归档确认条一片红(现在红色只留给不可逆的删除)、选了哪个分类看不出来(现在有 ✓)、
子主题标题重复父名("已过期 下面一堆 已过期>…")。
兼容性
- 老库不用迁移:v1.2.x 写进文件的
category: 已过期在读和写两边都归一到已废弃
(一个别名表 + 一条测试钉住),所以不会裂出两个近义分组;想物理改名可用
mem set <id> --category "已废弃"。 - 写入格式、注入机制、CLI 参数没有破坏性变化;归档条目照旧可搜索、可取回。
English summary
v1.3.0 reworks the archived stage. It is now three levels — archived → category → topic → entries:
three default categories always show (已蒸馏 distilled into the docs, set automatically when an entry is
superseded; 已废弃 useless for good; 暂时不用 not needed right now but likely wanted again later), you can
type a new one while archiving (single or batch), topic rows are collapsed — click one to reveal its
entries, and entries without a topic sit right under the category. Topics are back to one level
(the short-lived parent/child splitting is gone; a / in a name is just a character).
Also fixed: a category could vanish entirely when it had no direct entries; the two rule files
(global / workspace AGENTS.md) disappeared whenever the live stage was empty; the category picker offered
已蒸馏 / 已过期 / 其它退场, none of which belong in a human-picked list. Existing stores need no
migration — the old default name 已过期 is aliased to 已废弃 on read and write.
已验证
- 测试 1191 条断言全绿(CLI/数据层 291 · 检索 56 · 到期 93 · 注入钩子 63 · 计划器 46 · 面板客户端 340 · 宿主插件 302)
- 真机预检 21/21(在真实 DSH profile 的
node_modules里跑,验证真实defineTool/schemastery接受我们的定义) - 本版每个改动都做了对照组:退回旧实现,对应断言当场变红(共 6 组 —— 分类消失 8 条、主题两级 10 条、
第三个默认分类 9 条、旧名别名 1 条、空态吃掉规范 3 条、分类候选 4 条) - 真机渲染核对:用活的宿主状态路由 + 当版客户端渲染出真实层级,确认"三个分类常驻、主题行默认收起、条目缩进正确"
安装 / 升级
# 装进 DSH(web profile)
dsh plugin --profile web add "github:lpf20200901/dsh-memory-delta#v1.3.0"
# 或只想要 CLI(零依赖,不装 DSH 也能用同一套库)
npm i -g github:lpf20200901/dsh-memory-delta#v1.3.0
mem --help升级已有安装:把上面的 add 换成 update 即可;库文件在 <工作区>/memory,不受升级影响。
装完重启 DSH(宿主侧路由与面板都要重新加载)。
v1.2.0 — 不崩了,也更省了
dsh-memory-delta v1.2.0
这一版是**"把真机上会咬人的问题清干净 + 让每轮更便宜":一个让模型检索必然报错**的缺陷、两个能把宿主进程带走的缺陷、一类让手工维护的库直接崩掉/静默丢数据的解析问题,外加把注入正文瘦了 26%。
这一版修掉的(真机上能感觉到的)
memory_search在真机上必然失败(v1.1.0 也中招):工具声明的output.schema漏了tags/date/file,而 DSH 会按additionalProperties:false校验工具返回值 —— 于是只要有任何命中,整次调用就是一条错误。顺带发现同一个工具的第二个缺陷:只有render的文本会交给模型,而原来的 render 只回一句Matched N memory entries.—— schema 修好后调用不报错,模型却一条命中都看不到。现在 render 逐条列出[层] id 日期 score+ 片段 + 文件路径。- 两个能把宿主进程带走的缺陷:
moveEntry/removeFile/loadConfig用了process.exit(),而它们会被面板按钮和模型工具间接调用(配置里一个尾逗号就能让"写一条记忆"结束整个 DSH 进程);动作路由的catch里引用了try内的const,于是磁盘一报错、错误处理自己先崩。都改成抛异常,并在路由层加了回归断言。 - 手工维护的 frontmatter 不再被读坏:
tags: dsh-memory(标量)以前读成字符串 →mem recall与模型的memory_search整条 TypeError、mem list --tag静默匹配不到;块序列tags:+- a读成null,写回一次就永久丢掉标签;--topic "123"会变成数字、[生产]会变成数组且搜不到。现在读写两侧成对设计,并加了"往返恒等"测试。 restore留下的悬挂引用:取回条目时只清了自己那一半,对方的supersedes一直挂着它 →validate永远报错且没有任何命令能修。现在两边一起改(与rename同步引用同一条纪律)。mem set --topic不重建index.md:界面显示新主题、索引里还是旧名字,而validate查不出来。现在所有归类都走同一个写入口。
更便宜了(每轮都要花的钱)
- 注入正文瘦 26%(真实库 5924 → 4369 字节,≈1975 → ≈1456 tokens/会话):
- 与 id 一字不差的 key 不再写进正文 ——
createEntry的规则是"有 key 就用 key 当文件名",实测真实库 31 条里 30 条的 key 与 id 完全相同,而 id 已经随消息的结构化source.entries给到模型:正文里那个[key]是第二遍,白占 15% 的预算。 - 单条结论的展示上限从 140 字收到 90 字(超长部分的完整内容留在正文「理由」里 —— 正文不花预算)。
- 与 id 一字不差的 key 不再写进正文 ——
- 检索不再被流水淹没:按层降权(流水/会话索引 ×0.5)+ 行级命中封顶,并在 CLI / 工具 / 面板三处都如实报出"被折叠了多少条"。
面板(侧边栏「记忆」页签)
- 按主题归纳(人指定的
topic,不是模型写的),四个维度 —— 主题(默认)/ 类型 / 标签 / 日期 —— 作用于全部三个阶段(待你确认 / 已在用 / 已归档)。 - 批量操作:逐条勾选 + 一条批量条(归类 / 提升 / 撤回 / 归档 / 取回 / 删除候选);分组头的「选本组 N 条」= 按当前维度成组批量;危险动作走行内确认条;部分失败逐条说明原因。
- 主题改名(全库同名一起改,含归档层)、按主题搜(分组头的「搜这组」)。
- 搜索:同一句话不再重复请求、失败后按回车能重试、乱序响应不会覆盖新结果、被截断时给出条数与下一步。
English summary
v1.2.0 fixes a defect that made memory_search fail on every hit (its declared output schema missed fields, and DSH validates tool results with additionalProperties: false; v1.1.0 is affected too), makes the tool's render actually hand the hits to the model, removes two ways a button or tool call could kill the host process, makes hand-written frontmatter safe to read (tags: foo, YAML block sequences, quoted numerics), repairs dangling supersedes links on restore, and cuts the injected text by 26% by not repeating keys that are identical to ids and clipping each line at 90 chars. The sidebar tab gains human-assigned topics (the default grouping axis), four grouping axes across all three stages, batch operations, topic rename and per-topic search.
已验证
npm test— 1096 断言(CLI 274 / planner 46 / search 56 / due 93 / hook 63 / plugin 300 / client 264),零依赖- 真机预检 21/21(用真实的
@deepseek-ai/*模块复刻 DSH runtime 那一步校验) - 真实记忆库
mem validate:0 问题 / 4 告警 / 退出码 0 - 每一条修复都做过对照组:把修复退回去,对应的新断言必红(宿主进程那条的"红"是测试进程直接被杀,所以断言放在路由层)
安装
# DSH 插件
cd ~/.dsh && dsh plugin --profile web add "github:lpf20200901/dsh-memory-delta#v1.2.0"
# 或只想要 CLI(零依赖)
npm i -g github:lpf20200901/dsh-memory-delta#v1.2.0
mem init && mem new --type fact --conclusion "一句话结论" --key my-first-fact装了插件的工作区可以 /dsh-memory-delta 查看用法与边界。库自带说明书会写进 <工作区>/memory/README.md。
升级提示:本版把单条结论的展示上限从 140 字收到 90 字。若库里已有超长结论,注入里看到的是截断版(完整内容仍在条目文件里);想让模型看到完整的,把结论首行写短、细节挪进「理由」即可 —— 正文不占注入预算。
v1.1.0 — 记忆看得懂了,你也管得动了
v1.1.0 — 记忆看得懂了,你也管得动了
这一版几乎全是侧边栏「记忆」页的改造:以前它只是"列出来",现在它把记忆的流程讲清楚,
而且你能在界面上管 —— 确认、撤回、归档、取回、删除、搜、按三种维度看。
一眼看懂:按流程分组
以前分组按"类型"(事实/决策),光看名字不知道它在流程哪一步。现在按流程阶段分组,每组一句人话:
流程:模型写入 → 待你确认 0 → 已在用 17 → 已归档 0
▾ 待你确认(inbox) 模型想记住的 · 你点头才生效(在那之前不会发给模型)
▾ 已在用 每轮会话自动发给模型 · 这就是"它记住了"的部分
▾ 事实(facts) 踩过的坑 & 绕开的方法
▾ 决策(decisions) 你定下的约定
▾ 已归档(archive) 不再适用 / 被取代 · 不再发给模型,但搜得到、也能取回
顶部还有一条流程条,各阶段有多少条一眼看到。「已在用」里能按三个维度看:
类型(该放哪边)/ 标签(按主题)/ 日期(什么时候记的,带"今天/昨天/周几")。
你管得动:完整的一生都在界面上
| 想干什么 | 怎么做 |
|---|---|
| 确认模型记的东西 | 「待你确认」里点「提升到 facts/ decisions/」 |
| 还没想好、先别用 | 常驻条目点「撤回」(回候选,还能再提升回去) |
| 这条不再适用了 | 点「归档」(进归档,不再发给模型,仍可搜) |
| 归档错了、情况又变了 | 归档条目点「取回」(回候选,再确认一次才生效) |
| 这条根本不算数 | 候选条目点「删除」(只允许删候选 —— 常驻条目直接删就是静默消失) |
危险动作都要点两次(行内确认条),不会手滑。
搜得到:中文连写就行
搜索框直接搜条目和流水,中文不用加空格(沙箱禁管道 就能命中)。
打分逻辑和命令行的 mem recall、以及模型用的 memory_search 是同一套 ——
你搜到的和它搜到的永远一致。
还看得见"规范"
「已在用」里会显示全局规范(~/.dsh/AGENTS.md,DSH 注入、每个工作区都生效)
和工作区规范(AGENTS.md / AGENTS.local.md,可以直接点「编辑」进编辑器)——
以前它们默默生效但界面上完全看不见。
命令行也补齐了
mem promote <id> # 人确认:候选 → 常驻
mem demote <id> # 撤回:常驻 → 候选("先不当真",不改 status,能再 promote 回去)
mem archive <id> # 归档:不再适用又没有替代 → archive/(以前只能靠 supersede 或手改 status)
mem restore <id> # 取回:archive/ → 候选
mem rm <id> # 删除候选(只允许 inbox/)
mem rename <旧> <新> # 安全改名:id + 文件名 + 所有引用一起改顺带修掉的两个真问题:
mem set --status expired只改 frontmatter 不搬文件 → 条目既不显示在「已在用」也不算进「已归档」,
等于从界面上消失。现在有mem archive把它搬到该去的地方。- 没给语义键的条目会拿到
2026-09-17-<截断的结论>.md这种长名(中文截在词中间);
现在给了key就用 key 当文件名,历史遗留名可用mem rename收拾。
超预算说人话了
注入是有字节上限的(这段内容每一轮会话都会发给模型)。以前超了只有一行红字,
现在会告诉你:超了多少、为什么有上限(换算成 token/轮)、不会丢条目、以及怎么办
(归档/撤回、把结论首行写短、或调大 maxBytes)。
安装
- 作为 DSH 插件(推荐):市场里搜
dsh-memory-delta,或按 README 的两步手动装 —— 包放进 profile 的
node_modules/,在cordis.patch.yml里用- insert:挂上。本页附件dsh-memory-delta-1.1.0.tgz
是与本次源码一致的安装包。 - 只当命令行用:clone 之后
node bin/mem.mjs init --root ./memory。
已验证
825 项测试全绿 · 真机预检 15/15 · 面板的每一条流程(提升/撤回/归档/取回/删除/搜索/三维度切换)
都在真实 DSH 会话里点过。
MIT 许可 · 零运行时依赖。
English
Long-term memory for DSH. v1.1.0 is mostly about the
sidebar memory tab: it now tells the story of the memory (candidate → standing → archived), and lets
you manage it there — promote, withdraw, archive, restore, delete a candidate — plus a search box that shares
its scoring with mem recall and the model's memory_search, and three ways to view the standing layer
(type / tag / date). It also surfaces the global and workspace instruction files that used to be invisible,
and explains the injection budget in plain language when it is exceeded.
The CLI gained demote / archive / restore / rm / rename, so a retired conclusion no longer has to be
faked with set --status expired (which left the file where nothing displayed it).
Verified: 825 assertions green, 15/15 real-machine preflight, every panel flow exercised in a live DSH session.
MIT licensed, zero runtime dependencies.
v1.0.0 — 让新会话记住你
给 DSH 装上「长期记忆」:新会话自动带上你之前定下的结论,而且只带变化的部分,不白烧 token。
能干什么
- 新开会话不用再重新交代背景 —— 之前确认过的结论自动带上
- 没变化就一个字都不推(省 token 的关键)
- 模型记的东西先进「收件箱」,你点头了才算数,错的结论不会被一直带下去
- 同一个话题只留一条有效结论,新的必须显式替换旧的
- 可以写"什么时候该复核"(比如
3个月后),到点了提醒你一次 - 忘了在哪说过?按关键词搜,中文不用加空格
- 侧边栏有「记忆」页:记住了什么、占多少 token,一眼看到
- 不想装插件也能用命令行
安装
作为 DSH 插件:把包放进 profile 的 node_modules/,在 cordis.patch.yml 里用 - insert: 挂上(README 有步骤)。
只当命令行用:clone 之后 node bin/mem.mjs init --root ./memory。
已验证
541 项测试全绿 · 真机预检 15/15 · 差分注入 / 检索 / 到期提醒 / 侧边栏页都在真实 DSH 会话里跑通。
MIT 许可 · 零运行时依赖。
English
Long-term memory for DSH: a new session starts with the conclusions you already confirmed — and only
what changed, so nothing is re-sent when nothing changed. The model can only write to an inbox; you
promote a candidate before it becomes a standing fact.
Verified: 541 assertions green, 15/15 real-machine preflight. MIT, zero dependencies.