Skip to content

Releases: Noelune/unified-agent-memory

v0.9.2 — npm 包内容修复(默认启用参与度层)

Choose a tag to compare

@Noelune Noelune released this 31 Oct 11:48

Fixed — npm 包内容(0.9.1 的 npm 产物不可用,请直接装本版)

  • 0.9.1 的 npm tarball 缺失整个分层核心(app/domain/infra/surface 与钩子集成文件不在包里),装出来的包一跑就 ImportError。根因:files 白名单仍是 0.8.8 时代的顶层匹配,且发布流程在打包修复之前的工作树上执行。0.9.2 从修正后的白名单发布,发布前对注册表产物做了内容核验(58 个分层文件 + 钩子文件 + 0 个字节码)与真实导入冒烟。
  • files 白名单改为整棵 core/unified_memory(否定模式排除字节码——.npmignore 对 files 白名单目录实测不生效),并补上 integrations/hooks 与 integrations/claude/hooks。
  • 0.9.1 的 git 仓库内容本身没有问题;只有 npm 产物坏。已装 0.9.1 的用户请升级到本版。

Verified

注册表 tarball 内容核验 + 解包导入冒烟通过;npm run check 完整链 exit 0(typecheck → drift → sync → JS 886 → Python 534 → setup 32 → build → node → drift:python)。

用户更新

npm install dsh-unified-agent-memory@0.9.2

或 dsh 桌面端的插件更新。数据库自动迁移,无需手动操作。

v0.9.1 — 参与度层默认启用

Choose a tag to compare

@Noelune Noelune released this 31 Oct 11:48

Changed — 参与度层默认启用(不再是自愿开启)

  • block ID 补写自动执行:每个 vault 一次性的 backfill-ids 不再需要手动跑——memory sync、catchup(dsh 每小时补跑路径)与 setup init 在标记缺失时自动执行(marker 防重、幂等、只追加 ID 后缀、走唯一执行器,绝不进入检索读路径)。
  • Claude Code 钩子默认安装:setup init 与新的 setup hooks 把三条钩子幂等追加进 ~/.claude/settings.json(首次写入自动备份;你的已有钩子原样保留;无法解析的 settings 绝不触碰;--no-hooks / hooks --remove 随时退出)。手动粘片段仍是等价方式。
  • 钩子队列默认排水:catchup 每次运行顺带 digest --queue(session transcript → 事实抽取 → 提交区),失败只计报告、绝不拖垮补跑。
  • 要退出:setup hooks --remove + 已打标记的 vault 不会再动。

Verified

Python 534 / JS 886 / setup 32 / bench 一致 / 快照 46 逐字节 / e2e 46;新增 8 例默认启用行为测试。

⚠️ 注意:本版本的 npm 产物不完整(发布流程事故),请直接安装 v0.9.2。git 仓库内容本身没有问题。

v0.9.0 — 分层地基、事实身份与参与度层

Choose a tag to compare

@Noelune Noelune released this 31 Oct 11:48

一次大版本累积(80 个提交,五个批次)

兼容性:数据库首次打开自动迁移 v10 → v11(只增不减——memories/embeddings/access_log 一行不动,自动留 .v<旧版本>.bak 备份;已在真实库副本上验证 164/130/848 守恒)。无需任何手动操作。

Added — 批次 A:止血(12 项修复 + 1 个现场迁移 bug)

  • 迁移不再失忆:版本不符从"全表 DELETE"改为增量注册表 + 单事务 + 失败回滚 + 自动备份;并修掉一个会让所有真实 0.8.8 库升级直接崩溃的顺序 bug。
  • 事实身份改为 occurrence 身份(跨文档同句不再静默合并、删一篇不丢另一篇)。
  • 中文检索:三套词法表收敛为一张 fts_lex(ASCII 词 + CJK bigram)——旧表对中文恒 0 命中却拿最高权重。
  • 哈希审计链全接入(memory audit [--verify]);遗忘去毒(retrieved/used 拆分,只有被引用才强化);查询向量预算 0.8s→0.12s;图流门控;预算按价值密度装填。
  • 完整清单见 BATCH-A-REPORT.md。

Added — 批次 B/C/D/E:地基、置信度、Claim Ledger、参与度层

  • 四层架构(surface/app/domain/infra),依赖方向由架构测试钉住,六类白名单全部清零,规模预算门开启。
  • 检索弃答:零支持的弱命中不再返回,--json 新增 confidence。问不存在的东西得到空结果,而不是 8 条 0.03 分的弱匹配。
  • 遗忘收敛:保护网从 20+ 关键词宽网(命中约 75% 事实、遗忘形同虚设)收敛为显式白名单(凭据/红线/端点/协作);可触达率 41.7% → 75%。旧事实现在会真的参与衰减评分——全部可回滚(进 记忆遗忘区/)。
  • Claim Ledger(schema v11):每条事实一个永久 block ID(^c-…,Obsidian 原生语法,改文字不改 ID);memory backfill-ids [--dry-run] 幂等补写;memory search --as-of YYYY-MM-DD 与 memory explain <id>。
  • 参与度钩子:integrations/hooks/memory_hook.py 平台无关钩子运行时(会话开始常驻包 / 每轮本地检索 / 会话结束排队沉淀)+ Claude Code 接入物 + memory digest --queue。
  • README.md 不再进事实索引。

