Skip to content

dsh-keep-memory

Verified

dsh-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,不写死任何人的安装路径:

  1. -DshCmd 参数
  2. 环境变量 DSH_CLI
  3. 从运行中的 DeepSeek Harness 进程推断(装在哪儿都能找到)
  4. 常见安装位置

都找不到时它会停下来,告诉你该传什么。手动等价命令(把 <项目目录> 换成你的路径):

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,把记忆库和常驻协议建起来

它会:

  1. 建出 INDEX.md / FACTS.md / PROTOCOL.md 骨架(已存在的绝不动)
  2. 在用户全局 ~/.dsh/AGENTS.md 里幂等补一节「跨会话记忆」常驻段 —— 这段每轮都注入,是"让 AI 知道有记忆库"的唯一入口
  3. 报告常驻段占用字节数(上限 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 验证过

授权

MIT