dsh-keep-memory
Verifieddsh-keep-memory · v0.5.1 · MIT · Web UI
跨会话记忆读写工具:位置可配置(含设置页面板)、绕过文件沙箱审批、把记忆模板固化成工具
Install
dsh plugin add dsh-keep-memory Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Published to npm without a public repository. Inspect the package contents before installing.
Tags
Creators
Readme
dsh-keep-memory
给 DSH(DeepSeek Harness) 用的跨会话记忆插件:
把记忆库的读写固化成八个 memory_* 工具,记忆位置可配置、不随会话工作区漂移,
并且在受限沙箱模式下不需要逐次授权。
第一次用? 看 GETTING-STARTED.md。 想知道它为什么这么设计? 看 DESIGN.md。 你是 AI / 想让 AI 正确使用它? 看 PROTOCOL.md(这份也会被写进你的记忆库)。
一分钟了解它是什么
DSH 的每个会话都是白纸开局:转录落盘是为了审计,模型再也看不到它。这个插件补上那一层—— 一个位置固定、跨会话、跨项目的记忆库,加八个让 AI 读写的工具。
<记忆目录>\
├─ INDEX.md 目录层:一天一行(日期|一句话摘要|关键收获)
├─ FACTS.md 稳定层:跨天有效、会变的事实;同名小节原地更新
├─ PROTOCOL.md 协议层:告诉 AI 该写什么、写到哪一层
└─ YYYY-MM-DD.md 当天层:一次性经历;只追加,永不覆盖
外加两个记忆库之外的文件:
~/.dsh/AGENTS.md 常驻段(约 1.1 KB,上限 2 KB)——让 AI 知道有这套记忆、怎么用
~/.dsh/dsh-keep-memory.json 记忆位置配置(可选,不写就用出厂默认)
它解决什么问题
DSH 的文件沙箱只作用于工具调用,其可写根是硬编码的:
function writableRoots(policy) {
if (policy.mode !== "workspace-write") return [];
return [...new Set([policy.workspaceRoot, "/tmp", tmpdir()].map(canonicalPath))];
}
—— 只有「会话工作区 + /tmp + 用户临时目录」,没有任何配置项可以追加路径,
也没有「记住这次授权」的持久白名单。
而记忆库放在工作区之外才合理(它要跨会话、跨项目复用)。于是产生矛盾: 合理的位置写不进去,能写的位置不合理,每次写记忆都得点一次同意。
解法:插件属于可信宿主代码,直接用 node:fs 读写、不经过 ctx.fs 那道围栏,
所以能把记忆读写做成"位置可配置、写入免审批"的工具。这不是钻空子——
DSH 自己的设计文档就写着:围栏拦的是「模型控制的路径」,「对抗性宿主进程不在范围内」。
装插件本身就是一次授权。
代价是插件写文件没有沙箱兜底,所以护栏必须比"提示词里的君子协定"更硬—— 见下面护栏,那些约束写在代码里,不是写在提示词里。
安装
pwsh -File .\install.ps1
脚本做三件事:语法自检源码 → 用官方 CLI 装进 profile → 打印确认。装完需要重启 DSH。
重启后,插件页里打开本插件,底部有一块「第一次启动」清单(实时显示记忆库与常驻段状态,两步操作带复制按钮)——从那里跟着走就不会漏掉初始化。
脚本免配置 —— 它按下述顺序自己找 dsh CLI,不写死任何人的安装路径:
-DshCmd参数- 环境变量
DSH_CLI - 从运行中的 DeepSeek Harness 进程推断(装在哪儿都能找到)
- 常见安装位置
都找不到时它会停下来,告诉你该传什么。手动等价命令(把 <项目目录> 换成你的路径):
dsh plugin --profile desktop add "<项目目录>"
从 npm 安装(包已发布到 npm 之后)——等价,只是来源换成 registry,不用先放目录:
dsh plugin --profile desktop add dsh-keep-memory
升级要显式指定版本,因为 profile 记下的 ^ 范围不会跨 minor:
dsh plugin --profile desktop add dsh-keep-memory@latest
插件页对第三方插件没有「更新」按钮,也可以卸载后重装;装完同样需要重启。
从 Gitee 源码安装(当前发布方式;需要本机已装 git,pnpm 要用它拉仓库):
dsh plugin --profile desktop add "git+https://gitee.com/yfymdkj/dsh-keep-memory.git#<40 位 commit>"
把 <commit> 换成仓库里的完整 SHA(锁 commit 可复现);图省事用 #master 也行,但会跟着分支走。仓库在 https://gitee.com/yfymdkj/dsh-keep-memory。
卸载(不会动你的记忆文件):
pwsh -File .\uninstall.ps1
装完必须做的一步 ⚠️
光装上插件,AI 不会知道有这套记忆系统,也就不会主动用它。让 AI 跑一次:
调
memory_init,把记忆库和常驻协议建起来
它会:
- 建出
INDEX.md/FACTS.md/PROTOCOL.md骨架(已存在的绝不动) - 在用户全局
~/.dsh/AGENTS.md里幂等补一节「跨会话记忆」常驻段 —— 这段每轮都注入,是"让 AI 知道有记忆库"的唯一入口 - 报告常驻段占用字节数(上限 2000)
之后随便开新会话,AI 都会带着这套协议。常驻段的文案就是 PROTOCOL.md 的浓缩版;想自定义可以拿 AGENTS-TEMPLATE.md 改。
八个工具
| 工具 | 作用 | 会写盘吗 |
|---|---|---|
memory_recall |
读入口:INDEX + FACTS + 当天状态(scope: index/facts/today/all) | 否 |
memory_note |
追加一条到当天日记,自动建文件、自动补 INDEX 行;给了 summary/takeaway 会一并写进索引行 |
是(日记仅追加;摘要只动 INDEX) |
memory_facts |
写 FACTS.md:同名小节原地替换 / 唯一锚点字面替换 | 是 |
memory_search |
跨所有文件的字面检索(日期倒序优先,含 INDEX 摘要行) | 否 |
memory_tidy |
整理建议;mode=apply 才补 INDEX 缺行 + 回填「(待补)」摘要 |
仅 apply(只动 INDEX,不碰正文) |
memory_check |
体检:字节、BOM、死链、孤儿文件、常驻记忆块字节数 | 否 |
memory_init |
一键建起 INDEX/FACTS/PROTOCOL 骨架,并给用户全局 AGENTS.md 补常驻段(已存在的绝不动;force=true 才覆盖那一段) |
仅缺失时 |
memory_config |
记忆位置:show 查看 / set 只改指向 / migrate 复制并切换 |
set、migrate |
摘要为什么重要:INDEX.md 是给人(和 AI)扫的目录层,只记"有什么、在哪、值不值得看"。
摘要空缺时目录只剩骨架,也就失去意义。所以:
- 推荐:写日记时带
summary(人写,能放可检索的关键词) - 兜底:
memory_tidy mode=apply从当天日记首行机械回填——适合救活历史遗留的空摘要, 但它会覆盖已有摘要,别对着人写过的摘要跑
配置:记忆存哪里
解析优先级(高 → 低)
| 顺序 | 来源 | 位置 |
|---|---|---|
| 1 | 设置文件 | $DSH_HOME\dsh-keep-memory.json(即 C:\Users\<你>\.dsh\dsh-keep-memory.json) |
| 2 | 环境变量 | DSH_MEMORY_DIR / DSH_PERSONA_DIR |
| 3 | 出厂默认 | 记忆 = $DSH_HOME\memory;人设 = $DSH_HOME(全局 AGENTS.md 就在那儿) |
空白字符串不算有效设置(防止手滑写成空路径而把记忆指到盘符根目录)。
为什么默认是 $DSH_HOME\memory,而不是「DSH 安装目录同级」:安装目录可能在
C:\Program Files(普通用户没有写权限),也可能随卸载/重装被清理或搬走。
$DSH_HOME 是 DSH 自己的用户数据目录(会话、凭据、插件都在这儿),一定可写,
且不随安装位置变化。
改地址
memory_config action=show # 看当前生效路径与来源
memory_config action=set memoryDir=<新路径> # 只改指向,不搬文件
memory_config action=migrate memoryDir=<新路径> dryRun=true # 先预演,一个字节都不写
memory_config action=migrate memoryDir=<新路径> # 复制 + 校验 + 切换指向
也可以直接在设置页 →「记忆位置」面板上操作(显示当前路径/来源/文件数, 三个按钮:只改指向 / 预演迁移 / 迁移过去)。
迁移的安全语义(都写进代码,不靠自觉)
- 原件一律保留 —— 永不移动、永不删除
- 目标同名且内容相同 → 跳过(幂等,重跑安全)
- 目标同名但内容不同 → 报冲突,绝不静默跳过 (静默跳过会让你在"搬过去又搬回来"之后,悄悄搬来一份过期数据)
- 冲突文件默认不覆盖;确认要用源覆盖时显式加
overwrite=true - 每个文件复制后立刻比对字节数,不一致记入失败清单
- 只要有失败或冲突,就不切换指向(仍指向原目录,绝不留"搬了一半"的中间态)
dryRun连目标目录都不创建,但照样报冲突(预演就该看见真问题)
护栏
写在代码里,不是写在提示词里。违反时的行为是明确的:
| 护栏 | 违反时 |
|---|---|
| 日记只追加,无覆盖 API | 能力上不存在"清空今天" |
| 只写当天 | 传过去/未来日期 → 直接拒绝并说明理由 |
| 一律 UTF-8 无 BOM | 写入天然不带,memory_check 事后核验 |
| FACTS 同名小节原地替换 | 不会出现两个版本的真相 |
| 替换要求锚点唯一 | 出现多次/找不到 → 抛错,文件原样 |
| INDEX 摘要唯一定位才改 | 缺行/重复 → 拒绝,不留半改 |
摘要清洗 | 与换行 |
不会撑坏 markdown 表格 |
tidy 默认 dry-run |
不写盘;apply 也永不碰正文 |
migrate 只复制不移动 |
有冲突或失败 → 不切换指向 |
它不会做什么(重要)
- 不会自动往记忆里写东西。每次都是 AI 明确调用工具才写。 判断"什么值得记"的是模型,插件只负责落盘、去重、原地更新、防格式崩坏—— 它只有"手",没有"脑子"。
- 不会在会话开始时自动注入记忆内容(这是 DESIGN 里的明确非目标)。
AGENTS.md常驻段提供的是操作协议,不是自动喂记忆。 - 不会动你已有的记忆正文。归档策略未定之前,
tidy只补索引、不改正文。
测试
node --test test/store.test.mjs test/search.test.mjs test/config.test.mjs test/integration.test.mjs test/tools.test.mjs test/settings.test.mjs test/routes.test.mjs
共 80 个用例,分七层:
| 文件 | 覆盖 |
|---|---|
store.test.mjs |
无 BOM 写入、追加语义、索引自动维护、FACTS 原地替换不吃掉邻节、唯一锚点替换的拒绝路径、AGENTS.md 常驻段的幂等插入与 force 覆盖边界、INDEX 摘要回填的唯一性/清洗/拒绝路径、手写索引行的判重/就地规范化/原文保留、proposeSummary 的表格安全 |
search.test.mjs |
检索排序与大小写、截断标记、片段裁剪、孤儿文件与死链识别、tidy 幂等 |
config.test.mjs |
本地日期(跨天/月末不串位)、路径解析与优先级、来源标记 |
integration.test.mjs |
四条端到端流程:初始化→记录→搜索→体检、tidy 幂等、被拒后文件原样、追加不乱序 |
tools.test.mjs |
工具层本身:八个工具的名字与说明、真实落盘、拒绝路径、report 不写盘、memory_init 补 AGENTS.md 常驻段(幂等 + force)、memory_config 的 show/set/migrate |
settings.test.mjs |
设置文件读写与容错(缺失/损坏/非对象一律回退)、优先级、迁移的复制/校验/跳过/预演 |
routes.test.mjs |
设置页面板用的 HTTP 路由:GET /config、POST /config(只改指向)、POST /migrate(预演/冲突不切换指向)、未知路由 404 |
tools.test.mjs 用 test/resolve-stub.mjs 把宿主包 @deepseek-ai/dsh-tools 解析到本地 stub,
所以不需要安装插件、不需要宿主就能测工具层。全部用例都在系统临时目录里跑,
并同时重定向 DSH_HOME 与 DSH_MEMORY_DIR(双保险),绝不触碰真实记忆库。
已知边界
- 记忆位置可配置;不配则用出厂默认
$DSH_HOME\memory - 插件写文件没有沙箱兜底 —— 这是免审批付出的代价,安全靠上面的护栏
- 改插件源码或装卸插件后需要重启 DSH 才生效(
link:形式不热更新已加载的模块) memory_init会写用户全局AGENTS.md;它对所有会话生效、每轮注入, 所以常驻段预算严格限制在 2000 字节(memory_check会核验)- GUI 设置面板需要宿主
webServer服务;headless profile 下只提供工具,不挂面板 - 安装脚本是 PowerShell,目前只在 Windows 验证过