Verified

Python 526 / JS 886 / setup 32 / typecheck 0;bench 检索指标与 0.8.8 逐字段一致(五批次未触碰排序权重);CLI 快照逐字节;46 项真功能 e2e;真实库副本迁移守恒。

v0.8.8 — 两个进门就绊人的小地方

Choose a tag to compare

@Noelune Noelune released this 24 Oct 05:13
fc6b182

Fixed — 两个进门就绊人的小地方

preview 拒绝 --view。 view 是位置参数,文档和插件自己的 argv 构造都用那个形式,所以位置参数仍是唯一规范写法。但 --view 是读过 search --limit、doctor --strict 之后会顺手打出来的拼法,argparse 直接以 exit 2 顶回去:

$ memory preview --view pending
memory: error: unrecognized arguments: --view

现在它是别名,渲染同一个 view。两道闸门防止它变成第二个行为不同的入口:位置参数和 --view 给了不同 view 时报错并列出两者,不静默挑一个;一个都不给时列出四个可选 view,而不是把 None 传进 preview.build()。

顺带记一个坑:两者共用一个 dest 看着更整洁,但不工作——argparse 先解析可选、后解析位置,位置参数的默认值会把已解析的值冲掉。两个名字分开,在 cmd_preview 里汇合一次。

装好的包跑不了自己的测试。 files 没列 core/tests,.npmignore 里又写着 core/tests/,两处一起把它挡在外面:

$ python -m unittest discover -s core/tests
Ran 0 tests

