memory-eternal
Health Gecti
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Community trust — 25 GitHub stars
Code Uyari
- process.env — Environment variable access in bin/dsh-memory.mjs
Permissions Gecti
- Permissions — No dangerous permissions requested
Bu listing icin henuz AI raporu yok.
🧠 给 AI 装第二大脑 · DSH 插件:对话结束自动沉淀知识卡,跨会话召回,知识图谱可视化,审核/回收/备份一体(SQLite,零外部依赖)
🧠 memory-eternal — 给 AI 装「第二大脑」
Memory Atlas · 本地优先的 AI 记忆库
自动沉淀 · 先审后入 · 知识星图 · 全 Agent 共享
简体中文(默认) · English
▲ Memory Atlas · 在线 Demo(示例数据)· 点此打开
对话结束自动沉淀成知识卡,跨会话不失忆;召回只取相关小块,省 token 少噪音。
全自研、零第三方记忆框架、不改 DSH 源码;一个记忆库所有 Agent 共享,SQLite 持久存储,零外部依赖。
⭐ 如果它帮你少解释了一遍上下文,点个 Star 就是最大的支持
DSH 一条命令:dsh plugin --profile desktop add memory-eternal
🌐 在线体验 · 打开记忆库 Demo (示例数据,不用安装)
▲ 真实运行截图:待审判词 · 知识星图 · 读卡器
✨ 它到底解决什么问题
| 你遇到的 | 以前的做法 | 用了之后 |
|---|---|---|
| 聊了 3 天的项目,新会话全忘 | 手动翻历史 / 重新解释一遍 | 每轮结束自动沉淀成卡,开工前 memory_recall 一句就回来 |
| 记忆塞进 prompt,token 爆掉 | 全量粘贴、越聊越贵 | 只召回相关的一小块(可配上限),省 token 也少噪音 |
| AI 写的东西不敢直接信 | 无法追溯、无法回滚 | 卡片带来源 / 时间 / 状态,先审后入,删除进回收站 |
- 看得见:知识卡 / 图谱 / 健康概览 / 沉淀日志,四层可视化;
- 控得住:审核模式(全部人工 / 仅高风险)、去重与矛盾扫描、批量合并、每日备份与一键恢复;
- 带得走:知识库是本地 Markdown + SQLite,可 git 管理、可导出,不锁在插件里。
🚀 五分钟上手
🟦 DeepSeek Harness(DSH)—— 重点
装(官方桌面版走插件市场,dsh web 用一条 CLI):
# ① 官方 DeepSeek Harness 桌面版(推荐):设置 → 插件 → 搜 memory-eternal 安装
# 等价 CLI(该壳的 profile 名就是 desktop):
dsh plugin --profile desktop add memory-eternal
# ② dsh web(浏览器端):
dsh plugin --profile web add memory-eternal
# 或在 profile(pnpm workspace)里直接更新。注意用 pnpm,npm install 会报 EUNSUPPORTEDPROTOCOL
cd ~/.dsh/profiles/web && pnpm add memory-eternal@latest
# 刚发新版时 @latest 可能被 pnpm 的最小发布年龄门槛/元数据缓存挡住(静默停在旧版):
# 直接指定版本号最快,pnpm 会自动把它加进 workspace 的 minimumReleaseAgeExclude
cd ~/.dsh/profiles/web && pnpm add [email protected]
# 验版本(应等于上面装的号),然后重启对应宿主生效
node -e "console.log(require('memory-eternal/package.json').version)"
🖥️ 桌面版支持策略(先看这条)
- 优先、且只保证支持官方 DeepSeek Harness 桌面版(官方 Electron 发行版,profile 名
desktop)。v0.9.4 → v0.9.6 三轮修复全部围绕它:0.1.7 设置服务换血、schemastery 3.18.4的 volatile 活引用、以及窗口标题栏避让(data-windows-titlebar,避免全屏浮层压住最小化/最大化/关闭)。 - 作者自研的
dsh-desktop(dsh-desktop-shell/dsh-ui-*/dsh-plugin-marketplace那一套)已停止维护,不再作为支持目标 —— 官方桌面版已覆盖同样能力,继续维护两条壳只会分裂兼容性。注意它的 profile 依赖里还钉着已从 npm 撤下的[email protected],会让pnpm install直接失败;建议迁到官方桌面版,并从 profile 里删掉这套依赖。 - 兼容矩阵(均已实测):官方桌面版
desktopprofile(schemastery 3.18.4)+dsh web的webprofile(schemastery 3.18.1),两条路径共用同一份代码。
重启宿主后,三样东西立即生效:
| 效果 | 在哪看 |
|---|---|
| 自动沉淀知识卡 | 每轮对话结束自动发生,无需操作 |
memory_recall 工具 |
Agent 需要历史时自动调用 |
| 图形化界面 | 侧边栏底部「记忆」按钮 / 设置 → 记忆 |
入口:侧边栏底部「记忆」按钮 → 记忆库(左栏含 知识卡 / 知识图谱 / 用量 / 审核中心 / 回收中心 / 记忆配置);「DSH 设置 → 记忆」= 纯配置页。
界面语言:全套 UI 中英双语,跟随 DSH 设置 → 语言 实时切换(含知识卡 / 知识图谱 / 审核中心 / 新建卡模板);浏览器直接访问 web 时跟随浏览器语言。
改配置:记忆库左栏「记忆配置」(或 DSH 设置 → 记忆)→ DSH 记忆配置 / 成本控制 / 自动审核配置 / 服务自管理,点「保存配置」即写入。autoWebMode/watchdogAutoSpawn 的改动需重启 DSH 生效;注意已常驻的 watchdog 不会被配置改动自动停掉,要停止或换新版请执行 dsh-memory stop / dsh-memory restart(见「服务自管理」)。
🟨 Claude Code
npm i -g memory-eternal # 装 CLI + MCP(写 ~/.claude.json mcpServers.memory)
dsh-memory connect claude # 写 ~/.claude/settings.json 的 SessionEnd hook → 会话结束自动沉淀
装完即用:会话里说「recall 一下数据库选型」→ 自动检索记忆;会话结束 → 自动沉淀进统一 ~/.dsh/memory-vault(新卡 pending 待审核)。
🟧 Codex CLI / Cursor
npm i -g memory-eternal # 装完自动写 Codex config.toml / Cursor mcp.json 的 mcpServers.memory
dsh-memory connect codex # 写用户级 ~/.codex/hooks.json 的 Stop hook → 会话结束自动沉淀(含 Codex Desktop)
dsh-memory connect cursor # 写 ~/.cursor/hooks.json 的 stop/sessionEnd hook → 自动沉淀
重启工具 → MCP 已在列表,会话里直接:用 memory_recall 查一下项目历史决策;会话结束自动沉淀进统一 ~/.dsh/memory-vault(新卡 pending 待审核)。
三种 agent 同一套库:全部写入
~/.dsh/memory-vault,每卡submittedBy区分作者(DeepSeek Harness / claude-code / codex / cursor),主库只显已审核、审核中心管新卡。
原生插件(可选,平台 Marketplace):仓库含
.claude-plugin/.codex-plugin/.cursor-plugin清单,可claude /plugin marketplace add EternalNight996/memory-eternal+/plugin install、codex plugin marketplace add EternalNight996/memory-eternal+codex plugin add、Cursor Settings→Plugins。connect走用户级 hooks.json(更稳,不依赖 Marketplace 审核;Codex Desktop 也走这条)。
只保留本插件(卸载其它记忆插件,如 agentmemory):
dsh-memory只写自己的键(mcpServers.memory/ 含capture.mjs的 hooks),不依赖、也不冲突任何其它记忆插件。卸载其它插件需在其自身配置层删除对应条目(如 Codex 的[marketplaces.*]与hooks.state.*、其hooks.json事件、~/.agentmemory数据目录)。
🟩 浏览器(不依赖任何 Agent)
dsh-memory open # 起 web + 开浏览器(默认 http://127.0.0.1:7999)
统计 / 搜索 / 知识卡(增删改合并导入导出)/ 知识图谱,全在此。与 DSH 内嵌页同一份 UI,数据同步。
zcode(智谱):暂无原生 MCP,经社区 zcode-open-bridge 转 MCP 或用 CLI。
📖 命令速查
dsh-memory recall "数据库选型" # 检索
dsh-memory capture "重要结论..." # 手动沉淀(- 读 stdin)
dsh-memory sweep ~/.claude/projects # 挖掘已有会话记录
dsh-memory setup [--dry-run] # 重跑/预览自动挂载(幂等)
dsh-memory connect <claude|codex|cursor> # 写会话结束自动沉淀 hook(用户级 hooks.json,含 Codex Desktop)
dsh-memory mcp # MCP stdio(挂任意 MCP 客户端)
dsh-memory serve [--port 7999] # 前台跑 web
dsh-memory open # ensure web 存活 + 开浏览器
dsh-memory watchdog [--port 7799] # 看门狗保活 web(独立进程)
dsh-memory status [--json] # 看常驻 watchdog:pid / 端口 / 版本 / 是否存活
# 以及**端口上真正在服务的版本**(锁里的版本号只是 watchdog 自述)
dsh-memory stop [--port 7799] # 停止常驻 watchdog(配置改动不会自动停;连它拉起的 web 一起停)
# 停完会再问一次端口占用者,仍被占着就明确告警
dsh-memory restart [--port 7799] # 停止并重起 watchdog(升级后换新版)
# 按端口占用者兜底收掉锁里没登记的旧 web,起完自检端口上服务的版本
dsh-memory audit list [--status pending|rejected|approved|deleted|all] [--limit N] [--json]
# 列出卡片;总数不受 --limit 影响,--limit 只限制显示条数(默认全部)
# all = 审核队列(pending + rejected),不含 approved / deleted
dsh-memory audit approve <卡片路径...> # 人工批量批准
dsh-memory audit reject <卡片路径...> --reason "..." # 人工批量驳回
单独装(不发 DSH)时 dsh-memory 命令来自 npm i -g。
⚙️ 服务自管理(白话)
三个概念,别搞混:
- web server 怎么保活(
autoWebMode)→init=DSH 启动时拉一次(默认);interval=DSH 进程内定时探活自动拉起(0 额外内存);manual=全手动只从dsh-memory open起。 - 看门狗进程(
watchdogAutoSpawn,默认开)→ 一个独立 node 进程,DSH 退出了它也能拉起 web(约 +47 MB 内存)。只在要 7×24 保活时开。 - 版本漂移自愈(
autoRestartOnDrift,默认开)→ 常驻 web 比 DSH 活得久:升级只换磁盘文件,端口上那个进程仍跑着启动时加载的旧代码(「启动 dsh 后运行中还是旧版本」就是这么来的)。开着它,DSH 激活时会问端口上真正服务的版本,与磁盘不一致就用新代码重启常驻实例;关掉则只告警,需要时在「插件信息」点重启常驻实例。 - 自动挂载 MCP(
autoMcpSetup,默认关)→ 是否自动把 MCP 写进 Claude Code/Codex/Cursor 配置。关 = 不碰你本机配置文件,需要时手动dsh-memory setup。
改这些:记忆库左栏「记忆配置」(或 DSH 设置 → 记忆)→ 表格里改,点「保存配置」;autoWebMode/watchdogAutoSpawn 需重启 DSH 生效。
⚠️ 同端口不会因配置改动而自动替换(#19):看门狗是独立进程且故意不杀(多会话共用一个),所以
关闭watchdogAutoSpawn不会停掉已经在跑的那个,改webCheckIntervalMs/webMaxRestart也不会生效。
要停止 / 换新版请显式执行dsh-memory stop [--port N]与dsh-memory restart [--port N](stop会连它拉起的 web 一起停,不留占端口的孤儿);dsh-memory status会显示常驻实例的 pid、端口、启动时间与版本是否落后于本机安装的包,并单独报出端口上真正服务的版本
(锁文件里的版本号只是 watchdog 自述,端口上跑旧代码时它不会变)。⚠️ 升级后请以「端口实际服务版本」为准(#23):
restart现在不只信锁里登记的 web 子进程,
还会按端口占用者兜底清理(锁里没登记的旧 web 也会被识别并收掉,非本插件的进程只告警不动手),
并在起完之后自检「端口上服务的版本 = 本机版本」——对不上就以非 0 退出码报失败,不再打印「已启动」就完事。✅ 「更新了却还是旧版本」现在有两条出路(#19/#23):① 默认开的
autoRestartOnDrift在 DSH 启动时
自动替换旧常驻实例;② 面板「插件信息」里发现版本漂移时给一个**🔄 重启常驻实例按钮(服务端即dsh-memory restart --port N的同一实现:收旧实例 → 抢锁 → 拉起新 web → 自检端口版本)。
注意「运行中」那一格指的是服务本页的进程**:独立 Web 页里就是常驻 web,DSH 设置页里是宿主 ——
面板会同时列出常驻实例的版本,两者不同时一眼能看出该重启谁。
MCP 是协议不是常驻服务:agent 开会话才 spawn,用完即退,没有「开机自启」一说。
三种部署强度
| 场景 | 配置 | 内存 |
|---|---|---|
| 个人开发(推荐) | autoWebMode=init + 手动把 watchdogAutoSpawn 关掉(默认是开,见下方配置表) |
web 47 MB |
| 常驻 7×24 | watchdogAutoSpawn=on |
web + watchdog 47+47 MB |
| 真正开机自启(无 DSH) | Windows 计划任务跑 dsh-memory watchdog --port 7799 --interval 5000 --max-restart 10 |
同上 |
⚙️ 记忆配置(大白话)
所有配置都在 记忆库左栏「记忆配置」(或 DSH 设置 → 记忆)里改,点「保存配置」生效。下图就是配置页全貌(含插件信息 / Agent MCP 挂载状态 / 自动审核配置):
一、最常用
| 配置项 | 默认 | 大白话说明 |
|---|---|---|
| 自动沉淀 | 开 | 每轮聊完自动把有用的内容存成知识卡 |
| 自动召回 | 开 | AI 需要历史时自动帮你查记忆 |
| 记忆库目录 | ~/.dsh/memory-vault |
记忆存哪,纯 Markdown 可 git 管理 |
多库(按项目隔离记忆)
配置里还有两个字段(写在 memory-eternal-config.json,或设置面板同步过来的共享配置):
{
"vaultProfiles": [{ "name": "work", "path": "D:/vaults/work" }],
"activeVault": "work"
}
解析顺序:MEMORY_VAULT_DIR 环境变量 → activeVault 命中 vaultProfiles[].name 的 path → vaultDir → 默认 ~/.dsh/memory-vault。
v0.9.8 起,CLI / MCP / hooks / 独立 web / sweep 与 DSH 宿主共用同一套顺序:以前只有宿主认 activeVault,切库后终端与 hooks 仍往默认库写,记忆会被劈成两份。
说明:配置页目前还没有
vaultProfiles/activeVault的可视化表单(要手工写配置文件),按 workspace 自动选库也还没做。
二、省钱包(重要)
| 配置项 | 默认 | 大白话说明 |
|---|---|---|
| 蒸馏知识卡 | 开 | 把对话压缩成精炼知识卡(要调 AI,花钱)。关掉 = 存原文,一分钱不花 |
| 语义去重喂 AI | 开 | 判断新内容是不是重复(要调 AI)。关掉 = 用简单去重,省一次 AI 调用 |
| 蒸馏输出上限 | 900 | 压缩一次最多写多少字,越大越准越费钱 |
| 召回相关性阈值 | 2 | 检索要「多像」才返回,越大越准但漏得越多(越省) |
| 捕获最小长度 | 200 | 对话太短不存,避免闲聊浪费 |
| 日配额 | 60 | 一天最多存几张,防 AI 烧钱 |
三、服务怎么跑
| 配置项 | 默认 | 大白话说明 |
|---|---|---|
保活模式 autoWebMode |
init | init=DSH 启动时开一次 web;interval=定时检查挂了自动重启;manual=全靠手动 |
看门狗 watchdogAutoSpawn |
开 | 后台一个独立进程保证 web 不死(+47 MB 内存)。个人用可关——但关闭后既有实例不会自动停,需 dsh-memory stop(#19) |
漂移自愈 autoRestartOnDrift |
开 | DSH 激活时发现端口上服务的版本 ≠ 磁盘版本(升级后常驻 web 还是旧代码)就用新代码重启常驻实例。关掉只告警,仍可在「插件信息」点**🔄 重启常驻实例**(#19/#23) |
自动挂载 MCP autoMcpSetup |
关 | 让 Claude Code / Codex / Cursor 也能用你的记忆库。开=自动配好它们;关=不碰你电脑配置,手动跑 dsh-memory setup |
凭证提示 secretHint |
空 | 填一句自己的约定(例如「需要 API key/token 时先查记忆里的密钥目录」),会注入每个会话的 systemPrompt。记忆库里只存目录(名字/位置/取用方式),密钥的值不入库 —— 见下方「密钥怎么放」 |
🔑 密钥怎么放(
secretHint的用法):把值留在本机凭据里(环境变量 / 密钥文件,权限收紧),
记忆库里只留一张目录卡(有哪些键、放在哪、干什么用、轮换状态),再用secretHint让 agent
每次会话都想到去查这张卡。为什么值不能入库:卡片summary就是正文开头,召回默认就带 130 字
(recallSummaryLen),值一入库就随每次相关召回进入模型上下文;/export、备份、多机同步也会带上。dsh-memory setup会在 Claude Code 里注册一个PreToolUse守卫:直接读.credentials.yaml/.env/*-token会被拒绝并指路到目录卡。
💰 想省钱:把「蒸馏知识卡」关掉、调低「蒸馏输出上限」、调高「召回相关性阈值」。
🎯 一键推荐配置(按场景点一下)
配置页顶部有 🟢 A 轻量省心 / 💰 B 极致省钱 / ⭐ C 高质量 三个按钮,点一下自动填好对应值,再点保存即可:
| 方案 | 场景 | 保活 | 看门狗 | 蒸馏 | 蒸馏上限 | 召回阈值 | 内存 | LLM 成本 |
|---|---|---|---|---|---|---|---|---|
| 🟢 A 轻量省心 | 个人开发(推荐) | init | 关 | 开 | 2000 | 2 | ~47 MB | 正常 |
| 💰 B 极致省钱 | 预算敏感/多 Agent | init | 关 | 关 | 500 | 3 | ~47 MB | 近 0 |
| ⭐ C 高质量 | 长项目/团队 | interval | 开 | 开 | 1200 | 1 | ~94 MB | 高 |
🔔 自动沉淀日志 & 异常提示
自动沉淀是后台静默管线:一旦坏了,以前只能表现为「卡不再增加」,看不出原因。v0.9.0 起它会自己说话。
- 自动沉淀日志:设置 → 记忆 → 用量/今日,最近 100 条运行轨迹:
🚀 启动/👂 会话接入(用的事件接口 + 事件数)/✅ 新建卡/➕ 追加更新/⏭ 跳过(原因)/❌ 失败(原因)。 - 异常主动提示:出现失败即亮红——记忆库页面顶部红条,同时把「自动沉淀异常 + 原因」注入 Agent 上下文,Agent 会在回复第一句提醒你,不需要你自己去翻日志。
- 停滞兜底:本次运行至今一次收尾事件都没收到、且最近 20 分钟内持续有轮次开始(例如 DSH 改了事件名/作用域),5 分钟巡检记一条
warn—— 不再冒充红色「自动沉淀异常」,正在进行的长回合也不会被误判(一轮对话中途inbox/claimed会多次触发,收尾要等回合真的结束)。 - 接口自适应:自动识别 DSH 会话事件接口(
ownEvents()→snapshotEvents()→events),DSH 升级换 API 不会再静默断掉。
🛡️ 审核中心 & 回收中心
新卡默认进审核中心(pending),由你确认后才入主库;驳回的进「已驳回」,可恢复或删除进回收站。命中免审条件(审核模式=全部免审 / 免审智能体 / 免审类型)的新卡直接入库;回收站软删卡 30 天内可恢复,超期自动永久删除。
v0.3.0 起:审核守卫下沉到数据库层。 所有写入路径(自动沉淀 / MCP / UI 新建 / 导入)强制经过
enforceAudit(),调用方无法绕过。setCardStatus()是唯一审核操作入口,每次变更写入audit_log审计日志(who/what/when/why),不可篡改。
v0.10.0 起:升级为「两套存储」——未审核内容物理隔离。
cards(主库 / 正常区)物理上只存 approved;quarantine(隔离区 / 异常区)存pending/rejected/deleted。
意义:召回、检索、知识图谱、去重池、导出这些读路径查主库即安全——不再依赖「每处 SQL 都记得写WHERE status='approved'」。
(v0.9 审计实测漏过三处:去重池会把新知识追加进待审卡、readCard可按 path 直读未审核正文、/card接口无状态校验。)
审核流转 = 跨表搬家:批准 → 搬进主库,驳回/软删 → 搬进隔离区;搬家在一个事务里完成,更新记录与审计日志跟着走。
升级说明:首次启动会自动把存量非 approved 卡搬进隔离区(不删除任何内容,都在同一个.db文件里)。
若回滚到旧版本,审核队列会显示为空(旧代码看不到隔离区表),升级回来即恢复。
- 待审核 / 已驳回 双页签,按类型 / 日期 / 智能体筛选,全选后一键「批准 / 驳回 / 删除进回收站」。
- 审核规则在「记忆配置 → 自动审核配置」:
审核模式(全部要审 / 全部免审)+免审智能体+免审类型+回收保留天数。 - Agent 不能代你审批(v0.9.1 起):只要审核生效,插件就往 Agent 的上下文里注入红线——新卡只能停在「待审核」,并明确禁止调用
approve/reject接口或任何等价方式绕过;判断权始终在你手里。
🥇 为什么审核系统更靠谱(对比其它记忆产品)
多数记忆产品(mem0 / Zep / agentmemory…)对话一结束就自动全量入库,好坏不论——噪音、错误事实、敏感内容一起进库,之后又被召回,污染上下文、放大幻觉。
memory-eternal 走人机协同审核,只让可信内容进主库:
| 维度 | 其它记忆产品 | memory-eternal 审核系统 |
|---|---|---|
| 入库 | 自动全量,无把关 | 新卡先进审核中心(pending),人工批准才入主库 |
| 质量 | 未过滤噪音/错误 | 只保留你确认过的卡 → 召回更准、噪声更低 |
| 可信 | 无来源/审核追溯 | 每卡带 submittedBy 作者 + pending/approved/rejected 审核状态 |
| 免打扰 | — | 免审智能体 / 免审类型命中 → 可信卡直接入库,零等待 |
| 容错 | 删了就没 | 回收站软删,30 天内可恢复 |
🧬 为什么全自研
市面上记忆方案多,但大多依赖第三方框架 / 动不动起 MCP 服务 / 记忆锁私有库。本插件把骨架自己搭,零第三方运行时依赖,逻辑逐行可读:
| 模块 | 自研实现 | 替代什么 |
|---|---|---|
| 去重 | 词法 Jaccard bigram(0.62)+ 语义去重 | 防重复卡 |
| 检索 | CJK 感知:中文整词 + 字符 bigram | 无需全文搜索引擎 |
| 图谱 | 力导向 + [[wikilink]]/共享标签连边 |
知识关联一眼看清 |
| 存储 | SQLite(node:sqlite 内置,零依赖) |
不锁库、分表存储、SQL 查询 |
| 审核 | 数据库层审核守卫 + 审计日志 | 杜绝越权,所有写入强制走审核 |
与热门项目同向(总结→存储→按需召回),但定位不同:本地、自研、零依赖、可读可控。如果你已在用 mem0/Zep 等,也能把它当「本地持久记忆底座」叠加使用。
🛠 开发 / 测试
npm i
npm test # 单元测试:vault 去重/检索/图谱 + capture 管线 + API 形状
npm run build # 构建 lib/client.js(DSH 内嵌)+ web/app.js(独立 web bundle)
📄 README 是中英两份独立文件:
README.md(简体中文,默认,npm / 插件市场展示这一份)
与README.en.md(English)。两份标题层级一一对应,改一处要同步另一处(新增一节两边都加)。
🐞 反馈问题
插件左栏有 🐞 反馈异常 入口:填完问题描述后可以
- 在 GitHub 提交(已预填) —— 标题 / 正文 / 诊断信息 /
bug标签都填好,再点一次 Submit 即提交; - 复制给 AI 的提示词 —— 粘进 AI 对话框,让它用
gh查重后建 issue 并回报链接。
为什么不能「点一下自动提交」:GitHub 不允许匿名创建 issue;插件里内置 token 会被反编译提取;OAuth / 自建中转需要服务器。所以实现为「预填 + 一次点击」。
诊断信息会自动脱敏(home 目录 →~,sk-/ghp_/github_pat_/api_key:等 →***),但公开提交前仍建议自己扫一眼。
如果你拿不到插件界面,也可以直接把下面这段贴给 AI(把「我的问题描述」换成你的问题即可):
我在使用 memory-eternal(DeepSeek Harness 的记忆插件)时遇到了问题,请你帮我把这个问题提交到 GitHub。
仓库:https://github.com/EternalNight996/memory-eternal
请按这个流程做:
1. 先跑 `gh issue list --repo EternalNight996/memory-eternal --state open --limit 50`,看是否已有相同问题:
- 已有 → 用 `gh issue comment <编号> --body-file <文件>` 补充现象,不要重复开新 issue;
- 没有 → 继续第 2 步。
2. 把正文写成文件,然后 `gh issue create --repo EternalNight996/memory-eternal --title "[Bug] <一句话概括>" --body-file <文件>`;正文用这个结构:
现象 / 复现步骤 / 期望行为 / 实际行为 / 环境(插件版本 / DSH 版本 / 操作系统 / Node)/ 诊断信息。
3. 如果 `gh` 没登录或不可用,不要尝试提交,改为输出一条「标题与正文都已预填」的 GitHub 新建 issue 链接给我,我自己点提交。
4. 提交成功后,把 issue 链接发给我。
注意:我的问题描述请原样照录,不要替我美化或省略细节;诊断信息原样放进「诊断信息」一节。
我的问题描述:
<在这里写你的问题>
诊断信息:
<把「反馈异常」弹窗里复制的诊断信息粘到这里>
环境自检(贴问题时附上更快定位)
dsh plugin list # 插件版本
node -v # Node 版本
curl http://127.0.0.1:7999/memory-eternal/api/web-info # 独立 web 是否活着
📋 更新日志
发布规则(2026-09-30 起):正式版一律三端同步发布 ——
npm publish+ GitHub Release + Gitee Release,并保证vX.Y.Ztag 在三端一致。桌面版 profile 依赖github:EternalNight996/memory-eternal、dsh web走 npm 版本号,少任何一端都会出现「npm 上是新版、桌面版还是旧的」。统一入口:npm run release:dry(预演)→npm run release(正式)。详见 PUBLISH.md。版本撤回说明:v0.9.16 – v0.9.22 已全部撤回(问题期间版本:git tag 已删除、npm 已标记 deprecated,请使用 v0.9.23)。v0.9.14 已 deprecated(客户端渲染改动降低手感,已回退;其服务器侧算法优化并入 v0.9.15,渲染与 v0.9.13 完全一致)。v0.9.0 – v0.9.5 均已在 npm 标记 deprecated;v0.9.1 / v0.9.2 / v0.9.3 / v0.9.4 的 release tag 已从 git 移除(v0.9.5 只是被取代、tag 保留)—— v0.9.1/v0.9.2 带记忆页白屏缺陷,v0.9.0 沉淀告警误报刷屏,v0.9.3 不支持 DSH v0.1.7-rc.2(升级后插件整体挂不上:设置服务换血 + 客户端
settingsScope消失),v0.9.4 在官方桌面版(schemastery 3.18.4 的 volatile 活引用)下抛cfg.vaultDir.trim is not a function、插件整体挂不上,v0.9.5 的全屏浮层会盖住桌面版的窗口控制面板(右上角「×」压在窗口「关闭」上,点一下会退出整个桌面壳)。请一律使用 v0.9.6+(npm i memory-eternal@latest)。
DSH 兼容性:>=0.1.5-alpha.2 <0.3.0-0(v0.10.1 起放宽:旧的上界<0.2.0在 semver 上会把 DSH0.2.0-rc.x排除在外 —— DSH 的插件守卫用includePrerelease:true判定,实际仍能装上,但声明与事实不一致;现在明确覆盖整个 0.1/0.2 线,且<0.3.0-0仍然挡住 0.3.0 及其预发布。已在官方桌面版 DSH 0.2.0-rc.2 上实测运行:插件挂载、记忆页、自动沉淀、审核中心、导入导出均正常)。v0.9.4 起适配 DSH v0.1.7-rc.2(该版本把设置服务换成纯表单 API,并移除了@deepseek-ai/dsh-client-runtime与settingsScope);v0.9.5 起兼容 schemastery ≥3.18.4 的 volatile 活引用(官方桌面版 profile 即此形态,web profile 仍是 3.18.1,两者都支持);v0.9.6 起全屏浮层自动让开桌面壳的窗口标题栏(不再压住最小化/最大化/关闭)。0.1.5 系列仍走旧的settings.register路径,两条路径都保留。
| 版本 | 日期 | 关键改动 |
|---|---|---|
| v0.11.0 | 2026-10-22 | Memory Atlas 正式版:V5 UI 全量落地 —— 记忆库 4 列知识卡 + 今日回看 + 固定/连接微缩图;待审顶部类型条 + 队列 + 右侧判词栏(无底部栏、无弹窗);知识星图 V5 页头/连接簇图例 + DPR/网格/边渲染优化;运行状态四张状态卡;读卡器右侧目录/相关记忆;金乌品牌图标;二维码点击放大;首次引导先 Star/Fork;GitHub/Gitee/npm 三端同步。 |
| v0.11.0-rc.1 | 2026-10-21 | V5 预览 RC:Memory Atlas 深色余烬视觉、228px 侧栏与顶栏;记忆库 V5 卡片网格 + 页头;审核顶部类型条 + 队列 + 判词预览 + 全选批量批准/驳回;星图默认缩小;首次引导(重点 👍)、读卡器目录/相关记忆与左栏交流群/赞助/旧程序/异常反馈;公开 Demo 同步 V5。 |
| 版本 | 日期 | 关键改动 |
|---|---|---|
| v0.10.54 | 2026-10-20 | 本地大模型蒸馏更稳(#48)+ README 焕新与最新截图。① #48:本地 30B 模型冷启动要 40–60 秒,而蒸馏超时硬编码 45 秒 → 每次冷启动必报 ABORTED;现在超时可配 captureTimeoutMs(默认仍 45s,本地推理建议 120000+),且超时后自动用更长超时重试同一候选一次(不直接换模型)。② #48 的第二种形态:模型不给 max-tokens finish 时,截断只表现为「JSON 未闭合」→ 新增独立错误码 TRUNCATED_OUTPUT,与 MAX_TOKENS 一起走「加倍输出预算重试同一候选」的阶梯。③ 配置页新增「蒸馏超时(毫秒)」输入项。④ README 焕新:全部换成真实运行截图(知识卡 / 知识图谱 897 节点 4257 连线 / 健康概览 / 审核中心 / 配置页)+ 新增「它到底解决什么问题」对照表 + 更清晰的卖点与 Star 引导(中英同步)。npm test 共 440 项 |
| v0.10.53 | 2026-10-20 | 健康面板(可靠性线):把看不见的状态集中一处。在「用量/今日」顶部新增「🩺 健康概览」:① 知识库(journal 模式 / 可写 / 最后一次体检时间,异常时给 doctor 指引);② 备份(数量 + 最近一次多久前,>7 天标黄、0 备份标红);③ 自动沉淀(最近一次成功/失败与原因);④ 版本(磁盘 / 常驻 / 宿主;未检查时给「开始检查」按钮,因为 align 要联网比 npm 不能自动跑)。数据来自既有接口:/overview 新增 backups 摘要(count/newestAt/最近 5 个文件名),加上 /capture-log 与 /align。顺带修一个白屏 bug:健康卡最初误用了 ConfigPanel 作用域的 tf() → 点开「用量/今日」整页白屏;同时给 panel-check 补了两道闸 —— 页面存活(textLen<20 判空白页) 与 Runtime.exceptionThrown 捕获,因为「白屏没有横向溢出」原本会让检查通过。新增 tests/health-panel.test.mjs(4 项)。npm test 共 437 项 |
| v0.10.52 | 2026-10-19 | 诊断包脱敏补漏(隐私修复,建议尽快升级)。0.10.51 的「一键诊断包」有个漏点:doctor/align --json 与 meta.json 里的家目录是 JSON 转义形态(C:\Users\x),而我只替换了原样/正斜杠两种写法 → 包里仍能 grep 到家目录明文。现在三种写法(原样、正斜杠、JSON 转义)都替换成 ,并在测试里加了一条「JSON 转义形态不能漏」的断言;本机重新生成诊断包实测:无家目录明文、无 sk- 密钥。如果你已经用 0.10.51 生成过诊断包并分享出去,建议重新生成一次再分享。 npm test 共 433 项 |
| v0.10.51 | 2026-10-19 | 一键诊断包 dsh-memory support(可靠性线;#47 里用户贴的现场被 GitHub 截断,我拿不到完整信息,这功能就是为这类沟通做的)。一条命令生成 zip:meta(版本矩阵/平台/node/vault)/ doctor / align / status / 看门狗锁 / 宿主心跳 / 共享配置 / 沉淀日志尾巴(默认 200 行)+ README;写入前统一脱敏(家目录→,sk-/ghp_/github_pat/Bearer/配置里 key 类字段→)。实现为零依赖*:自写 store/deflate ZIP 写入器(lib/zip.js,Windows 资源管理器可直接打开,实测 Expand-Archive 解压正常)。受限环境可加 --no-spawn 跳过子进程;--out 指定输出、--tail N 控制日志条数。新增 tests/support-bundle.test.mjs(4 项:脱敏、zip 结构、包内不含明文密钥、CLI 接线)。npm test 共 433 项 |
| v0.10.50 | 2026-10-19 | 「模型不守 JSON」不再每轮白烧调用(#47 衍生改进)。① 把「模型回了散文、输出里根本没有 JSON 对象」从笼统的 UNPARSEABLE_OUTPUT 里拆出来,给独立错误码 MODEL_NOT_JSON,文案直接说清「该模型可能不遵守结构化输出」。② 新增 lib/model-strikes.js:记下「哪个 provider/model 最近不守格式」(24h 窗口、最多 50 条),宿主选路时把它们排到候选末尾(不删除;全部命中过则按原顺序兜底,绝不没候选),并留一条「蒸馏候选已调整」日志。③ 效果:像 stepfun/step-api 这种会把蒸馏当聊天回的模型,第二轮起就不再优先尝试,省掉每次的无效调用。新增 tests/model-strikes.test.mjs(4 项:散文识别 / 排序稳定性 / 记录窗口与裁剪 / 接线守卫)。npm test 共 429 项 |
| v0.10.49 | 2026-10-19 | 桌面版里「无法选择蒸馏模型」修复(#47):独立页把 /llm-routes 与 /llm-test 代理给宿主 + 文案补一个状态。根因:桌面版的「记忆」弹窗是 iframe 里的独立页,它没有 DSH 的 llm 服务 → /llm-routes 恒失败 → provider/model 只能手填。现在独立页读宿主心跳(.host.json 里的端口,仅 127.0.0.1、心跳 120s 内有效),把这两个路由转发给宿主执行并标记 proxied;宿主不在线时给明确原因(面板直接显示,不再静默回退成手填)。② 版本卡状态矩阵补 restartRepair:版本已对但没人保活时提示「重新托管并保活」,不再出现「换成 v0.10.48(当前 v0.10.48)」这种自相矛盾的文案(截图实测)。npm test 共 425 项 |
| v0.10.48 | 2026-10-19 | 界面验收手段 + 状态穷举(方案一落地)。① 新增 npm run panel-check(scripts/panel-check.mjs,零依赖):用本机 Chrome headless + CDP 打开面板,逐屏断言没有横向溢出并截图(列表 / 阅读器 / 配置 / 反馈弹窗)。它第一次跑就抓到两个真 bug:卡片标题里的无断点长串((begin/chunk/probe/complete/abort))溢出 45px;反馈弹窗 textarea 溢出 22px(width:100% + padding 在 content-box 下)—— 正是用户报的「排版不对 / 滚动条异常」。② 修复:.mc-cardrow h4 断词、.me-overlay input/textarea/select 统一 border-box。③ 版本卡判定抽成纯函数 src/client/version-state.js:versionCardState(...) 返回 {showButton, emphasis, warn, hint},组件只做 key→文案;新增表驱动测试穷举 324 种组合,并守住不变式「只要拿到版本信息,按钮永远显示」—— 0.10.45「第三种状态导致按钮消失」从结构上不可能再发生。npm test 共 420 项 |
| v0.10.47 | 2026-10-19 | 面板版本探针自动复查 + 「常驻未运行」文案修正(用户反馈:重启后面板一直显示「常驻实例 未运行」)。① 现场其实是活的(status 显示 7999 在听、服务 v0.10.46),但版本探针只在面板挂载时跑一次 —— 恰好撞上重启窗口就会被缓存成「未运行」并一直显示。现在每 60 秒静默复查(不点亮「检查更新」按钮)+ 切回页面立刻复查(visibilitychange),重启后 6 秒本来就会复查一次。② 文案:常驻实例真没跑时,原来那句会拼出「当前 v」(空值);现在改走 vcRestartStart:「点击会用当前磁盘版本 v{disk} 把常驻实例拉起来(不用重启 DSH)」。npm test 共 408 项 |
| v0.10.46 | 2026-10-19 | 「重启常驻实例」按钮改为永远显示** + 修正「谁旧」的文案(用户第二次反馈按钮消失)。上一版把按钮放进「常驻实例需要重启」的分支,而实际状态是常驻实例已是新版、服务本页的进程(DSH 宿主)还是旧版** → 落进宿主提示分支、按钮又没了;更糟的是警告文案写着「点重启常驻实例即可」,而重启常驻根本不会改变宿主提供给你的本页版本。现在:① 按钮只依赖 vc 存在,任何状态都在;② 三种状态各给准确说明 —— 常驻旧(点了就换新版,不用重启 DSH)/ 宿主旧(重启常驻不影响本页,请重启 DSH)/ 全部对齐(卡住时可强制重启);③ 警告文案拆出 vcHostProcessStale,明确「服务本页的是宿主进程」。npm test 共 406 项 |
| v0.10.45 | 2026-10-19 | 修复「重启常驻实例」按钮消失 + 对话框排版/滚动条异常(用户截图反馈)。① 按钮常驻:它原来被包在「有异常才渲染」的警告块里 —— 版本一对齐整块消失,用户再也找不到(#28 也抱怨过)。现在操作行独立渲染:异常时是修复入口,一切正常时也保留按钮并说明「通常无需重启,卡住时可强制重启」。② 滚动条异常:阅读器最多 8 个头部按钮,而 0.10.42 给按钮组设的是 nowrap → 在 max-width:760px 的对话框里横向溢出;现在按钮自身仍不缩不换行、但按钮组整体允许换行(掉到第二行也不变形)。③ 排版:.me-dialog / .me-dialog-body 改 border-box 并隐藏正文横向滚动(阅读器的 width:100% + padding 不再撑破容器);诊断/反馈的 长路径加 word-break(远程 agent 的路径没有断点,以前必横向滚);大标题 modal 的 h2 加省略号。新增 tests/ui-overflow.test.mjs(4 项守卫)。npm test 共 406 项 |
| v0.10.44 | 2026-10-19 | 界面体验(第二批):标签点击即筛选 + 搜索命中片段。① 标签筛选:列表/阅读器里的标签芯片现在可点,点了立即按该标签筛(与搜索 / 类型 / 状态可叠加);工具栏出现可清除的「🏷 标签 ✕」。服务端 /cards?tag= 走 SQL 精确匹配 —— 按「JSON 数组里带引号的完整标签」用 instr 匹配,不会把 ab 命中成 a;countCards 与 sort=title 两条分支同步生效。② 搜索命中片段:命中正文时,列表多显示一段关键词附近的片段并高亮(折叠空白、两端省略),一眼看出「为什么命中」。③ 新增 src/client/snippet.js(纯函数,Node 可单测)+ tests/tag-filter.test.mjs(3 项)。npm test 共 402 项 |
| v0.10.43 | 2026-10-19 | 界面体验(第一批 · 读卡)。① 复制:阅读器新增「📋 复制正文」「🔗 复制路径」,复用带兜底的剪贴板工具,1.5s 内显示「已复制」(不再点了没反应);② 阅读进度:头部下沿 2px 细条随正文滚动推进,短卡直接 100%;③ j/k 翻页:阅读器内 j 下一张、k 上一张(基于当前排序后的列表),方向键仍留给正文滚动,输入框内不触发;④ 阅读宽度:窄/中/宽三档(720/900/1180px)一键切换并记住偏好;⑤ 延续 0.10.42 的头部不挤压修复。新增 tests/card-reader-ux.test.mjs(4 项守卫)。npm test 共 399 项 |
| v0.10.42 | 2026-10-19 | 卡片头部不再被长标题挤压(用户截图反馈)。旧布局是 space-between,而 h3 没有 min-width:0(长标题压不动),按钮组也没有 flex:0 0 auto / nowrap → 按钮被挤成竖排(「收/起」「关/闭」)。现在:h3 可收缩并单行省略(悬停显示完整标题)、按钮组 flex:0 0 auto + nowrap(永不被压扁)、头部允许换行(极窄时整组按钮掉到第二行,仍逐个可点);阅读器与导出图谱两个头部统一走 .me-head-actions。新增 tests/card-head-actions.test.mjs(3 项守卫)。npm test 共 395 项 |
| v0.10.41 | 2026-10-19 | 知识图谱初始视图缩小两个度。图谱是 canvas + sim.zoom,滚轮每格 ×1.1;按「两个度」= ×(1/1.1)² ≈ 0.826 处理 —— 初始 fit()(首屏、布局稳定后、以及「重置视图」)现在都在「刚好铺满」的基础上再外扩两格,四周留出拖拽/框选余量;滚轮与初始视图共用同一个 ZOOM_STEP 常量,以后调步长不会两边漂;原有限幅(0.35–1.8)保留,小图不会被缩到看不见。新增 tests/graph-initial-zoom.test.mjs(3 项源码守卫,含系数数值)。npm test 共 392 项 |
| v0.10.40 | 2026-10-19 | #45 后续(阻塞级):枚举超时不再被当成「端口空闲」+ macOS 红绿灯遮挡(#46)。① 报告者实测:Get-NetTCPConnection 在该机要 4.1–61.4 秒(TCP 表大),而 takeover 只给 2.5s → 超时 → 空输出 → null → 「端口已空出」→ 新看门狗绑不上 7999、漂到 8001,旧 web 永远活着、0.10.38 的修复永远轮不到执行。修法:Windows 改先走 netstat -ano(实测 1.8s 且直接给 pid,本机复测 403ms 拿到 pid+命令行),PowerShell 只作兜底;portFree 升级成三态(free / occupied / unknown),枚举超时=未知 → 中止接管并写明确日志(不再漂移);status 把「响应超时」与「没有服务」分开报;探测路径不再漏 socket。② #46:macOS 桌面版的窗口按钮是 Electron 原生 traffic light(titleBarStyle:hiddenInset),任何 z-index/portal 都盖不住 —— 插件改为跟随宿主安全区 --dsh-frame-overlay-top(macOS 68px,原生全屏宿主降到 20px),浮层顶边恒 68px、遮罩仍铺满全屏,不再被「关闭」按钮压住。npm test 共 389 项 |
| v0.10.39 | 2026-10-17 | 接线自愈:codex 的 TOML 按段解析 + 单引号/转义兼容 + 启动期只读体检(#44 第②④条)。① 修了一个真 bug:setup / align 解析 ~/.codex/config.toml 时没有限定 [mcp_servers.memory] 段,会抓到别的 server 的第一个 command =(本机实测读成了 MCP_DOCKER 的 docker.exe);而且只认双引号,而 codex 自己写出来是单引号 → 既读不到也改不掉,却照样打印「已更新」。现在读写共用纯函数(按段 → 单/双引号 → 反转义),改完校验真的变了,幂等(已是目标形态不算改动),改不了就如实报 section unparseable。② 新增启动期只读体检 staleWiringCheck():MCP/hooks 里若写着已不存在的 node 路径、或条目与当前版本不一致,就在自动沉淀日志里留一条 warn(dsh-memory setup 可修)—— 没人会天天跑 align,静默失效要变成可见。③ 本机验证:align 三端接线全部 ✓(修前 codex 一直报「node 路径与当前不一致」)。npm test 共 379 项 |
| v0.10.38 | 2026-10-17 | 接管成功即释放 claim(修 0.10.35 的副作用)+ #45 真机 A/B 验证数据。0.10.35 的 claim 在「助手变成常驻看门狗」这条路径上永远不会被释放(那个进程不退出),于是接管完成后 60 秒内的下一次 restart 会被自己挡成 in-flight(本机实测踩到)。现在 bootWatchdog() 之后立刻 releaseReplaceClaim() —— 互斥窗口严格等于「接管进行中」。#45 真机验证(同一台机、同一组 20 次 /version-check):0.10.36 → 9 成功 / 11 拒连,TIME_WAIT 24→1976(+1952 ≈ 每请求 100 条连接 = 递归深度),首帧 2389ms;0.10.37 → 20/20 成功,TIME_WAIT 1969→1971(+2),18–34ms。npm test 共 376 项 |
| v0.10.37 | 2026-10-17 | 修 #45(我 0.10.36 引入的严重回归:/version-check 自我探测递归被重试放大)+ #44(node 路径硬编码 / 僵尸看门狗误判)+ #43(跟随会话的官方字段)。① #45:selfPort() 就是本进程自己的端口,而 /version-check 会用 probeServedVersion 再向它发一条 /version-check → 处理器 → 自己 → 处理器…… 深度只受超时约束、每层各开一条连接 → listen backlog 打满 → 间歇 ECONNREFUSED(17–37ms)、TIME_WAIT 静置 40 秒涨 4402、served 反成空串;0.10.36 的「重试 3 次」把它乘以 3。现在自探一律直接作答(probedSelf:true,不发 HTTP),/restart-self 同一道闸门;/version-check 也支持 ?port= 问别的端口。② #44:nodeBinary() 缓存命中前验存在性,execPath 失效(brew 升级删掉旧目录)时回退 PATH —— 长命 watchdog 从此自愈;inspectResident 新增 portServing,decideResidentAction 遇「登记说 watchdog 活着、端口却没人应答」判 restart(watchdog-port-dead)而不再 delegate(那正是「沉淀静默停摆、日志只写 delegated」的现场);status 打印实际用的 node 与存在性。③ #43:sessionRouteFrom() 改为 async,按官方字段优先取 session.requestHeader().config → agent.options → 旧嗅探兜底。npm test 共 375 项 |
| v0.10.36 | 2026-10-16 | 把两种新状态接到所有入口(align / doctor)+ 修一处测试卫生。① align 新增一条结论:常驻实例由宿主托管(锁不可写 → 看门狗路径被绕过,改由宿主停旧起新并保活)—— warn 级,明确写「实例是有人管的,不是孤儿;锁恢复可写后自动交还看门狗」(align.ok 不受影响,warn 不算失败);② dsh-memory doctor 增印一行 常驻实例托管:(看门狗 / 宿主托管 / 不可替换(环境限制)),配合已有的 journal 模式、写探针、锁可写性、备份清单,一次看清这台机器处于哪种状态;③ 测试卫生:/restart-self 的 alreadyCurrent 用例依赖开发者本机真实的锁文件(我本机锁里只剩一条 endedAt 记录,于是它不再判 alreadyCurrent、用例翻红)—— 把「读锁」做成可注入(deps.readWatchdogLock),用例自己注入一条活登记,从此与开发机状态无关。npm test 共 371 项 |
| v0.10.35 | 2026-10-16 | 接管助手单飞互斥(#30 第六轮实测:宿主 tick 与 CLI 各拉一个 --replace 互相踩)。报告者观察到失败的 restart 期间同端口上同时存在两个互不相识的接管助手(父 = DSH 宿主 / 父 = CLI),谁都不写锁、还互相收对方的 web —— 现场更难复盘。修法:新增 claim 文件(memory-eternal-replace.claim,与看门狗锁同目录)—— ① 助手开工前独占抢 claim,抢不到就直接退出(把接管让给已经在跑的那个),并在退出时释放;② restartResident 发现接管进行中(持有者活着且 <60s)就不再拉第二个助手,返回 stage「in-flight」并说明是谁在跑、多久前启动;③ 失败开放:受限环境写不了 claim 时照常接管(不能让权限问题把接管彻底堵死),但把原因写进返回值 / 日志。测试覆盖:先到先得 / 活持有者让位 / 过期与死 pid 可接管 / 只清自己的 claim / restartResident 的 in-flight 分支。npm test 共 370 项 |
| v0.10.34 | 2026-10-16 | 治本:锁不可写时由「宿主」自己托管常驻实例(采纳 #30 报告者第四点建议)。他的诊断:那台机器上「登记」恰好落在唯一写不了锁的那个进程上 → 自动替换被永久拒绝。修法:① 宿主的 autoWebMode=interval 保活循环不再只探存活,还探端口上服务的版本 —— 落后且锁可写 → 仍交给看门狗 / 漂移路径(宿主不抢);落后且锁不可写 → 宿主自己接管(ensureWebServer({forceTakeover:true}):只收掉自家 web、在同端口起新实例、由宿主保活);② 新增可测试的 takeoverOwnWeb()(安全闸门:只对 looksLikeOurWeb 为真的占用者下手,别人的进程一个都不动);③ 「宿主托管」记成状态(recordHostManaged)→ /version-check 报 resident.managedBy='host',面板显示「宿主托管」而不是「未托管 / 孤儿」;看门狗重新登记后自动清除。npm test 共 366 项 |
| v0.10.33 | 2026-10-15 | 蒸馏供应商:动态下拉 + 跟随当前会话(尽力)+ 仅试首选 + 测试按钮(issue #42 第一刀)。① 动态下拉:新增只读接口 /llm-routes(宿主侧的 listProviders/listModels),配置页的「蒸馏 provider / model」从手填编号变成下拉选择(取不到时自动回退成文本输入);② 跟随当前会话(他的最高优先级):新增配置 captureFollowSession,开启后宿主蒸馏先尝试用这一轮会话的 provider/model(sessionRouteFrom() 按 agent / session / session.route / session.options 逐个嗅探),读不到就回退显式配置并记一条日志(DSH 未保证会话对象暴露 route —— 这条日志就是以后补适配的依据,不猜);后台 / 无会话任务(CLI、watchdog、hooks)天然只能回退配置;③ 仅试首选 + 测试按钮:captureFirstOnly 让选路只取第一个候选、失败即停(避免「先打官方 → 白烧一次调用 → 再轮换」);POST /llm-test 用真实调用路径打一次小请求,回显 provider / model / 耗时 / 候选数 / 错误码,也是 ①② 的验证手段。④ 顺带修一处探测抖动:probeServedVersion() 现在退避重试 3 次(报告者实测「同一时刻 version-check 200、2 秒后 status 说没有服务」),单发探测不再被重启窗口/瞬时拒绝骗到。npm test 共 363 项 |
| v0.10.32 | 2026-10-15 | 「常驻实例不可替换」变成长期状态,而不是每次开机重试(#30 第五轮复测:A: 盘的稳态)。报告者实测 5 天 44 次「版本漂移」,机制是每一步都按设计工作、合起来永远换不掉:升级 → 端口上仍是上一代 → 宿主探测漂移 → 拉起 --replace 助手 → 助手前置探到 lock.writable=false → 正确地拒绝(0.10.29 的设计,零破坏)→ 旧实例继续存活 → 下次开机再报一次。修法:① 新增 lib/resident-state.js:把「不可替换(环境限制)」记成状态(原因 / 锁路径 / 端口 / 版本 / 次数,7 天有效);② 宿主在重启前先探锁可写性,不可写就不再重试注定失败的替换,只记一条 —— 首次 warn、之后每次开机降级为 boot 级「已知状态」(报告者原话:不想每次开机都看到一条 warn 永久持续);③ 措辞与「漂移」显式分开:「常驻实例不可替换(环境限制)」 + 出路(修权限后 restart,或 dsh-memory watchdog --port N 前台观察);④ 面板 / /version-check / align 都能看到这个状态(resident.replaceBlocked;align 里是一条 warn 级结论,环境修好后自动清除)。npm test 共 359 项 |
| v0.10.31 | 2026-10-15 | restart 在「端口枚举很慢」的机器上 100% 失败的根因修复(#30 第三/四轮复测,报告者定位到行)。机制:--replace 助手的顺序是「清场 → 端口空了才写锁 → 启动看门狗」,而清场里 Get-NetTCPConnection 实测要 4–13.6 秒且不受 8 秒预算约束 → 助手永远走不到写锁那一步 → 锁里什么都没有 → waitForTakeover(要求「锁里出现新的活看门狗」)必然超时 → CLI 与面板按钮在这类机器上 100% 报「(无响应)」,与版本号无关。修法:① 清场失败也必须留下登记(acquireWatchdogLock({force:true})),现场不再出现「端口有 web、锁里空白」;② acquireWatchdogLock 支持 force(--replace 的语义就是替换同端口那个,不许让位 —— 否则助手在清场没跑完时先退出);③ 0.10.30 已给枚举执行器加超时(defaultExec 真掐断子进程、findPortListener({timeoutMs}) 逐层传递、清场给 ≤2.5s 有界值);④ CLI 的 restart 结果真的写进自动沉淀日志(提示里一直这么说,但 CLI 路径过去不写)。他还实测到「两个接管助手互相赛跑」(宿主 tick + CLI 各拉一个)—— 登记 + force 之后这种赛跑的危害大幅降低(后到者接管登记,而不是双双留空)。npm test 共 355 项 |
| v0.10.30 | 2026-10-15 | 两处「让整页不可用」的修复:畸形 tags 黑屏(#41)+ 接管链路的两个真病灶(#30 第三/四轮复测)。① #41:/write 传字符串 tags(如 "[\"a\",\"b\"]")被 writeCard 直接 JSON.stringify → 双重编码落库 → 读出来仍是字符串 → 前端 card.tags.slice(...).map 抛错 → React 根组件挂载失败、整页黑屏(重启 DSH / 插件 / 常驻实例都无效,因为坏数据一直在库里)。现在 normalizeTags() 是唯一入口:数组 / JSON 字符串 / 逗号串 / 双重编码的旧数据都收敛成数组,写路径与所有读路径都走它(既有坏数据不必用户手工修);渲染层再加兜底(坏 tags 最多少显示几个标签,不许把整棵树带走)。② #30:报告者在我 0.10.29 里又抓出两处 —— restoreWatchdogLock 的「活登记」判定少了 !w.endedAt(PID 复用/正在退出时,写回会被放弃);Windows 端口枚举没有超时(实测 Get-NetTCPConnection 无条件要 4–7.5 秒、一次 13.6 秒,而接管清场的 8 秒预算根本管不住它 → 决策基于过期数据)。现在 defaultExec 支持超时并真的掐断子进程、findPortListener({timeoutMs}) 逐层传下去、接管清场给有界值(≤2.5s)。新增 tests/tags-normalize.test.mjs(4 项)+ watchdog-restart 2 项回归。npm test 共 354 项 |
| v0.10.29 | 2026-10-14 | 失败的接管不再破坏现场(issue #30 复测的关键发现)。报告者在 0.10.26 上实测:restart 失败后锁文件被删除、端口上还是同一个 web —— 现场从此看起来像「从来没有过常驻实例」,只剩一个孤儿。四处修复:① 释放锁不再删文件(releaseWatchdogLock 留一条带 endedAt 的「已结束」记录,status 会显示「已退出(陈旧记录)」;这种槽永不阻挡新实例,pid 复用也不怕);② 失败的 restart 把替换前的登记写回去(restoreWatchdogLock,只在本端口没有活着的登记时才写);③ 枚举不到占用者时不再误判「端口已空」——clearResidentForTakeover 用 TCP 复核(受限环境里进程查询被拒会返回空,旧实现据此认为端口空出 → 新 web 绑不上 → 自检「(无响应)」+ 锁被清空),还在监听就中止接管且一个登记都不动;④ 锁写不进去时前置拒绝接管(写不进锁 → 新实例无法登记 → 只会留孤儿;直接报错并给前台观察的出路)。另外 restart 失败信息补上「端口上现在是谁」+「锁有没有写回」,status 把「端口探测」与「锁登记」分段标注(报告者指出旧输出两者并列会误导)。npm test 共 348 项 |
| v0.10.28 | 2026-10-14 | 新增「版本对齐检查」:把「插件迭代后旧接口 / 旧接线还在」一次查清。插件升级只换磁盘文件,两类「旧」不会自己消失:① 运行时 —— DSH 宿主进程内存里注册的事件订阅(agent/turn-stopping、agent/inbox/claimed…)来自它启动时加载的那份代码,不重启 DSH 就会一直用旧接口(那条「轮次收尾事件疑似失效」多半就是它:本机实测宿主 v0.10.8 / 磁盘 v0.10.27,而且该告警文案里的「累计 135/75」本身就证明跑的是 ≥0.10.20 之前的旧判据);② 持久接线 —— 插件写进 ~/.claude.json、~/.claude/settings.json、~/.codex/config.toml、~/.cursor/mcp.json 的 MCP 与 hooks 条目含旧安装路径 / 旧 node 路径 / 旧参数,升级后不会自动改写。新增 lib/align.js + GET /align + dsh-memory align [--json] [--fix] + 面板「用量/今日 → 🔧 版本对齐检查」:逐条给结论与可执行动作(完全退出并重开 DSH / dsh-memory restart --port N / dsh-memory setup 重写接线 / 更新插件),--fix 只重写插件自己的键(不动数据与配置)。新增 tests/align.test.mjs(6 项,含「同一 node 但条目是旧写法」的措辞准确性守卫)。npm test 共 345 项 |
| v0.10.27 | 2026-10-13 | 「损坏时从备份重建」从引导升级成可执行(issue #31 的现实出路)。WAL 头一旦损坏,代码修不了数据 —— 上一版只能告诉你「有哪些备份、按什么顺序替换」,现在多了 dsh-memory doctor --restore latest|备份名:不带 --yes 只演练(一个文件都不动,先告诉你备份里有几张卡、会不会覆盖可写库);执行时四道闸门:① 校验备份(只读打开 + PRAGMA quick_check + 数卡片);② 当前库此刻完好时要求显式 --force(用当场探测判,不信缓存的体检结论);③ 检测到常驻看门狗还活着就中止(避免边写边换,除非 --force);④ 恢复后复核(重新打开 + 写探针 + 卡片数)。原现场一律改名 *.corrupt-<时间戳> 留证,绝不删除(库 + -wal + -shm 一起)。新增 tests/db-restore.test.mjs(4 项:坏库重建 / 完好库需 force 且演练不动任何文件 / 拒绝危险来源 / 体检结论刷新)。npm test 共 339 项 |
| v0.10.26 | 2026-10-13 | 撞锁改用「应用层异步重试」,而不是 busy_timeout(issue #31/#33 实测对症)。本机对照实验:外部进程持写事务时我方立刻拿到 database is locked,而且对方被强杀之后仍然锁着 —— 老实现没有忙等,一撞锁就永久失败(在 A: 盘那类受限 / 被外部占用的现场,表现出来就是「整个记忆库坏了」)。评审已否决 PRAGMA busy_timeout(node:sqlite 的 DatabaseSync 是同步 API,忙等会阻塞宿主事件循环、把整个 DSH 卡住),所以改成新增 withDbRetry():撞锁时让出事件循环睡一小会儿再试(线性退避,最多 3 次),只用写路由;「响应已发出」时显式禁止重试(e.noRetry,避免重复写),非锁类错误(数据 / 语法)只试一次并原样抛。测试覆盖:重试到成功 / 非忙错误只试一次 / noRetry 生效 / 试满仍失败 / 源码守卫(不许偷偷加 busy_timeout)。npm test 共 335 项 |
| v0.10.25 | 2026-10-13 | 「判不出来」不再冒充「未托管」+ 库不可写时的统一 UX(issue #33 第③层收口)。① 托管状态变三态:/version-check 增报 resident.managedKnown、lock:{path,writable,error} 与 pid —— 受限环境(沙箱禁写 / ACL / 非系统盘)写不进看门狗锁文件时,面板不再显示「未托管」(那会让用户以为实例坏了、反复重启),改为「托管状态未知:锁文件写不进去 → 无法判断;保活通常仍然有效」,并同时显示真实原因与路径;writeWatchdogLock 的失败也不再全静默(每个进程留一条可查的痕)。② 库不可写时统一 UX:写路由(delete / merge / card / refine-apply / optimize-execute / …)在入口短路径失败(503 + DB_UNAVAILABLE + 恢复指引),没枚举到的写路径由最外层兜底把 SQLite 原文翻译成同一套答复;面板跨标签顶部明示「记忆库当前不可写 —— 卡片读写会失败,但配置仍可保存」。③ dsh-memory doctor 增报看门狗锁可写性(受限环境一眼可见)。npm test 共 334 项 |
| v0.10.24 | 2026-10-13 | 「同目标、不同做法」也能归到一起了(issue #36 第三块,闭环)。前两版解决了「冗余合并」与「矛盾判别」,但最后一块 —— 同一目的、不同工作流/代码 —— 按正文相似度根本抓不到(「用 7z 压缩」「用 tar.gz 分发」「npm 包瘦身」字面差很远、目标却一样)。做法:先让模型给每张卡抽一句「这张卡解决什么问题」,再按目标聚类 —— 目标抽取结果缓存在 refine-goals.json(同一张卡只抽一次,第二次扫描 0 次调用),聚类用并查集;命中后按「同一目标 N 张卡」成组给出,动作二选一:都保留(记两两决策,下次不再提示)或合并这一组(走现有 /merge,新卡进审核队列)。全程只读,唯一写入是目标缓存。入口:面板「整理 → 🎯 同目标不同做法」+ API /refine-goals、/refine-goals-keep;CLI dsh-memory refine --goals --keep --path A --path B(聚类本身需要模型,在面板跑)。npm test 共 332 项 |
| v0.10.23 | 2026-10-13 | 存量「注入垃圾卡」可一键清理(issue #36 第二刀)。上一版修掉了新生成的注入垃圾卡(技能目录 / <system-reminder> 被当 user 消息 → 剥标签后仍被当知识沉淀),但存量还在:在真实库里一跑就找出 32 张「和对话毫无关系」的卡(- \code-review`: Review the changes… 这种目录行、用户system-reminder-you-are-teammate-c…这种碎片)。新增findInjectedCards()(只**提议**、不动数据;判据:含注入标签 / 技能目录条目占比 ≥30% / 剥离注入后只剩 <200 字 / 标题是碎片)与 purgeInjected()(**走回收站、可还原**,逐个报告失败)。入口:面板「整理 → 🧹 注入垃圾卡」(扫描存量 / 单张回收 / 全部回收)+ CLI dsh-memory refine --junk [--purge --path P] [--json];API /refine-junk、/refine-junk-purge。npm test` 共 331 项 |
| v0.10.22 | 2026-10-13 | 「矛盾扫描」:相似可以合并,矛盾必须人判断(issue #36 第一刀)。现状是 /optimize 只按相似度(≥0.55)盲合并 —— 而两张语义相近、结论互斥的卡相似度同样很高(实测 0.93),盲合并会把冲突悄悄「和解」掉,且 mergeCards 会永久删除源卡。新增 lib/refine.js + 客户端「整理 → 🧭 矛盾扫描」:① 宽召回(≥0.30,上限放到 1.0,特意把近重复那一段纳入 —— 那正是盲合并最危险的区间);② 模型只判「重复 / 补充 / 矛盾 / 无关」并给一句冲突点 + 建议保留谁;③ 人拍板:保留 A / 保留 B / 都保留 / 合并;扫描绝不写库,`keepA |
| v0.10.21 | 2026-10-13 | 「库不可用」从「静默运行」变成「看得清」(issue #33,按评审裁剪范围)。① 打开即体检:老实现执行完 PRAGMA journal_mode=WAL 就当成功(这条 PRAGMA 失败时不抛错,只把真实模式当一行返回),文件只读 / 目录不可写时插件会一路静默跑下去;现在打开后读回真实 journal 模式 + 真写一次探针(建表→删表,写主库而非 temp 库),结论记进 dbStatus(),不可写时立即把原因与排查方向打到日志。② /overview 降级返回:库/目录不可用时不再吐 500(500 会被探针、面板、脚本读成「这个端口坏了 / 不是我们的服务」,历史上正是重启风暴与端口漂移的入口),改成 200 + degraded:true + dbError + db:{journalMode,writable,error},并保住 vaultDir / version 身份字段 —— 「这是我们、但库有问题」从此可以被分开表达。③ 新增 dsh-memory doctor:库健康自检(真实 journal 模式 / 写探针 / 备份清单 / 恢复引导),带 --json 可脚本化;不可写或疑似损坏时给出只引导、不动数据的步骤(先停写者 → 复制现场留证 → 查备份 → 替换 → 复核)。刻意不做(评审结论,已写成测试守卫防止顺手加回):busy_timeout(DatabaseSync 是同步 API,撞锁会阻塞宿主事件循环)与 WAL→DELETE 自动降级(会永久改变库模式,且 rollback journal 并发更差、可能反过来加剧锁竞争)。npm test 共 322 项 |
| v0.10.20 | 2026-10-12 | 「库不可用 → 看门狗自杀自拉 → 端口漂到 8006」三层现场一起修(issue #31 / #33,AkiMei0721 实测)。① /overview 非 200 ≠ 端口死了:/overview 会碰数据库,库只读 / WAL 损坏时它返回 500,而看门狗据此判「离线」→ 走 spawnWeb()(开头先 SIGTERM 掉自己的上一个 child)→ 自杀自拉、端口从 7999 一路漂到 8006(6 个实例);新增 decideTickAction() 纯决策:只要 TCP 还能连就视为存活(api-degraded-but-listening,并重置重启计数),新实例的就绪判定同样接受「已监听但 /overview 非 200」。② 版本探测改走 /version-check(不碰库;拿不到才回落 /overview)—— 病根是「端口上明明活着、版本却拿不到」,于是 verify 报「(无响应)」、status 甚至误判成「v0.10.4 之前的旧版」。③ 受限沙箱里不再崩:默认 stdio:'pipe' 会在 spawn() 时同步抛 EPERM(老实现只在 'error' 事件里兜底,根本没机会执行)→ dsh-memory watchdog --reap 直接崩、进程枚举永远空名单;现在 try/catch 包住 spawn,并用临时文件接 stdout、完全不碰命名管道。④ 顺带给 stall 判据加「最短运行时长(30 分钟)」:刚落进程必然 0 收尾,不该据此报「收尾事件失效」(实测重启 1.5 分钟后就误报过)。新增 tests/issue-fixes-8.test.mjs(4 项)+ tests/stall.test.mjs 的 too-early 用例。npm test 共 318 项 |
| v0.10.19 | 2026-10-12 | UNSUPPORTED_REASONING_EFFORT 不再让整条蒸馏熔断(issue #34)。现场(zafir-zhong 等):很多网关在能力自述里说支持 reasoning effort,真把 reasoningEffort: 'off' 发出去才回 UNSUPPORTED_REASONING_EFFORT(实测:workbuddy/hy3、FallbacksChain/Auto、new-api-resp/gpt-6-luna),于是整个候选被判失败、换下一个 provider —— 用户的自动沉淀直接停摆(#37 / #38 / #40 的诊断信息里都能看到这条 FAIL)。既有设计只覆盖「provider 自述不支持 → 不传该字段」(#15),缺了运行时这一半。修法:新增进程内记忆 markEffortUnsupported()\/isEffortUnsupported() —— 首次被拒时不带该字段重试一次(同一候选、同一 provider),并记住这个 provider/model,此后同一进程内它第一次请求就不再带 effort;记忆按 provider/model 分开,不误伤别的 provider。回归守卫加在 tests/capture-route.test.mjs(第一次带 → 被拒 → 第二次不带并拿到卡 → 后续只发一次 → 别的 provider 不受影响)。npm test 共 313 项 |
| v0.10.18 | 2026-10-12 | 「保存配置」被无条件拒绝的修复(issue #35)。现场(Dzifxazy):全新安装、什么都不动、直接点保存 → HTTP 400「字段不能为空:secretHint」,整单被拒,用户改的数字与开关一个都存不进去;文案还会把排查方向误导到「某个数字格子没填」。根因:宿主侧的前置校验把任何空字符串都当成「数字没填」,只豁免一份硬编码名单(vaultDir / captureProvider / captureModel / recallEmbedding / activeVault)—— 而 secretHint 的默认值就是空串(z.string().default(''))又不在名单里;面板提交的是整张表单,于是每次保存必然命中。修法:新增 emptyNumericFields(patch, Config.dict),按 schema 的 type === 'number' 判定「哪些键被清空」,彻底删掉硬编码名单(以后新增可空字符串字段不需要谁记得去补)。新增 tests/config-validate.test.mjs(2 项:空串只对 number 字段报「没填」——里面放了一个「不在任何名单里」的新字符串字段做回归钉子;源码守卫不许退回名单)。npm test 共 312 项 |
| v0.10.17 | 2026-10-12 | 「常驻实例在服务、却没人托管」这类状态自相矛盾的根治(issue #28)。现场:用户升级到 0.10.16 后常驻实例没起来,面板既没有重启按钮也没有任何提示(横幅只在 vc.stale 时渲染,而宿主与端口版本号一致时 stale 为假),只看到一条「自动沉淀异常」;本机复现出另一半:stop 之后端口上还活着、锁里却空了、status 说「没有登记的看门狗」—— 因为端口上那个 web 是宿主 ensureWeb() 直接拉起的(锁里没有 webPid),stop 只杀了看门狗。五处一起修:① /version-check 报出 resident.managed(锁里有活着的看门狗才算被托管)—— 「在服务」≠「被托管」;② 面板:常驻实例没在跑或没被托管时都显示重启按钮与专门文案(「未运行」/「未托管」),不再只认「版本落后」这一种;③ /restart-self:alreadyCurrent 必须同时满足「版本一致 且 被托管」,否则照常替换(孤儿 web 版本再新也得重新托管);④ 显式 dsh-memory stop 把端口腾出来(forcePortOccupant: true,闸门不变:只终止 looksLikeOurWeb 认得出是自家的进程);⑤ 自检改为「新实例接管」:restartResident 不再只看「端口服着期望版本」(升级前后版本号常常相同、旧实例本就是这个版本 → 自检会被旧实例骗过,实测打印「自检通过(等待 0s)」后紧接着 status 说没有登记的看门狗),新增 waitForTakeover():必须「版本一致 且 锁里出现一个不在替换前 pid 集合里的活看门狗」才算成功,失败原因也据此分离成「版本不对」与「没人接管」。本机同一现场实测:替换前 41672/17432 → 替换后 28136/31976,restart 等待 13s 后自检通过,紧接着的 status 立刻显示新看门狗已登记(不再矛盾)。npm test 共 310 项 |
| v0.10.16 | 2026-10-09 | --reap 的输出不再「明明收了却说没收」(alario-tang 反馈,macOS + 0.10.13 实测)。现场:kill -9 掉 watchdog 造出 PPID=1 的孤儿 web 后跑 dsh-memory watchdog --reap --port 7999,端口确实被释放、web 确实被收掉,屏幕上却写着「扫描到 1 个 watchdog 进程,清理孤儿 0 个」—— 因为 0.10.13 让 scanned 含了 web、而 webKilled 一个字都没打印。修法:新增纯函数 formatReapSummary() 统一口径(扫描数拆成 watchdog A / web B、清理项按类分别列出 pid),reapStaleWatchdogs 返回值补 scannedWatchdogs / scannedWebs(scanned 仍是两者之和,兼容旧调用方),CLI 改用它。测试补 2 项(收了 web 时不许再说「清理孤儿 0 个」/ 扫描计数必须拆开),并在 tests/watchdog-orphan-web.test.mjs 头部记下复现要点:这类孤儿只能用 kill -9 造 —— SIGTERM 会走 watchdog 的优雅退出、把它拉起的 web 一起停掉(与 #19 里「Windows 上 SIGTERM 即无条件终止」是同一枚硬币的两面)。npm test 共 308 项 |
| v0.10.15 | 2026-10-09 | 三处「升级留下的尾巴」。① 推荐方案 A 不再把刚省掉的调用装回来:v0.10.12 把「蒸馏输出上限」默认提到 4000(省掉「先撞 2000、再以 4000 重跑同一候选」那次白烧),但面板里「一键推荐配置 A(轻量)」仍写死 captureMaxTokens: 2000 —— 用户点一下就又把重试装回来;现已与默认对齐,并加源码守卫(tests/csrf-origin.test.mjs 断言 planA 的上限 === 默认值)。② README 的「停滞兜底」描述与新判据对齐(中英两份):旧文案写「15 分钟收不到收尾事件就报」,现在是「本次运行至今 0 收尾 + 最近 20 分钟内持续有轮次开始 → 记一条 warn」,并写明正在进行的长回合不会被误判。③ 顺带核实日志里另一种措辞 轮次收尾事件未触发(agent/turn-stopping 没到) 最后出现于 2026-09-24,属于旧判据的历史残留,现行代码里已无这条路径(无需修复)。npm test 共 306 项 |
| v0.10.14 | 2026-10-09 | 「自动沉淀异常:轮次收尾事件疑似失效」误报根治。现场:几十分钟的长回合里,agent/inbox/claimed 在一轮对话中途也会多次触发,而 turn-stopping 要等回合真的结束才来 —— 于是 20 分钟窗口必然「开始 ≥3、收尾 0」,判据把还在进行中的轮次当成「事件被改名」,直接把红警报推给用户(本会话实测那条「累计 39/18」就是这么来的)。修法:加一条硬前提 —— 「事件被改名/移除」是进程生命周期属性,不是窗口属性:本次运行里只要收到过哪怕一次收尾,通路就是好的,窗口内 0 收尾只说明当前轮次还没结束(evaluateStall 新增 everStopped,并在返回值里给出 `reason: wiring-proven |
| v0.10.13 | 2026-10-09 | 孤儿 web.js 终于收得掉了(#11「待做 1」的 POSIX 分支)。现场(alario-tang,macOS arm64):watchdog 先死 + 锁文件丢失 → 端口 7999 上留着一个 PPID=1 的旧 web;stop/restart 读锁槽找不到目标、watchdog --reap 只认 watchdog.js、面板按钮又走同一条链路 → 漂移只能报不能自愈,用户被迫手动 kill。查出两个具体缺口:① POSIX 侧端口占用者只拿到进程名 —— lsof -Fpcn 的 c 字段是 node 而不是命令行,而「要不要收」的判据(looksLikeOurWeb)要求命令行含 memory-eternal 或本机 web.js 绝对路径 → 判据恒假 → stopWatchdogs / clearResidentForTakeover 的兜底循环一次都不杀(Windows 走 Get-CimInstance 拿完整命令行,所以本机从未暴露);现在补一次 ps -p <pid> -o args=,拿不到才退回旧行为(宁可不杀、也不误杀),并让 platform 可注入以便单测。② --reap 扩到自家 web:新增 listWebs 分支,把「同端口 + 是自家 web + 托管者已死(该槽 watchdog 不存活)+ 不在保护集合(keepPid / 当前 pid / 父进程 / 带 --reap 的同类)」的 web 一并回收;托管者还活着的 web 一律不动,别的端口、别人的 web.js 一个不杀。新增 tests/watchdog-orphan-web.test.mjs(5 项),并把两条只谈 watchdog 的旧用例显式声明 listWebs: [](否则 reap 会去列本机真实进程表)。npm test 共 302 项 |
| v0.10.12 | 2026-10-09 | 两件「不显眼但每天都在扣钱 / 留洞」的事。① 蒸馏输出上限默认 2000 → 4000(= 硬顶):日志实测高频出现「输出撞到 maxTokens=2000 上限 → 以 4000 重试同一候选」,每一次都是白烧一次 LLM 调用(先撞顶、再拿 4000 重跑同一个候选)。maxTokens 只是上限、按实际生成量计费,所以默认直接放到硬顶:同样的轮次一次调用就够;maxTokenLadder 仍保留给显式把 captureMaxTokens 调小的用户。② 插件 API 加跨站写入闸门:/memory-eternal/api/* 无鉴权,本机任意网页都能 POST /config、/audit/approve、/write… 新增 crossOriginError() —— 状态变更请求(非 GET/HEAD/OPTIONS)必须与请求自身的 Host 同源:浏览器跨站请求一定带 Origin(与 Host 不同源即 403 CROSS_ORIGIN_BLOCKED);同源页面(DSH 设置页/记忆页、独立页)与局域网访问都同源不受影响;没有 Origin 的 curl / CLI / MCP 照常放行;app:// 这类桌面壳自定义 scheme 也放行以免误伤。宿主自己的拦截路由(/config POST、/restart-self、/mcp/action…)与共享 api 入口都装了这道闸门。新增 tests/csrf-origin.test.mjs(4 项:判定矩阵含局域网 / 无 Origin / null / app://、跨站 POST 被 403 且不进业务逻辑、两处入口都在、默认上限=硬顶且无 ladder 重试)。npm test 共 297 项 |
| v0.10.11 | 2026-10-09 | 蒸馏输出解析再补一层容错:字符串里的裸引号(Expected ',' or '}' after property value)。现场:fail 日志里 UNPARSEABLE_OUTPUT 占大头(最近 600 条里 103 条),最近一条带全了现场 —— JSON.parse 原始报错:Expected ',' or '}' after property value in JSON at position 1913,摘录里能看到正文写着 运行 "npm test" 时…:模型把引号当正文直接写进字符串,于是它被当成字符串结束符。这与 #24 修过的「裸控制字符」是不同的一类(控制字符在字符串内非法,引号只是需要转义)。修法:新增 repairUnescapedQuotes() —— 一个 " 后面(跳过空白)紧跟 : , } ] 或文本结尾才算字符串结束,否则当正文补 \";它只作为最后一个候选参与解析,且必须仍通过卡片体检(标题合格 + 正文 ≥20 字)才被接受 —— 宁可不修,也不写坏卡。回归守卫 tests/capture-json-repair.test.mjs(4 项:合法 JSON 恒等变换、裸引号现场、控制字符与裸引号叠加、截断仍如实失败)。npm test 共 293 项 |
| v0.10.10 | 2026-10-09 | 审核中心能看正文了 + 就地批准/驳回(issue #27)。现场(thinrflbtlm):审核中心过去只列「标题 / 类型 / 来源 / 严重度 / 原因」,看不到正文;而 v0.10.0 之后待审卡物理隔离在 quarantine、知识卡界面只列已审核卡 —— 于是「不人工确认就没法审」,审核形同虚设。① 每行新增 ▸/▾(点标题也行)就地展开 frontmatter + 正文,用与卡片详情同一套 Markdown 渲染(renderMd + splitFrontmatter),不是裸文本;② 正文按需拉取(`GET /card?path=…&status=pending |
| v0.10.9 | 2026-10-09 | 升级期端口漂移把记忆页打成「拒绝连接」的根治(issue #29)。现场:旧包遗留的 detached 实例占着 7999 且不自报版本 → 宿主 ensureWebServer() 认不出它、向后漂到 8000 并写进 web-info;而插件的自愈随后把 8000 上的实例当野实例收掉 → web-info 永久悬空,客户端 iframe 指向死端口(首次能进、刷新即「拒绝连接 127.0.0.1」)。① 记忆库页改走 host 同源壳:宿主新增 /memory-eternal/ui/app(与配置页 /ui/config 同一份 index.html,其中 <script src="app.js"> 相对路径正好解析到 /ui/app.js),iframe 地址从此与「常驻实例监听哪个端口」彻底解耦(旧宿主 404 时仍回落 web-info)。② /web-info 实时收敛:每次读之前先探「配置端口 → 记录端口」,谁在服务就用谁,都不通就如实 alive:false(不再把死地址当活的);自愈替换成功后与 interval 保活探到活时都就地回写。③ 不再盲目漂端口:新增 looksLikeOurWeb()(宽松识别「自家但不自报版本」的占用者:/overview、/web-info、/budget 任一 ok:true 即认),疑似自家时先给自愈留 6s 接管窗口,等不到才漂并留日志。④ 体检退避重试:inspectResident() 一次探针打空不再直接判「旧版不自报版本」(那个结论会触发误替换,每次启动都白起一次实例),退避重试后才下结论。新增 tests/issue-fixes-5.test.mjs(4 项:宽松识别真/假阳性、等自愈接管不漂端口、宿主与客户端源码守卫)。npm test 共 287 项 |
| v0.10.8 | 2026-10-08 | 独立 Web 端保存「点一下即生效」:把应用 pending 的上下文从插件回调搬到宿主 HTTP 请求(方案 A,实测驱动)。现场:独立页保存 → 宿主连续重试 5 次后 dropped,报错恒为 HMR transactions cannot be nested(0.10.7 只修掉了它冒充「自动沉淀异常」与升级期未知键,同步本身没修)。实测定位(同一宿主、同一个 settings.update):从插件回调(setInterval / fs.watch)里调 → 抛守卫错且一个字节都没提交(只含宿主认识的键、值真的变了,同样失败);从插件 HTTP 请求处理器里调 → 真的提交(recallSummaryLen 130→132→130,revision 4→6)。所以修法不是换 API 而是换调用上下文:① 宿主新增 POST /memory-eternal/api/drain-pending,在请求处理器里应用 pending(两道闸门:只接受本机来源 + 心跳里的一次性令牌,防本机任意页面乱写);② 宿主把自己 web server 的监听端口 + 令牌写进心跳(webServer.port 是服务公开的 getter),独立端保存后据此主动叫醒宿主,拿到真实结果再回给界面(新增 savedApplied「已保存并生效」);③ 叫醒失败(旧宿主没这条路由 404 / 端口对不上 / 超时)不算错误 —— 回落到 0.10.5 的等待路径与「去 DSH 设置里改」的指路。新增 tests/host-drain.test.mjs(6 项:回环判定 / 守卫三件套 / 端到端叫醒 / 失败如实回报 / 404 回落 / 源码守卫)+ tests/e2e-host-drain.mjs(2 项:真起进程的端到端「独立页保存 → 叫醒宿主 → 宿主应用 → 回报已生效」,以及旧宿主心跳(无端口/令牌)时回落成「排队等待」)。npm test 共 283 项 |
| v0.10.7 | 2026-10-08 | 「启动 dsh 后运行中还是旧版本」根治:版本漂移自愈 + 一键重启常驻实例(#19 / #23 的正面解法)。现场:升级 npm 包只换了磁盘文件,常驻 web(默认 7999)比 DSH 宿主活得久 —— 端口上那个进程仍在跑「启动时加载进内存」的旧代码;而宿主激活时的策略是「同端口已有活着的 watchdog 就 delegated(不再 spawn、更不替换)」,于是重启多少次 DSH 都不会换掉它(实测本机:宿主 10:19 启动已是新版、设置页同源显示 0.10.6,而 7999 上的 web 是 08:15 启动的 0.10.0,/config 与 /version-check 各报一个版本)。① 启动自愈(autoRestartOnDrift,默认开):激活时先 inspectResident() 体检 —— 版本一律问端口(锁里的 pkgVersion 只是 watchdog 自述,旧实例写的还是空串,根本看不出版本漂移),再 decideResidentAction() 决策:一致 → delegate(保持 #19 的如实口径)、漂移 → restart、读不到本机版本 → 绝不乱动、刚替换过 → 防抖不重来(配置改动会让 effect 重跑)。「更新运行中的程序」的正确形态由此定为「用新代码重启那个常驻进程」,而不是热替换内存里的模块:ESM 模块缓存 + 闭包状态(settings / vault 路由 / SSE hub / fs.watch / 已监听端口)没有安全热换的余地,缓存失效重 import 只会得到第二份模块图(双实例、双监听、双写)。② 替换机制统一到一处:新增 restartResident() = spawn 一个 detached 的 watchdog.js --replace 助手(收旧 watchdog / 旧 web → 等端口真的空出来 → 抢锁 → 拉起新 web),再自检「端口上真正服务的版本 = 本机版本」(#23 建议 2);端口没空出来就中止接管、不退让到 port+1(退让实例正是孤儿 web 的诞生地)。dsh-memory restart、宿主启动自愈、面板按钮现在共用这一份实现。助手是 detached + stdio:ignore(stderr 没人看),所以接管结论必须写进「自动沉淀日志」—— 否则「重启了却没生效」又会变成一条无迹可查的静默失败。③ 面板一键修复:新增 POST /memory-eternal/api/restart-self,三条硬约束 —— 本进程很可能就是要被替换的旧 web,所以先回响应再调度替换;常驻实例已经是最新就回 alreadyCurrent(宿主面板点这个按钮时最常见,绝不为一个健康实例白折腾一次停机);同一时刻只允许一个替换在跑。④ 面板语义消歧:「运行中」改为「运行中(服务本页的进程)」(独立 Web 页里它是常驻 web,DSH 设置页里是宿主 —— 这正是两个面板数字「打架」的根源),并新增「常驻实例 vX」徽标:两者不同时一眼看出该重启谁;漂移横幅给按钮 + 等价命令,若常驻实例已新而本页仍旧,则明说「需重启桌面版 / DSH」,不再给一个点了也没用的按钮。⑤ updateAvailable 改按版本比较:latest !== onDisk 在本地跑未发布版本时会反过来劝你装回旧版(磁盘 0.10.7 / npm 0.10.6),现在用 compareVersions(含预发布语义 rc.2 < rc.10)。⑥ 顺带清掉客户端词典里重复的 saving 键(esbuild 每次构建都在报警告)。新增 tests/watchdog-restart.test.mjs(17 项:决策矩阵 / 体检问端口 / 替换自检 / 接管前清场不误杀 / 路由先响应后替换 + 并发闸门 / 版本比较)。⑦ README 改为中英两份独立文件:README.md 是默认的简体中文(npm 与 DSH 插件市场展示这一份),README.en.md 是英文镜像;顶部加语言切换与「改一处要同步另一处」的说明,并补齐英文版缺失的「环境自检」小节;新增 tests/readme-i18n.test.mjs 锁住结构 1:1(标题层级序列 + 代码块数量 + 互链 + 默认语言标记),防止以后只改一份。⑧ secretHint 凭证提示 + 凭据读取守卫:填一句自己的约定就会被注入每个会话的 systemPrompt,让 agent 需要 API key / token 时先查记忆里的「密钥目录」卡(记忆库只存目录:名字/位置/取用方式,值留在本机凭据库);dsh-memory setup 同时注册 Claude Code 的 PreToolUse 守卫(拒读 .credentials.yaml / .env / *-token 并指路到加载器),并会刷新仍指向旧安装副本的 hook 路径。⑨ 发布前又抓到三个实测现场并修掉:① 面板「重启常驻实例」按钮在服务本页的进程是旧版时也显示 —— 点了必然 404(旧宿主没有这条路由),用户只看到一句「未知接口」;现在按钮只在「端口上那个常驻实例落后于磁盘」时出现,404/405 还会给一条「重启桌面版 / DSH 或执行 dsh-memory restart」的指路。② 独立页(新版)提交整张表单、宿主还是旧版时:宿主按白名单丢掉它不认识的键(如 secretHint)→ 回读校验必然对不上 → 连试 5 次 dropped,用户只看到「HMR transactions cannot be nested」;现在应用前按宿主 schema 过滤未知键(宿主升级后自然生效),并把「配置同步失败」与「自动沉淀异常」彻底分开(config-warn / config-fail,不再翻红沉淀健康状态)。③ autoRestartOnDrift 在宿主快照里没有这个键时按默认开渲染(实测现场:0.10.6 宿主 + 0.10.7 面板 → 复选框空着,而宿主实际按开处理,面板与真实行为不一致)。新增 tests/issue-fixes-4.test.mjs。npm test 共 274 项 |
| v0.10.6 | 2026-10-07 | 孤儿 web 有了回收机制 + 共享配置同步失败不再静默。① 孤儿 web:web 子进程是 detached 启动的,watchdog 被硬终止时(Windows 上 process.kill(pid,'SIGTERM') 即无条件终止)它的退出处理器不执行;若这个 web 当初因为目标端口被占而退让到 port+1…,登记它的锁条目又随 watchdog 一起消失 —— 于是 8001/8002 这类端口上会常驻没人认领的 web(实测本机就躺着两个,其中一个已跑了 20 小时,而 status 只探配置端口,永远发现不了)。现在 startWatchdog 启动时、以及 stop/restart(checkPort 路径)都会扫退让窗口 [port+1, port+9] 并收掉它们;三道门缺一不可(端口在窗口内 / 命令行确实是本插件的 web / pid 不在锁里任何 watchdog 的 pid 或 webPid 上),别人的 node web.js 一个都不杀。另外 spawnWeb 发现目标端口上已有自家 web 时不再重复拉起(退让实例的主要来源),只交给 tick 探活。② syncConfigFile() 不再静默吞错:它写的是独立页 / hooks / MCP 共用的读源,写不进去就表现为「配置改了但他们看不到」——旧实现是 catch { /* 静默 */ }(实测到过一次:共享文件 mtime 停在激活前,而宿主里的值已经变了)。现在失败会进 stderr 与「自动沉淀日志」的 warn(同一原因只报一次,不刷屏);因为 logCapture 定义在 apply() 更靠后的位置,日志刻意延迟一拍再写,避开 const 的 TDZ。新增 tests/watchdog-orphans.test.mjs(7 项),npm test 共 245 项 |
| v0.10.5 | 2026-10-07 | 独立 Web 端的配置保存:不再空头承诺,也不再要求「必须装了 DSH」。① 宿主无关的直写路径(#21):独立端保存后若 800ms 内没人消费 pending 文件、且本机没有活着的 DSH 宿主(心跳文件 memory-eternal-config.host.json 不新鲜、或里面的 pid 已死),就直接把改动原子合并写进共享配置、清掉 pending,响应回 pendingOutcome: applied-direct —— 于是 Codex / Claude Code / Cursor、以及只跑 dsh-memory serve 的用户,保存当场生效(此前那条路只会写「待应用」等一个根本不存在的宿主,界面上那句「下次启动生效」永远不会兑现)。宿主活着时绝不直写:共享配置是宿主的派生镜像,绕过它会被下一次 syncConfigFile() 盖回去(有专门的反向测试守着这条边界)。② 失败要指路,不只是重复报错(#21):识别「宿主守卫类」报错 —— HMR transactions cannot be nested(Cordis HMR 宿主)与 root.events.emit is unavailable from a plugin activation(dsh-tui 一类)是同一族缺陷的两种措辞 —— 并把「此宿主不允许插件从自己的回调上下文写配置」与「去哪改」(DSH 设置 → 记忆,或 profile 的配置补丁层)写进放弃时的报错、诊断信息与独立页提示;POST /config 同时回 hostGuard / hint 字段。③ 界面不再一律弹「已保存」(#21):客户端开始消费 pendingOutcome —— queued / failed 用非成功样式并说明「尚未确认生效」,applied-direct 明确写「本机没有 DSH 宿主,已直接写入共享配置」,只有真生效才显示「已保存」。④ 顺带修掉一个会让宿主/测试无法退出的清理竞态:心跳定时器与 pending 文件监听都是在异步 import() 之后才建立,若在它 resolve 之前就 dispose,回调就再没人清理(心跳定时器还额外 unref())—— tests/settings-compat.test.mjs 用假 ctx 跑 apply() 时正是被它挂住的。新增 tests/issue-fixes-3.test.mjs(9 项),npm test 共 238 项 |
| v0.10.4 | 2026-10-07 | 四个「看着正常、实际静默错位」的 issue 集中修复(#21 / #22 / #23 / #24)。① 宿主守卫误报的保存不再被当成失败(#21):dsh-tui 一类宿主的能力守卫会在 settings.update 内部的 describe() 上抛 root.events.emit is unavailable from a plugin activation,而写入其实已经提交 —— 旧实现只认「有没有抛错」,会把已生效的改动重试 5 次后标成 dropped、日志里还报失败。新增 applyPatchVerified(抛错后回读确认,判据 patchApplied 浅比较,命中则记一行日志按成功处理),drain 与独立 Web 端共用这条路径;独立 Web 的 POST /config 也不再无条件回「一切正常」:等最多 800ms 回读 pending 文件,回 pendingOutcome: applied / failed / queued 并给对应措辞(waitPendingOutcome)。② /cards 的 status 不再静默错位(#22):status=deleted 现在真的列回收站(与 /recycle/list 的数字对得上),未知取值显式回 unknownStatus: true 并回显 appliedStatus / requestedStatus(回落 approved 的老行为保留,但不再沉默)。③ 升级后以「端口上真正服务的版本」为准(#23):/overview 新增当前服务进程加载的 version;dsh-memory status 单独报「端口实际服务版本」,对不上就直说「升级尚未生效」(锁里的版本号只是 watchdog 自述;端口上是 v0.10.4 之前的旧 web 时探不到版本 —— 它的 /overview 还没有 version 字段 —— 这时会明说「是本插件的 web 但不会自报版本,即旧版」,而不是误报「没有服务在响应」);stop 停完再问一次端口占用者、仍被占就告警;restart 不再只信锁里登记的 webPid —— 按端口占用者兜底收掉锁里没登记的旧 web(先用命令行确认确实是本插件的 web(包名 memory-eternal,或本插件自己的 lib/web.js 绝对路径)才动手,别人的 node web.js 只告警不动手 —— 收紧这条判据是刻意的:误杀别人占着该端口的进程,比不杀更糟),端口没空出来就中止重启,起完自检「端口服务版本 = 本机版本」,对不上以非 0 退出(不再打印「已启动」就完事)。④ 蒸馏解析不再把「字符串里的裸控制字符」误判成无法解析(#24):模型把 markdown 正文的换行写成真实 U+000A(括号闭合、looksTruncatedJson 为 false)时,旧实现必然 JSON.parse 失败 → 整卡退成原文卡;新增 repairJsonControlChars 只在字符串内部把裸控制字符转成合法转义(\n \r \t \b \f,其余 \u00XX;字符串外的换行是合法 JSON 空白,一律不动),候选串顺序为「原样 → 截 { … } → 各自的已修复版」;解析失败的消息不再吞掉 JSON.parse 原始报错,现场摘要改为首尾各留一段(describeOutputExcerpt,旧的 slice(0,160) 恰好把「被切在 "body": 半截处」显示得像输出断了);撞输出上限的重试从「只翻倍一次」改为 maxTokenLadder 一路翻倍到 schema 上限(1200 → 2400 → 4000,不再止步 2400)。新增 tests/issue-fixes-2.test.mjs(21 项),npm test 共 229 项 |
| v0.10.3 | 2026-10-06 | 修复 audit list 的计数语义(#20)。① 总数与 --limit 解耦:共 N 张 取匹配总数(countCards),--limit 只限制显示条数,截断时明确写出 显示前 M 张 … 还有 X 张未显示。② 默认不再静默截断(不给 --limit、或 --limit 0 = 全部显示)。③ --json 拆分为 total(匹配总数)/ returned(本次返回)/ count(= total 的兼容别名;0.10.2 里它误等于 returned)/ limit(null = 全部)/ hasMore。④ --status all 的语义写进用法与 README:= 审核队列(pending + rejected),不含已出队的 approved 与回收中心的 deleted;approved 可显式查询。⑤ 同源修复 MCP 的 memory_audit_list:它同样存在「默认 20 静默截断 + 把截断条数报成待处理数」,现改为报匹配总数 + 未显示提示,上限 100 → 500。 |
| v0.10.2 | 2026-10-06 | 五个存量 issue 集中修复(#15 / #16 / #17 / #18 / #19)。① 按项目选库在宿主侧终于生效(#15-1):宿主过去拿进程 cwd 去匹配 vaultProfiles[].match.workspace,而那是「启动 dsh 的目录」——现在从 session.header.cwd 取会话自己的工作区(capture 与 memory_recall 两条路径都传),CLI/MCP/hooks 行为不变。② 蒸馏不再选错 provider(#15-2):声明 supportsReasoningEffort:false 的候选不再被传 reasoningEffort(能力未知一律按支持处理),并把它们排到支持者之后但不删除,避免兜底全无。③ 输出截断不再伪装成解析失败(#18):新增独立错误码 MAX_TOKENS(含实际上限值),撞上限自动以双倍上限重试同一候选;兜底上报改为始终以第一候选为主因 + 其余错误按码汇总(不再被最后一个候选覆盖);默认 captureMaxTokens 900 → 2000;失败兜底的原文卡加 distill-failed 标签便于过滤。④ 独立 Web 端保存配置不再静默丢失(#16):drain 报错不再被 catch {} 吞掉(进 stderr + 自动沉淀日志,5 分钟去重节流),共享配置 memory-eternal-config.json 改为 tmp + rename 原子写(原先非原子写会在截断窗口内被独立 web / MCP 读到半截内容 → JSON.parse 失败 → 设置静默回落成默认值),重试耗尽后保留 pending 文件并标注 dropped:true + lastError(不再删除用户改动),失败状态进 /diagnostics 与 /config 响应。⑤ 审核中心有了 CLI(#17):dsh-memory audit list/approve/reject(多路径批量、--json 便于 jq 筛选、复用同一套 setCardStatus 守卫与 audit_log);MCP 侧只新增只读的 memory_audit_list,不暴露 approve/reject。⑥ 看门狗有了生命周期(#19):新增 dsh-memory status/stop/restart,锁文件写入 pkgVersion(status 会提示「常驻实例是旧版」),宿主日志按抢锁结果如实打印 spawned / delegated to existing pid(不再每次都谎报 spawned),关闭 watchdogAutoSpawn 时明确提示既有实例仍在跑;并且 dsh-memory stop 会连同该 watchdog 拉起的 web 一起停(Windows 上 process.kill(pid,'SIGTERM') 是无条件终止,watchdog 的退出处理器根本不会执行 —— 旧写法会在 stop 后留下一个占着端口的孤儿 web;web 子进程 pid 现在记在锁里,杀之前还会用进程命令行确认确实是 web.js,防 pid 复用误杀);README 修正「默认值自相矛盾」与「重启即可关闭」的错误承诺。 |
| v0.10.1 | 2026-10-02 | 修复「导出 OK、导入 0 张」。根因:客户端「导出JSON」写出的是裸数组,而 /import 只读 payload.cards —— 裸数组被静默当成空备份(ok:true / imported:0 / skipped:0),UI 只显示「导入完成:0」,看不出是格式不匹配。① /import 现在同时接受裸数组(v0.10.0 及以前的备份)与信封对象 {format, formatVersion, exportedAt, count, cards};形状不认识 → 400 + 人话原因,空文件 → warning,坏 JSON → 原有的解析失败提示;② 导出改为带信封(老版本只认 payload.cards,信封对老版本也互通)并保留每张卡的 status / store;③ 导入去重基线改为导入前快照(writeCard 新增 dedupAgainst)—— 否则同一份备份里互相近似的卡会在写入过程中互相判重、被自己人吞掉一批(备份是真相源);④ 导入应答补 total / skipped / quarantined / failed[](含每条未导入原因),提示语改为「导入完成:N 张 / 文件共 M 张 · 待审核 K · 跳过 D」,一张都没进来时按失败样式提示;⑤ 新增 tests/import-roundtrip.test.mjs(6 项:裸数组 / 信封 / 导出→导入全量往返 / 重复导入如实报重复 / 坏形状 / 审核守卫不可绕过)。⑥ 装坏了也要说人话(issue #14):从插件市场装出来的副本可能缺 web/ 静态资源,那时侧边栏「记忆」只会弹一页 {"ok":false,"error":"ENOENT …"} —— 现在缺 index.html 但 app.js 还在就用内置外壳把界面拉起来(自救),缺 app.js 则回一段把诊断画进 #root 的 JS + 一页含「缺哪个文件 / 绝对路径 / 包版本 / 怎么重装」的说明;两种缺失都写进自动沉淀日志(app.js → fail 健康态亮红、只缺 index.html → warn),宿主启动时也自检一次(判据与措辞由新增的 lib/web-assets.js 统一,附 tests/web-assets.test.mjs 6 项)。⑦ 顺带修 appendCaptureLog 在 DSH_HOME 目录不存在时静默失败(诊断信息连一条都留不下)。⑧ DSH 兼容范围放宽到 >=0.1.5-alpha.2 <0.3.0-0:旧上界 <0.2.0 按 semver 会把 DSH 0.2.0-rc.x 排除在外(DSH 守卫用 includePrerelease:true,实际装得上,但声明与事实不符)——现在覆盖整个 0.1/0.2 线,<0.3.0-0 仍挡住 0.3.0 及其预发布;dsh.compatibility 同步补 "0.2.0-rc.2": "compatible"(本机在官方桌面版 0.2.0-rc.2 上实测运行)。⑨ 修三个第三方插件清单长期非法 JSON:.claude-plugin / .codex-plugin / .cursor-plugin 的 description 收尾引号被双重编码吃掉(最后改动停在 v0.9.23),DSH 不走这三个文件所以一直没暴露,但 Claude Code / Codex / Cursor 侧安装必然解析失败;现已重写为合法 UTF-8 JSON、version 跟到 0.10.1,并新增随包体检守卫 tests/manifests.test.mjs(3 项:随包 JSON 全部可解析 / 三清单 name+version 与 package.json 一致 / 随包文本无编码事故乱码)。实测用户 661 张真实备份(裸数组、3.9MB):修复前导入 0 张 → 修复后 661 张(654 进主库 + 7 进隔离区)。npm test 共 187 项 |
| v0.10.0 | 2026-09-30 | 两套存储:未审核内容物理隔离。① cards(主库)只存 approved,新增 quarantine(隔离区,含 quarantined_at / quarantine_reason)存 pending/rejected/deleted;② 召回/检索/图谱/去重/导出等读路径不再需要状态条件(查主库即安全),根治 v0.9 审计出的三处绕过审核漏洞(去重池把新知识写进待审卡、readCard 直读未审核正文、/card 无状态校验);③ 审核流转改为跨表搬家(事务内完成,id 由新增的 card_sequence 单一发号器分配,card_updates 随卡改绑);④ 首次启动自动迁移存量非 approved 卡(不删内容,幂等);⑤ 新增 checkMainStoreInvariant() 不变量体检与 tests/two-store.test.mjs(15 条);⑥ 入料噪声闸门:剥离 DSH 运行时注入(环境快照 / team 广播 / teammate 原文 / 工具说明),挡住碎片卡与糊标题;⑦ 修 settings-compat 在 Windows 上的原生崩溃(fs.watch 父目录 → 改监听文件) |
| v0.9.23 | 2026-09-30 | 保存不再闪烁。保存成功后客户端会立刻重拉 /config,而宿主 volatile 回流是滞后的 → 输入框会「先退回原配置、再跳回修改后的值」。现在客户端把「已保存但宿主未回显」的字段做本地叠加(新增 src/client/config-merge.js 纯函数 + 4 项单测),宿主回显一致后自动摘除,之后仍跟随宿主权威值;重置表单会清空叠加层。npm test 共 139 项 |
| v0.9.22 | 2026-09-30 | 配置准即时同步(SSE 推送 + 文件监听)。① 新增 SSE 推送中心 lib/sse.js:DSH 内嵌宿主与独立 Web 宿主都暴露 /memory-eternal/api/events,配置一变更就推给所有已打开的页面,客户端订阅后自动重载 —— 正在编辑时不覆盖你的输入(dirty 标记)。② 独立页保存 → DSH 应用:从 5 秒轮询升级为 fs.watch 毫秒级触发(5 秒轮询保留为兜底)。③ 独立页监听共享配置文件:DSH 端一保存,独立页立刻同步。新增 tests/sse.test.mjs(5 项),npm test 共 135 项 |
| v0.9.21 | 2026-09-30 | 保存成功不再被误读为失败 + 配置页排版修复。① 保存成功一律用成功样式提示(原来「已写入;宿主尚未回流」被弹成红色失败样式,用户以为保存失败),措辞改为「已写入配置文件;宿主仍是旧版,重启 DSH 后即以新值为准」;② 配置页字段网格列宽 150/160px → 200/220px、间距加大、长中文标签允许换行,勾选框与说明文字顶端对齐,字段不再互相挤压。 |
| v0.9.20 | 2026-09-30 | 独立 Web 页补取值范围校验:/config 镜像 Config schema 的 min/max(如 recycleRetentionDays 1–3650、webPort 1–65535),越界直接 400 并说明「需要 1–3650,当前 0」。至此「数字框清空 / 越界」这条最常见的保存失败链路在客户端 + 独立页 + 宿主三层都被拦住并给出字段级原因。 |
| v0.9.19 | 2026-09-30 | 「保存失败」不再无信息量(根因:数字框清空=0 被 schema 拒绝,宿主回空 500)。复现与实测:越界值 / 清空数字框 / 类型错误 → DSH 宿主返回 HTTP 500 + 空响应体 → 客户端 r.json() 抛错 → 只能显示通用「保存失败」。修复:① 数字输入框清空不再静默变成 0(JS 里空串转数字得 0,而 recycleRetentionDays 的 schema 是 min(1),直接失败);② 保存前按 schema 范围逐字段自检,指名报错(例如「recycleRetentionDays」需要 1–3650 之间的数字,当前 0);③ 响应体兜底解析:非 JSON / 空响应体时显示 HTTP 状态码与原始片段,而不是「保存失败」;④ 宿主 POST 外层兜底,绝不返回空响应体,并对空字符串数值字段前置返回 400 + 字段名;⑤ 独立页 /config 增加类型与范围校验(拒绝空数值、类型不符),避免写入非法「待应用」值;⑥ drainPendingConfig 增加失败计数,连续 5 次失败后放弃并删除(不再每 5 秒无限重试)。npm test 共 130 项 |
| v0.9.18 | 2026-09-30 | 独立 Web 页终于能保存配置 + 只读状态不再「毫无反应」。根因(issue #12 的另一半):独立 web server 进程没有 DSH 的 settings 服务,它的 /config 过去固定返回 writable:false,客户端 save() 第一行就 if (readonly) return,按钮还是 disabled —— 所以点保存一点反应都没有,与插件版本无关。修复:① 新增共享文件协议 lib/config-sync.js:独立页保存 → 原子写 memory-eternal-config.pending.json → DSH 端激活时与每 5 秒轮询应用(settings.update(patch, undefined) 跳过乐观并发校验),成功后删除,失败保留重试;DSH 没运行时改动留在文件里,下次启动生效。② 独立页 /config 返回 writable:true, viaPending:true,页面顶部显示蓝色说明「保存会写入待应用文件,由 DSH 自动同步」。③ 只读时按钮不再禁用:点击必弹出明确原因(🔒 只读 + 指引「桌面版 → 设置 → 记忆」),彻底消灭「按了没反应」。新增 tests/config-sync.test.mjs(4 项:读写往返、损坏内容容错、drain 成功即删/失败保留、clear 幂等)。npm test 共 125 项 |
| v0.9.17 | 2026-09-30 | 「更新了却还是旧界面」根治。① 构建期把插件版本注入客户端 bundle,配置页新增「页面脚本 vX」徽标;② 当页面脚本版本 ≠ 磁盘版本时醒目提示「⚠ 当前页面仍在运行旧版脚本(页面 v{page} / 磁盘 v{disk}):按 Ctrl+F5 刷新页面即生效」;当「运行中(宿主加载)」落后于磁盘时,提示语也改为「先刷新页面,宿主半未更新才需重启桌面版 / DSH」。起因:issue #12「保存配置没有任何反馈」的真实原因之一,就是页面里跑的还是更新前加载的旧 JS —— 宿主已设 Cache-Control: no-store,但已打开的页面不会自动重新加载脚本,不刷新永远是旧的。另:独立 web 服务(:7999,9/29 启动的旧进程)已重启到新版并验证 loaded=onDisk=latest |
| v0.9.16 | 2026-09-30 | 修复配置保存不生效(issue #12)+ 配置项全覆盖。① 保存链路:宿主 dsh-settings.write() 要求「面板持有的 revision 与 describe 完全一致」,revision 一过期就抛 SettingsConflictError 导致保存失败 —— 现在遇到冲突会自动取最新 revision 重试一次(提示里标 ♻)。② 宿主 volatile 回流滞后:写成功后把改动叠加在本地视图(面板重开立刻是新值),等宿主快照追上再自动摘除;宿主 POST 新增写入回读校验,未回流时返回 pending 并提示「已写入,重启 DSH 后以配置文件为准」。③ 保存即时反馈(你此前反馈过):保存按钮旁直接显示 ⏳ 保存中 / ✅ 已保存 / ⚠ 错误,并标明是否重试/是否回流;按钮文案修正(原来误显示「导出中…」)。④ 配置全覆盖:补齐 7 个以前只能手改配置文件的字段 —— 总开关 enabled、单库目录 vaultDir、蒸馏路由 captureProvider / captureModel、沉淀冷却 captureCooldownMs、语义召回 recallEmbedding、会话预算 sessionBudgetChars;宿主 /config 补 6 项、独立 web /config 补 9 项。⑤ 新增永久守卫测试:tests/config-coverage.test.mjs 保证每个 Config 字段都被「宿主 API / 独立 web API / 配置页」三处覆盖(漏一个就红),tests/settings-save.test.mjs(5 项)钉死 revision 重试、回流兜底与「错误不吞」。npm test 共 121 项 |
| v0.9.15 | 2026-09-29 | 只带服务器侧图谱提速(渲染与 v0.9.13 完全一致)。① 建图落盘缓存:按指纹缓存 nodes+edges 到 <vault>/memory-eternal-graph-cache.json(原子写、指纹失效、超 8MB 不缓存、写失败只影响下次重建),596 卡实测冷启动 422ms → 11ms;② bigram 哈希化 + 稀有 gram 计数排序:bigramHashSet() 把 bigram 压成 32 位整数入 Set<number>(写卡去重仍用字符串版,判定语义不变),稀有倒排改按 df 计数排序(202 → 54ms);③ MinHash + LSH 候选生成(48 维签名 / 24 band,与稀有倒排取并集后精确校验):jaccard 调用 15,148 → 6,797,相似度段 210 → 130ms。全量冷建图 600 → 422ms,边数与 v0.9.13 一致(tag 2777 / similar 4)。客户端零改动:不做精灵/静态层/骨架降级(v0.9.14 那三项因影响手感已回退并 deprecated)。新增 lib/minhash.js + tests/minhash.test.mjs(4 项),npm test 共 108 项 |
| v0.9.13 | 2026-09-28 | 按用户反馈修 7 项。① 版本跟踪:配置页「插件信息」同时显示运行中(宿主加载)/ 磁盘安装 / npm 最新三个版本;磁盘比运行中新时给橙色警告(「磁盘已安装 X,但当前进程仍加载 Y —— 重启后才生效」),并新增「🔄 检查更新」(服务端查 npm,10 分钟缓存 + 4s 超时)。② 复制失败:记忆弹窗在 iframe 里、默认没有 clipboard-write 权限,navigator.clipboard.writeText 必然被拒 —— 现在先试 Clipboard API,失败退 document.execCommand('copy'),再失败就把内容摊进只读文本框并给「全选」;iframe 加 allow="clipboard-write; clipboard-read"。③ 保存没有提示:保存结果显示在保存按钮旁(⏳ 保存中… / ✅ 已保存 / ⚠ 错误),文案从错误的「导出中…」改为「保存中…」。④ 回收中心:恢复 / 彻底删除 / 清空都有成功与失败提示,空库给说明与禁用态。⑤ 通用按键反馈:审核中心的批准 / 驳回 / 删除原先 catch{} 吞掉一切,现逐条统计成功失败并提示;未选卡片也提示。⑥ 图谱缩放卡顿:新增 src/client/graph-lod.js —— 视口剔除(屏幕外节点/边不画)+ 标签预算(≤120 全画 / ≤300 画 160 / ≤600 画 90 / 更多画 60,优先焦点、悬停、搜索命中、高度数)+ 文本宽度缓存 + 大图关阴影。⑦ 新增 tests/graph-lod.test.mjs(5 项),npm test 共 108 项 |
| v0.9.12 | 2026-09-28 | 新增「反馈异常」入口(左栏 🐞):弹窗里填完问题描述后可 ① 一键打开 GitHub 预填 issue(标题 / 正文 / 诊断信息 / bug 标签都已填好,再点一次 Submit 即提交),或 ② 复制一段给 AI 的提示词(贴进对话框,让 AI 先用 gh issue list 查重、再建 issue 并回报链接)。为什么不做「点一下直接提交」:GitHub 不允许匿名创建 issue;插件内置 token 会被反编译提取;OAuth / 中转需要自建服务 —— 所以走「预填 + 一次点击」。诊断信息由新接口 /memory-eternal/api/diagnostics 生成并自动脱敏(home 目录 → ~,sk- / ghp_ / github_pat_ / api_key: 等 → ***,截断到 2400 字符),issue 链接用二分截断保证不超长(URLSearchParams 会把中文膨胀约 9 倍)。新增 lib/feedback.js(纯函数层)与 tests/feedback.test.mjs(4 项:脱敏 / 诊断字段与限长 / 预填链接编码与截断 / 中英双语提示词),npm test 共 103 项 |
| v0.9.11 | 2026-09-28 | 修复 watchdog --reap 把自己杀掉:直接运行 node lib/watchdog.js --reap 时,调用者自己的命令行同样含 watchdog.js + 同端口、且不在锁里 —— reap 于是把自己 SIGTERM 了,表现为「一条输出都没有 + 退出码 1」(走 dsh-memory watchdog --reap 时因命令行不含 watchdog.js 而侥幸没事,所以只在直跑时暴露)。现在保护集合固定包含 keepPid / 当前 pid / 父进程 pid,并跳过命令行带 --reap 的其它进程;输出改成 fs.writeSync(2, …) 同步写,不再被 process.exit 截断。回归测试补「自己与父进程都不得被杀」(用真实空转子进程当靶子,不再用假 pid)。npm test 98 项 |
| v0.9.10 | 2026-09-28 | 多库与看门狗收尾 + 图谱帧优化。① 多库(#10):vaultProfiles / activeVault 终于有了配置页表单(增删库、填目录、选激活库,并显示当前生效目录 / 来源 / workspace);vault 解析抽成 lib/vault-resolve.js,宿主与 CLI / MCP / hooks / 独立 web / sweep 共用同一份,新增 vaultProfiles[].match.workspace 按项目目录自动选库(最长前缀优先、opt-in,默认仍是单库);memory_recall 增加 scope 参数(库名 / 路径前缀 / all 跨库聚合),跨库结果标注来源库。② watchdog(#11):锁改为按端口分槽(多端口 / 多 vault 并存不再互相覆盖),新增 dsh-memory watchdog --reap 回收升级前遗留的孤儿进程(只清同端口、且不在锁里的;锁中活跃实例与用户手动起的都不动)。③ 图谱帧时间:边改为按「颜色 + 透明度 + 线宽」分桶后用 Path2D 一次描边(2.7k 条边从 2.7k 次绘制调用降到几个桶),节点径向渐变改为按 颜色|半径 缓存 + translate 复用(593 次/帧 → 首帧之后基本不再创建)。④ 新增 tests/vault-resolve.test.mjs(7 项)、看门狗测试扩到 7 项;npm test 共 98 项 |
| v0.9.9 | 2026-09-28 | 修复侧边栏「记忆」入口被挤出可视区(issue #2):槽位宿主有两种形态 —— 壳自己的横向行容器 .footerActions,以及被其他插件(dsh-diff-approval / dsh-footer-order 等)改成 flex-direction: column 的 div[data-slot="sidebar.footer.action"]。旧 CSS 对两者都套了 flex-wrap: wrap + .me-footer { flex: 1 1 100% }:纵向容器里 wrap 的语义是「换列」、basis: 100% 指的是高度 100%,叠加后按钮被挤进右侧溢出列、裁切到只剩右边缘一条缝。现在 flex-wrap 只留给横向 .footerActions,.me-footer 改用方向无关的 flex: 0 0 auto,并新增 SSR 回归断言。另:README 双语新增「交流群 & 打赏」(群二维码 + 微信收款码 / 赞赏码),新增 SPONSOR.md 与 .github/FUNDING.yml(FUNDING 只支持文字链接,故 custom 第一项指向 SPONSOR.md 页面直接展示二维码) |
| v0.9.8 | 2026-09-28 | 集中解决存量问题。① 采集可信性(issue #3 / #4):蒸馏路由不再写死 providers[0] —— 新增 captureProvider / captureModel 配置,并按注册顺序依次重试;流末尾 finish 块里的适配器错误(缺凭证 MISSING_CREDENTIAL、限流等)现在成为可见失败(沉淀日志写明 code/message + 健康态亮红 + 兜底原文卡注明原因),不再笼统记成「蒸馏无输出」;停滞探测从「累积计数差额」改为滑动窗口(20 分钟内持续开始且零收尾才告警),误报与漏报一并修掉。② 独立进程 vault 一致性(dsh-memory-eternal#5):CLI / MCP / hooks / 独立 web / sweep 与宿主共用同一套优先级(MEMORY_VAULT_DIR → activeVault → vaultDir → 默认库),切库后不再数据分裂。③ 看门狗单例锁(dsh-memory-eternal#6):同端口已有活着的 watchdog 时新进程直接退出并清理陈旧 pid 记录,止住「每次重启多一个孤儿进程」。④ 图谱性能:标签边改「共享标签数 Top-K」(593 卡实测 24,193 → 2,763 条边)、相似度改稀有 bigram 倒排候选(冷启动 948 → ~520ms,warm 2-4ms)、payload 3,531KB → 579KB;客户端 dim 判定改邻接 Set(每帧 52ms → <1ms)。⑤ 新增 5 个测试文件(路由/finish、停滞窗口、vault 解析、看门狗锁、图谱性能基线),npm test 85 项 |
| v0.9.7 | 2026-09-28 | 打开卡片默认全部展开(可收起)+ 正文排版重做:卡片阅读器不再默认折成 300px,而是打开即读全文,长卡可一键收起(折叠后底部给「展开 ⌄」提示)。frontmatter 从正文里抽出来,改成 kind / 审核状态 / 标签 / 时间 / 来源的彩色 chips(kind 沿用图谱配色),不再把一段 YAML 当正文露出来。正文排版换成新方案(src/client/markdown.js,宿主与独立 web 共用同一套):小标题按语义自动配 emoji + 颜色分组(结论 ✅绿 / 风险 ⚠️琥珀 / 根因 🔍蓝 / 方案 🛠️紫 / 示例 💡粉),连续 - 合成真正的 <ul>、1. 合成带圆形序号的 <ol>、- [x] 变复选框、> 引用升级成带 emoji 的 callout、--- 变分隔线、行首「短键:值」渲染成对齐样式、**加粗** 加荧光笔下划线;围栏代码整段保护,内部 # / - 不再被误排版。新增 7 项排版单测(含 XSS 转义、链接协议白名单、散文冒号不得误判成 key/value)。另含 PR #8 移植:Electron 宿主下的子进程 node 解析(lib/node-bin.js,修「独立 web server 拉不起来 → 侧边栏弹窗白屏」)与 webServer 就绪后再注册 API 路由(修「设置页永远加载中」) |
| v0.9.6 | 2026-09-28 | 修复官方桌面版全屏浮层挡住窗口控制面板(点「关闭」会退出整个桌面壳):桌面壳把「最小化 / 最大化 / 关闭」画在页面顶部,并按 DSH 约定在 <html> 上打 data-windows-titlebar + 给出 --dsh-windows-titlebar-height(已在官方桌面版 app.asar 里确认这两个标记确实存在);记忆库全屏浮层原本是 inset:0 + 内联 100vh,整条盖住标题栏,而我们右上角的「×」恰好压在窗口「关闭」上 —— 用户一点就直接退出整个桌面壳。现全屏态改用 .me-modal-full 类(不再写死内联 100vh:内联样式无法被 CSS 覆盖),并在 [data-windows-titlebar] 下把 .me-overlay-top / .me-overlay / .me-modal-full 一律下移 --dsh-windows-titlebar-height、高度改为 calc(100vh - 该高度);浏览器里没有该属性时偏移 0px,行为与之前完全一致。导出图谱的全屏预览同步从 100vh 改为 100%,避免在缩短后的浮层里溢出。新增 1 项渲染回归断言(三处让位规则必须出现在打包产物中) |
| v0.9.5 | 2026-09-28 | 修复官方桌面版(schemastery 3.18.4)下插件整体挂不上:桌面版 profile 解析到的是 schemastery 3.18.4,它会把标了 volatile 的 Config 字段解析成 cosmokit 的活引用({ get(): snapshot },品牌 Symbol.for('cosmokit.volatile.write')),而 0.9.4 的业务代码把字段当普通值用 —— cfg.vaultDir.trim() 当场抛 TypeError: cfg.vaultDir.trim is not a function,插件在 apply 里就崩(桌面版实测报错原文)。现按 cosmokit 协议统一深解引用(引用取快照、数组/对象递归展开),settings.get() 对外只给普通值;每次读取都重新解,loader 原地提交的 volatile 热更新照样立刻可见,JSON.stringify 写共享配置也不再变成 {}。新增 2 项回归(3.18.4 真实引用形态 + 嵌套数组/对象),并用桌面版真实模块解析布局(schemastery 3.18.4 + 真 cosmokit)跑通 apply 验证 |
| v0.9.4 | 2026-09-28 | 适配 DSH v0.1.7-rc.2(修复「升级后插件整体失联」):① 设置服务换血 —— 0.1.7 的 ctx.settings 只剩表单 API(describe/update/replace/mutate/configure),不再有 register();旧代码 ctx.settings.register(...) 直接抛 TypeError,插件根本挂不上(记忆页消失、自动沉淀停摆)。现改为跨版本兼容层:旧版仍走 register,新版读 loader 传进来的活配置引用、订阅 loader/volatile-update 做热更新、写回走 settings.update(条目 id, patch, revision)(revision 冲突映射 HTTP 409),并注册 configure({ auto:false }) 免得宿主再生成一张重复的表单页。② 字段 volatile 标记改为构造后统一打:不再链式写 .volatile() —— 该方法 schemastery ≥3.18.4 才有,而插件在 profile 层实际解析到的是 3.18.1,链式调用会在 import 期抛 volatile is not a function,把插件整个打挂。③ 客户端去死引用:inject 移除已随 @deepseek-ai/dsh-client-runtime 一起消失的 settingsScope(0.1.7 客户端服务表里已无此服务,插件会永远等待服务而永不激活 → 记忆页与侧边栏按钮全部不见);dsh.client.inject 同步去掉不存在的包名。④ 新增 tests/settings-compat.test.mjs(8 项:0.1.7 形状挂载 / 活引用读值 / volatile 热更新 / 写回与 revision 冲突 / 老版本 register 路径 / 字段 volatile 标记),已并入 npm test。已在真实 DSH v0.1.7-rc.2 上启动验证:宿主插件正常挂载(配置落盘 memory-eternal-config.json)、客户端 bundle 正常进入 boot graph(plugins/??memory-eternal/client.js) |
| v0.9.3 | 2026-09-12 | 修复记忆页白屏("没画面"):v0.9.1 把「滚动续拉」的 useEffect 写在了 loadCards 定义之前——React 在渲染期求值依赖数组,触发 TDZ(Cannot access 'loadCards' before initialization),整个记忆页一渲染就崩;现已移到定义之后。新增渲染冒烟测试(tests/ssr.test.mjs:真跑打包产物、把卡片/设置/用量/图谱四个入口各渲染一遍,已并入 npm test)——「构建全绿、页面白屏」这类 bug 以后发不出去 |
| v0.9.2 | 2026-09-12 | 日志可见性 + 误报修复:自动沉淀日志落盘持久化(~/.dsh/memory-eternal-capture.jsonl,最多 500 条)——独立 Web 页不再显示「日志仅在 DSH 宿主内可用」而是直接能看到记录,宿主重启后历史也不丢(启动时读回);修复停滞探测误报刷屏(轮次计数对不上其实是常态:子代理轮次、被取消的轮次都不发 turn-stopping;现在要求「有轮次在跑 且 15 分钟无任何沉淀活动」才报一次,活动恢复即自动撤销,不再每 10 分钟刷一条把面板挤满、健康状态永久亮红);蒸馏无输出/JSON 解析失败时改为原文卡兜底,内容不再白丢 |
| v0.9.1 | 2026-09-12 | 性能与体验批次:记忆页按视图懒加载(打开「设置」不再顺带拉 250KB 卡片 + 890KB 图谱,首屏只需约 0.5KB);知识图谱生成 3232ms → 43ms(bigram 集合只算一次 + 数学上界剪枝无损跳过 + 标签倒排),并加指纹精确失效的服务端缓存(写卡/审核/删卡立即重建,无脏读);全站 brotli q5(图谱 877KB → 54KB,比 gzip 再小 34%;卡片 246KB → 83KB → br 20KB),大响应体同时压 br + gzip 取更小者,保证任何响应都不比 gzip 差;/cards 支持真分页(offset/limit/total + 服务端标题排序,跨页全局有序),首屏只拉 100 条、滚到底续拉;「用量/今日」的 Agent 卡清单改为服务端署名过滤(30 条);审核红线:插件在 Agent 上下文注入「禁止调用 approve/reject 代用户审批」;watchdog 回收备用端口的多余实例(实测清掉一个常驻 76MB 的重复 web 进程) |
| v0.9.0 | 2026-09-12 | 修复自动沉淀静默失效(一连 6 天不写卡):DSH 升级后会话事件接口由 events 改为 ownEvents()/snapshotEvents(),监听器读不到事件后静默返回、不留任何痕迹 —— 现改为三级自适应取法(ownEvents() → snapshotEvents() → events)+ 每会话接入留痕;日配额改为只统计真实写卡(此前「不值得保存」的判定也照样扣配额,40 次即耗尽 24h 配额)、captureCooldownMs 冷却真正生效、单次沉淀输入截尾 20000 字;新增自动沉淀日志面板与异常主动提示(页面红条 + 注入 Agent 上下文 + 轮次收尾停滞巡检);移除「每日回顾」自动简报(定时器 / /todayBrief 接口 / UI 按钮) |
| v0.8.0 | 2026-09-06 | 全套 UI i18n:跟随 DSH 系统语言实时切换(中/英),覆盖知识卡 / 知识图谱 / 审核中心 / 配置面板 / 新建卡模板;Windows 测试收尾 SQLite 连接 EBUSY 修复 |
| v0.3.1 | 2026-09-03 | 知识图谱按需加载(点击时 fetch,关闭时注销) |
| v0.3.0 | 2026-09-03 | SQLite 化存储层(node:sqlite 零依赖)+ 数据库层审核守卫 enforceAudit() + audit_log 审计日志 + 自动从 .md 迁移 |
💬 交流群 & 打赏
遇到问题、想提需求、或者想聊聊 DSH 插件开发 —— 欢迎进群:
如果这个插件帮你省了时间,可以请作者喝杯咖啡(两个码任选其一):
| 微信收款码 | 微信赞赏码 |
|---|---|
![]() |
![]() |
打赏完全自愿,不影响任何功能;插件本体始终开源免费(MIT)。
仓库页的 Sponsor 按钮下拉里选 custom 第一项,会落到 SPONSOR.md(同样是这一页的码)。
📄 License
MIT
让 AI 真正记住你:对话自动沉淀,知识随手可查。 ⭐ 觉得有用就点个 Star,Let's make AI not forget.
English README: README.en.md
Yorumlar (0)
Yorum birakmak icin giris yap.
Yorum birakSonuc bulunamadi