于是"装了所以无法自证"成了必然。测试套件只用标准库,约 700 KB,没有任何依赖上的理由扣着不放。包从 104 条目增到 132,含 28 个测试模块,打包 545 KB。audit:package 仍然干净:入口是 core/tests/*.py 这个 glob,.log/.bak/.db/__pycache__ 进不来。

Tests

  • test_preview.py:别名与位置参数输出同一个 JSON;两种拼法冲突时非零退出并说明;都不给时列出四个 view。
  • test/package-contents.test.mjs(新增):core/tests/*.py 在 files 里、.npmignore 没排除它、非源码(src/、scripts/、test/、node_modules、字节码)不随包走、入口保持 *.py glob 以免夹带 fixture。
  • health.py 的未归类计数不再把 markdown --- 分隔线当事实行(用与同一函数里判重复相同的谓词;回归测试在旧谓词下报 4 != 3)。

Verified

core unittest discover 374 OK、node --test 24 OK、vitest 865 OK(38 文件)、typecheck 0 错误、audit:package 132 条目、drift:check 与 drift:python 均通过。

v0.8.7 — 括号留着,只删里面的废话

Choose a tag to compare

@Noelune Noelune released this 20 Oct 17:35

这一版在退什么

0.8.6 看到「不要写这种话」就以为是不能用括号,把六处正常的括注改成正文:

0.8.6 改成了 现在
dsh · 未识别 profile dsh(未识别 profile)
已并存,两条都留下,写进 事实冲突.md 已并存,两条都留下(写进 事实冲突.md)
8 个孤儿索引库,是旧 vault 留下的,… 8 个孤儿索引库(旧 vault 留下的),…
空文档 (空文档)
并入 · Agent 建议 并入(Agent 建议)
没有对应的旧记录 (没有对应的旧记录)

那不是删废话,是把废话挪出括号再加一个逗号,句子反而更长。全部退回。

真正删掉的

原来 现在
将发送到:一个新会话(不动你正在聊的这个) 将发送到:一个新会话
已经是最新版本了,不用再装。 已经是最新版本了。

「一个新会话」已经说明了它不动当前会话,后面那半句是重复。

规则

括号照常使用。问题只在括号里塞了解释性、重复性的补充说明 —— 那种内容直接删掉,不要改成
正文里的另一句话。占位、标签、具体值照常留在括号里。

其它

只有文案变化,所以是 patch。装完要重启 DSH。

v0.8.6 — 把界面里那些括注式的自言自语删掉了

Choose a tag to compare

@Noelune Noelune released this 24 Oct 05:13

Changed — 把界面里那些括注式的自言自语删掉了

0.8.5 的徽标悬停提示写成了这样:

点击更新到 v0.8.5(只向 npm 查一个版本号,不上传任何本机信息)

用户的评价:

不要写这种傻逼的要死的话……一股子AI味,看起来贼蠢

对。括号里那句话不是给读者用的,是这个功能在替自己辩解:没有它,用户照样知道点一下会发生什么;
有了它,读者要先读一段产品宣言才能看到版本号。悬停提示只需要一个动词和一个版本号:

点击更新到 v0.8.5

同一批清掉的还有四处同样的写法:

原来 现在
将发送到:一个新会话(不动你正在聊的这个) 将发送到:一个新会话,不动你正在聊的这个
dsh(未识别 profile) dsh · 未识别 profile
已并存,两条都留下(写进 事实冲突.md) 已并存,两条都留下,写进 事实冲突.md
8 个孤儿索引库(旧 vault 留下的),… 8 个孤儿索引库,是旧 vault 留下的,…
(空文档) / (Agent 建议) / (没有对应的旧记录) 空文档 / · Agent 建议 / 没有对应的旧记录

规则收进一句话:括注只放读者要用的具体值——版本号、路径、命令、可选方案;原因、免责、
功能动机、安抚话术一律不进括号。
已经写进本机记忆库的「我的偏好摘要」与「工程执行规则」,
后续会话会读到它。

为什么是 patch:改的是文案,行为、接口、记忆格式都没动。

v0.8.5 — 侧边栏「可更新」,点一下安装

Choose a tag to compare

@Noelune Noelune released this 20 Oct 15:31

这版解决什么

DSH 永远不会提醒第三方插件有新版本:updateAvailable() 读不到 pkg.installTarget 就直接返回
false,而这个字段只给官方按需目录里的包设置。两个第三方插件市场也都还停在 0.7.1。结果就是
0.8.x 对已经装过的人等于不存在——没有提示,也没有去查的理由。

现在面板自己说:

  • 侧边栏插件图标旁渲染 「可更新」,点一下直接安装最新版。
  • 状态四态:可更新 → 更新中 → 重启生效;失败显示 更新失败,悬停给出宿主原话,再点重试。
  • 安装期间忽略重复点击(两次 pnpm 跑同一个 lockfile 只会更乱)。
  • 点击同时打开面板——失败原因印在里面。一个点了没反应的按钮和一个坏掉的按钮长得一样。

安装不是插件自己干的

POST /api/dsh-unified-agent-memory/update 只调宿主的
pluginManager.installBundle('dsh-unified-agent-memory@版本', { saveExact: true }) —— 插件页和
dsh plugin 用的就是它。它自己会在失败/取消/无 bundle patch 时还原 package.json 与
pnpm-lock.yaml。这里不跑 pnpm、不碰 node_modules。

两个宿主细节:

  • HMR 下会先被拒。桌面端 installBundle 对「已是 profile 依赖」的包抛
    already-installed。官方 UI 的做法是 remove → install,这里照做;宿主没有 removeBundle
    就如实报失败,绝不在背后覆写文件。
  • 没有插件管理器的主机(headless / CLI)答 unsupported,提示手动更新。

隐私

安装这个 POST 的请求体是 {},不发任何本机信息。唯一的外网请求仍是那个早就存在的版本号
GET(每小时一次,registry.npmjs.org 不通才退到 npmmirror)。悬停提示就把这句话写给用户看。

装了之后

装完需要重启 DSH 才生效——运行中的插件不能替换自己。徽标会一直显示「重启生效」直到重启。

本版是 patch:没有对外契约变化(记忆文件格式、CLI 参数、既有 JSON 契约都没动),新路由是新增的。

0.8.4 — 面板不再把别人的队列摆给你看

Choose a tag to compare

@Noelune Noelune released this 20 Oct 10:44

0.8.4 — 面板不再把别人的队列摆给你看

0.8.3 把两个队列的说法分开了,但那一块本身就该消失。这一版是同一件事的下半截,把它删掉。

删掉的东西

待处理页里那个「待晋升的提交」列表——连同它的两个按钮一起去掉。那是 Agent 提交完、等下一次晋升任务处理的中间态,还没轮到人。每天晋升任务会自己合并重复、把判不了的移上来。不需要在 Agent 干完活之前替它做决定。

页签改名为 需要你裁决,一页只问一个问题:这条记忆到底该算哪一边?

现在的分工

队列 谁处理 在哪看
情境信息/事实冲突待裁决.md 你 面板「需要你裁决」
Agent提交区/*.md 每日晋升任务 健康页的待晋升计数

提交队列的积压是健康数字,不是待办事项,所以它只在健康页出现一次。徽标改成数冲突队列——一个会为「没人需要做的事」亮起来的徽标,会教会你看不见它,然后在真正要紧的那天失效。

死代码清理

面板不再调用 /dismiss 和 /accept,客户端里对应的 dismissItem / acceptItem、五个文案函数(previewEmptyText、dismissReasonText、acceptReasonText、acceptOutcomeText、inboxSummary)、三个工具函数(oldestFirst、relativeAge、noteDigest)全部删除,随附 46 个没有主人的测试用例。

两条服务端路由和 memory accept|dismiss CLI 保留不动——面板不调用它们,和下线路由是两件事,后者会动契约,另开一版。

验证

  • 闸门全绿:typecheck / drift:check / vitest 830 / core 370 / setup 32 / build / node 21 / drift:python / sync:check
  • lib/client-ui.js 144.1kb → 131.5kb
  • 产物核对(按 \uXXXX 转义查):收录 0 处、不采纳 0 处、没有需要你裁决的事 1 处、裁决队列读不到 1 处
  • 新增断言变异验证 5/5 CAUGHT

升级

升级后需要重启 DSH 才会看到新面板。

v0.8.3 — 待确认页:把两个队列的名字分开,并说清谁清它们

Choose a tag to compare

@Noelune Noelune released this 20 Oct 10:10

v0.8.3 — 待确认页:把两个队列的名字分开,并说清谁清它们

用户的原话:「每天 3 点才晋升,为啥现在这一页就有内容?」

机制没错,说法错了。 这一页装的是两个队列:

屏幕上的块 数据来源 什么时候出现 等谁
需要你裁决 情境信息/事实冲突待裁决.md 晋升时发现同一件事两种说法才写 等你
待确认(旧名) Agent提交区/*.md memory_submit 一写就出现 等下一次晋升

下面那块是晋升之前的收件箱,跟 3 点无关。但两块共用一页,页签叫「待确认」、
徽标只数下面那块,于是「还没到点处理的提交」看起来像「在等你确认的东西」——
而页面从不交代谁清、什么时候清。

这一版只改说法,不改行为

  • 页签 待确认 → 待处理;块标题 待确认 → 待晋升的提交;徽标 tooltip 同步为 N 条待晋升的提交。
  • 列表上方补一句说明:这批提交等每天的晋升任务处理,重复的会合并、判不了矛盾的移到上面的「需要你裁决」;现在就点「收录」/「不采纳」等于提前替晋升任务处理掉这一条——按钮是可选项,不是唯一出口。
  • 刻意不写死 03:00:晋升时间属于各安装自己的 job 配置,写死对任何换过时间的安装都是错的。
  • 空态、积压提示、健康页/统计页行标签、not-a-submission 拒绝说明一并扫干净。

读的还是同一个 preview pending / preview conflicts,按钮、路由、核心语义一字未动。

已知未修

页签徽标仍只数收件箱、不含冲突数——stats.totals 里没有冲突计数,要加需改 core 的 stats.build() 契约。

Full Changelog: v0.8.2...v0.8.3

v0.8.2

Choose a tag to compare

@Noelune Noelune released this 20 Oct 09:53

Patch release from an external audit. No new surface: six defects closed, all reproducible in an isolated vault, one report refused as not-a-defect.

Fixed

  • A failed core rendered as an empty block. runCore() answers a failure with output: '' and the diagnosis in error; renderText() read output alone, so a missing interpreter, an uninitialised vault and a core traceback all reached the agent as a blank text block — the tool looked like it had nothing to say. It now falls back to error, and still prefers output when both exist.
  • Read routes had no Host fence. guard() checked only that the caller was loopback; the DNS-rebinding fence lived in guardWrite(). curl -H 'Host: evil.com' …/status returned 200, and /status, /preview, /doc, /search hand back local absolute paths and note bodies. The fence moved into guard(); guardWrite() lost its duplicate.
  • A refused write exited 0. dismiss / accept / advise / decide printed the reason to stderr and returned normally, so a script read a refusal as success. note already exited 1 on the same path. --json deliberately stays exit 0 — runCore discards stdout from a failed subprocess, so the panel needs data.reason in a normal envelope.
  • show refused the spelling the vault writes. 上下文索引.md links notes as [[我的偏好摘要]]; the doc had to end in .md to resolve. note_named adds the suffix (never strips it), and the refusal reason is computed on the normalised name.
  • 用新 deleted the old fact instead of retiring it. promote has always moved a superseded line under ## 已取代; decide erased it — so the note claimed the old value had never existed. Both now share one move_to_superseded.
  • A ruled submission stayed in the inbox forever. decide dequeued and wrote, but never archived, so doctor kept counting it as pending work while the panel said the queue was clear. archive_settled_source() now archives — but only when every fact in that file is already in the adjudication log, because one ruling is not the whole file.

Not a defect

preview --view conflicts reporting unrecognized arguments: --view appears nowhere in the source, docs or history. The view has always been positional (preview conflicts). The report used a syntax this project never shipped.

Full check: typecheck, drift (JS + Python), 874 vitest, 370 core, 32 setup, 21 node — all green. Each fix was mutation-verified: reverting it turns the new test red.