Cross-session plaintext memory: deterministic BM25 keyword recall (no embeddings), memory_save/list/search/confirm/forget tools behind a human-confirm gate, and global + project-scoped JSON stores that follow your git repo.
Install
# from npm (prebuilt)
dsh plugin --profile web add @max-null/dsh-memory
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:Max-Null/dsh-memory
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).
README
This plugin publishes its README in Chinese only.
本插件属于 @max-null/* 插件系列——这一系列共同构成 SSID(思灵 · Seek Soul in Darkness) 桌面体验。SSID 是整合它们的盒:dsh-capture · dsh-chat-rail · dsh-chinese-thinking · dsh-draft-polish · dsh-guardian · dsh-habit · dsh-memory · dsh-node-appearance · dsh-plugin-center · dsh-quick-toolbar · dsh-skill-mcp-center · dsh-ssid-panels · dsh-ssid-zh-ui · dsh-achievements。
This plugin belongs to the @max-null/* family — a set of plugins that together form the SSID (思灵 · Seek Soul in Darkness) desktop experience.
一个面向 DeepSeek Harness 的跨会话明文记忆插件。遵循「一切皆插件」——它不修改 DSH 源码,声明 name/inject/apply,由 Loader 从 cordis.yml 加载。
设计原则
- 写入即生效,人是例外干预者:模型写入的记忆直接生效(
approved),不再逐条等人放行;人保留随时查看、改写、删除、钉住或回滚的能力——人不在场不等于失控。 - 可观测先于精准:每条记忆是明文,
memory_list随时可见、memory_forget随时删除——不存在"静默暗礁"。 - 明文是人机共享的审计窗口:记忆是可读文本,模型可自检其是否过期(有效性锚点会在所绑环境值变化后标记
stale),人可随时查看与改写。 - 确定性且缓存安全:BM25 关键词检索是存储的纯函数、无 LLM 调用;固定指引进 system-prompt section,
approved + injected记忆进 recall context(global 全量 + 当前会话工作区),逐条为单行摘要并按注入预算截断(超预算按最近更新优先取舍,省略数在面板可见)。
截图
装完后在设置里多出「记忆」一项,可查看与管理跨会话记忆:
入口: 设置 → 记忆
| 设置入口与面板 | 云同步配置(开关打开后) |
|---|---|
![]() |
![]() |
云同步那块的三步走:打开开关 → 填仓库地址与访问令牌(不知道填什么就留空,只做本机镜像)→ 重启生效。第一次用可以点「第一次用?看看怎么配」看四步说明,或者点「复制「让 DSH 帮我配」」把那段话粘到任意会话里让 DSH 带你做。
用法
npm install @max-null/dsh-memory
在你的 cordis.yml 加一条(其余 storage / system-prompt / tools 由宿主已有;记忆的存储后端由插件自己注册):
- id: memory
name: '@max-null/dsh-memory'
提供的服务与工具
- 服务
ctx.memory:remember/list/search/forget/update/move/setStatus/setInjected/supersede/retract/sweep/demoteStale/verifyAnchors/reload等 - 工具(14 个):
memory_save、memory_list、memory_search、memory_update、memory_confirm、memory_forget、memory_move、memory_sweep,提示词模板四件套prompt_search、prompt_get、prompt_list、prompt_add,加日记两件套diary_read、diary_write - 注入:
tool:memory指引 section(工具用法 + 常驻注入判据)+memory:self机制自述 +memory:recall召回 context(global 的approved + injected+ 当前会话工作区的approved + injected,带[memory:<id>:<namespace>]来源标记;摘要化 + 预算截断)+memory:diary指针(只有目录与最近日期,不含正文) - 检索:BM25(CJK 单字 + 2-gram,content 与 keywords 字段分离加权;中文多字查询精度显著优于单字切分);可选语义融合(见「可选配置」)
日记(第二套记忆,2026-10-24)
记忆与日记是两套东西,不是同一套的两个视图:
| 记忆 | 日记 | |
|---|---|---|
| 写什么 | 从中得出了什么 | 今天发生了什么 |
| 准入判据 | 有用性 | 无——写就是了 |
| 怎么被读到 | 检索命中 / 常驻摘要 | 整篇读 |
| 存储 | ctx.storage 里的 JSON 块 |
<DSH_HOME>/diary/YYYY-MM-DD.md |
日记正文永远不经过 ctx.storage:那条路的写入模型是 writer per process and last-write-wins,而日记不该被另一个实例的「读—改—写」周期吃掉。日记也不进检索、不占常驻注入预算——注入里只有一行指针(目录 + 最近日期)。
- 一天一个文件,按
## HH:MM分段;同日多场对话是追加,不覆盖。 - 工具:
diary_read(省略date读最近一篇)、diary_write(写一段)。 - 写进日记的是经历:发生了什么(有细节)、我怎么想(可以有情绪、可以没结论)、关于他(他说了什么、我注意到了什么)。不写任务清单(那是 git log),不写结论(那是
memory_save)。 - 提醒:指针行在距上次写日记超过 6 小时后附带一句提醒,同一次挂载只出现一次。
- 开关:插件页的「日记」开关(
diary.enabled,默认开)。关掉后两个工具都拒绝执行、指针不注入;已有 md 文件原样保留,不会被删。
设计依据、取舍与未决项见 docs/设计/2026-10-24-日记机制-设计方案.md。
两层存储(global / project)
记忆按 namespace 分两层物理存储,各落在独立的明文 JSON:
| namespace | 默认位置 | 用途 |
|---|---|---|
global |
$DSH_HOME/storages/memory.json |
跨项目的个人偏好 |
project |
<cwd>/.dsh/storages/memory_project_<hash>.json |
跟随仓库的项目共识,可 git 分享 |
两个根都可用 config 覆盖(globalRoot / projectRoot)。memory_list / memory_search 不带 namespace 过滤时会同时查两层。旧版双重前缀文件名(memory_project_memory_project_<hash>.json)在打开时自动迁移为规范名。
主题(topic,0.20.0)
namespace 管「在哪生效」,topic 管「这条讲什么」,两者正交:
| 字段 | 回答的问题 | 取值 |
|---|---|---|
namespace |
换个项目还成立吗 | global / project |
kind |
是记忆还是提示词模板 | fact / prompt |
topic |
这条记忆讲什么 | 自由字符串:见闻 / 约定 / 坑 / 偏好 / 事实 / 判据 … |
memory_save 传 topic 落库,memory_list / memory_search 传 topic 切片。留空或省略即「未分类」。用它把「自己出去逛到的」(见闻)和「一起干活时记下的」分开翻,不必再往正文里手写前缀。
跨工作区可见性(0.10.0 / 0.11.0)
project 记忆按会话工作区分文件存放,所以「工作区」就是可见性的边界。0.10.0 起补了三个方向——针对的是同一个盲区:不知道存在(检索是有意图的动作,搜不出自己不知道存在的东西)。
| 方向 | 机制 | 效果 |
|---|---|---|
| 向上 | 祖先链 | 在子目录里开会话能检索到父目录的记忆(反向不行,链是单向向上的),结果带 .. / ../.. 来源标记 |
| 向下 | 子项目索引行 | 注入里多一行「本工作区下另有 N 个子项目带记忆:xxx(M 条)——用 memory_search 检索」 |
| 全局 | 记忆索引行 | 注入里多一行「索引:当前可见 N 条记忆,主题集中在 X(14)、Y(12)…——用 memory_search 检索」 |
三条边界:写入永远只落当前工作区(「我在这个项目里记的东西」不该被推理到别处);但检索得到的记录就改得动——memory_update / memory_forget 同样沿链定位;注入路径只读当前工作区(远处的记忆进检索、不进每轮成本)。
0.11.0 起,「放错了层」有了显式的修正通道:memory_move(id, to) 把一条记忆移到 global、self(当前工作区)或相对祖先(.. / ../..)。它是例外动作——默认写入仍然只落当前工作区,只有当你判定「这条其实属于上一层、或上一层的那一支」时才动它。移动保持 id、从源层删除(不是复制),并报告移动后源层是否仍看得见它:向上移动源层不失明(继承只向上,global 人人可见),向下移动则会让源层及其下层从此看不见它。典型用途是把放高了的记忆降回它真正属于的工作区——比如只属于某一个生态的规矩躺在 global 里、漏进了无关项目。判据与边界见设计文档 §3.2。
后两行不占注入预算、自消除(没有内容时整行不出现),且长度常数级——不随记忆增长。索引行里的主题取自各条的关键词词频,与检索用的是同一套词汇,所以索引里出现的词就是能搜到的词。
孤儿文件清点:node scripts/scan-orphans.mjs [根目录] 报出「哪些记忆文件当前代码已经打不开」(历史命名遗留:早期无哈希后缀、双重 memory_project_ 前缀)。只读,不做任何处置——旧文件可能含未过隔离检查的内容,处置需人工拍板。
祖先链诊断与探针:node scripts/ancestor-probe.mjs plan [--cwd <会话 cwd>] 只读地复算该 cwd 的祖先链(哪些层级带记忆文件、引擎会纳入几级),并回答「这个 cwd 上祖先链是否可观测」——若以上祖先层都没有记忆文件,祖先链生效与否行为逐字相同,那是个零差异的观测点。要实测检索侧跨链,用 seed / check / clean 在祖先层造一条临时探针再撤销。前提:探针只对没打开过该层的会话有效——引擎的表打开即缓存,而写路径的新鲜度门(refreshForWrite)只比对 global 与当前 cwd,不含祖先层,被缓存过的层会用陈旧内存态覆盖掉外部写入。
多实例共存(0.7.1)
两个 DSH 实例(例如 DSH web 与 SSiD 桌面壳)可以同时运行、共用同一个 DSH_HOME,记忆不再互相抹掉。
DSH 存储层的写入是「读—改—写全量覆盖」,且官方两个后端都声明不做跨进程协调(storage-json:writer per process and last-write-wins is correct;storage-sqlite:cross-process coordination is out of scope)。插件层的处置是写前重读(等价于 update 前先 select):每次写入在串行区里先比对存储文件指纹(mtime + size),发现磁盘被别的实例动过就先重载再写。代价是磁盘没变时的一次 statSync。
残余窗口只剩两个实例在同一瞬间写——此时后写者赢;同时写不同记忆已不再互相影响。
未覆盖:workspace.json(工作区登记)由 DSH 自己的 workspace 服务写,插件层够不着,仍会被双实例互相覆盖;根因处置在上游——discussion #6882。当前防线是轮转快照备份(SSiD 侧 shell/scripts/backup-storages.mjs + 计划任务)。
定时备份(2026-10-18;日记归档 2026-10-25)
动机:2026-10-18 的事故——.dsh 下的 storages 被外部整目录删除后,本插件的写入路径(读—改—写全量覆盖)在空磁盘上把内存里的 global 记忆覆盖成一条;而当时 memory.json 没有任何一份存在于 .dsh 之外,无从恢复。
机制:插件启动后每小时(可配)把记忆文件只读复制到 <DSH_HOME 的兄弟目录>/.dsh-backups/memory/<YYYYMMDD-HHmmss>/,并写一份 manifest.json 记录来源与文件清单。插件关闭(dispose)时再补一次——定时器只在运行期间生效,关 DSH 前最后一段变更靠这一次兜住。保留最近 14 份(可配,只作用于记忆快照),超期自动清理。
默认根跟着 DSH_HOME 走:缺省 DSH_HOME=~/.dsh 时就是 ~/.dsh-backups/memory;隔离实例(不同 DSH_HOME)各自落在自己的兄弟目录,不会把快照混进同一份。同时它仍在 DSH_HOME 之外——2026-10-18 那次事故是整个 .dsh 被删,备份必须活在外面。
日记另走一条路:<DSH_HOME>/diary/*.md 与记忆同一轮复制,但落在固定目录 ~/.dsh-backups/memory/diary-archive/,只增不删,不参与上面的轮转。两边的不对称是实的:记忆快照丢一份还有别的份(同一份数据的多个时间点副本),归档里每一篇都是日记的唯一副本,丢了没有任何地方能重建。日记那一侧改过就以日记为准覆盖(人可能在事后改过错字),归档里有而日记里已没有的篇目一律保留——那正是归档的全部价值。归档目录名不匹配轮转正则,因此不需要给清理逻辑开白名单。
无界面:这是行为类功能,不新增按钮、面板或设置项——行为效果即上面那段:部署后 ~/.dsh-backups/memory/ 下每小时多一个目录,目录里是当次复制出的 memory.json、memory_project_*.json 与 manifest.json;diary-archive/ 里则按天累积 YYYY-MM-DD.md。
| 项 | 默认值 |
|---|---|
| 备份内容 | memory.json(global)+ 每个打开过的工作区各一份 memory_project_*.json + <DSH_HOME>/diary/*.md |
| 目标目录 | 记忆:<DSH_HOME 的兄弟目录>/.dsh-backups/memory/<时间戳>/(在 DSH_HOME 之外;缺省 DSH_HOME 下即 ~/.dsh-backups/memory/);日记:同一根下的 diary-archive/ |
| 间隔 | 60 分钟(首次延迟 1 分钟);插件关闭时另补一次 |
| 保留 | 记忆:最近 14 份;日记:全部(只增不删) |
| 失败行为 | 只写日志,不影响记忆读写与启动 |
边界:
- 备份本轮运行打开过的所有工作区的 project 文件:来源是引擎按会话 cwd 记下的那份工作区账,每轮备份时现取。本轮一次都没打开过的工作区不在范围内——备份是定时任务,没有「当前工作区」可问,打开过的才留得下痕迹。多个工作区给出同名文件时加递增后缀,绝不覆盖。
- 只读文件、只复制——绝不复用引擎的写入路径。写路径会先
reload()再合并,磁盘缺失时它会把内存覆盖掉,正是这次事故的成因;备份不能走同一条路。 - 日记按逐字节内容判断是否已备份过,不按 mtime:复制与编辑都会动 mtime,内容才是事实。
云同步(2026-11-11)· 默认关闭
把本机记忆镜像到一个 git 仓库,多个环境之间共享同一份记忆。
默认不启用,而且是显式开关。
memory.sync.enabled为true时才注册memory_sync与memory_conflicts——关着时根本不注册(模型看不到,也就不会去调), 自述里的工具数停在 14。判据不是「配了 remote」(2026-11-12 改):填了目标地址不等于打开了功能,那会让一次 配置试验变成意外开机。开关与目标解耦后,还多出一档「只开本地镜像、暂不推送」 ——此时
remote留空即可,push 会明确回「未配置远端地址 —— 已完成本地提交」。开关在插件页「记忆」详情里点(与日记开关同一张卡片)。打开后卡片里会出现三个输入框 (远端地址 / 镜像目录 / 分支,2026-11-13 加)——不必去改配置文件。值写进 profile 的
cordis.patch.yml(memory.config.sync.enabled等,不是独立的设置文件):该 profile 开了patchReload: live时即时生效,否则重启实例后生效——2026-11-12 在 dev 实例里实测, 关掉热重载时点了开关、工具不会立刻出现。remote/mirrorDir那几项决定连接目标, 无论哪种情况都要重启。访问令牌(PAT)不进配置:面板里是一个密码样式的输入框(不回显明文),值存进
$DSH_HOME/memory-sync-token的独立文件;也认环境变量DSH_MEMORY_SYNC_TOKEN(优先)。 令牌不落进 patch 文件——那文件会被复制、分享、提交。
为什么是「一条记忆一个文件」。 整份 memory.json 放进 git 会撞:2026-11-06 实测,两端各新增一条记忆后 merge,git 报 CONFLICT (content)——两条记录挤在同一个插入点,按「取一边」解决会静默丢掉另一端那条,而 git 仍报「合并成功」。改成一条一个文件后同一场景是 Merge made by the 'ort' strategy,0 冲突。
项目身份用项目名,不用路径。 本机 H:\MaxNull\WorkStation 与另一台机器 D:\WorkStation 是同一个项目——目录名(统一小写)决定它在 records/project/ 下落到哪。用绝对路径哈希的话,换个盘符就成了另一个项目,记忆会静默分裂成两份互不相认的。
memory:
sync:
enabled: true # 总开关;关着两个工具根本不注册
mirrorDir: '' # 缺省 <DSH_HOME>/memory-sync
remote: '' # https 远端;留空只做本地提交
branch: main
envName: '' # 本环境短名,冲突留证时用作后缀
proxy: '' # 访问 GitHub 需要代理时填,如 http://127.0.0.1:7897
代理:node 的 HTTP 不读 HTTPS_PROXY,所以只配系统代理/系统 git 代理是不够的——插件里要显式给。优先级:面板的「代理」> HTTPS_PROXY > HTTP_PROXY;都不给就直连。
测试连接:面板里填完地址、令牌、代理后,点「测试连接」走一次 ls-remote(不写本地仓库、不推不拉),失败会分成「地址不对 / 令牌无效 / 连不上(需要代理)」三类分别报——推不上去时不用猜是哪一环坏的。
token 不写进配置:从环境变量 DSH_MEMORY_SYNC_TOKEN 或既有凭据机制取。
| 工具 | 作用 |
|---|---|
memory_sync |
status 只看状态 / pull 拉取并导入 / push 镜像并推送 |
memory_conflicts |
列出未被自动裁决的冲突 |
仓库形态:
<mirrorDir>/
records/global/<uuid>.json 一条记忆一个文件
records/project/<项目名>/<uuid>.json
tombstones/<uuid>.json forget 留下的痕迹(不删记录文件)
conflicts/<uuid>.<env>.json 两边都改过时,两个版本都留着
边界:
- 冲突不自动裁决。 同一条被两个环境同时修改时,两个版本都写进
conflicts/,本地版本保留并标记待处理。记忆是长期资产,被静默覆盖的那份就没了。代价是冲突会堆到有人处理为止。 - 删除留墓碑,不删文件。
memory_forget是真删(table.delete),而另一端的「刚加、我还没拉到」在本地看起来完全一样。靠「上次同步的 id 快照」(.sync-state.json)区分:曾同步过、现在没了才是删除。首次同步永远不产生墓碑——那时没有「曾经有过」这个信息。 - 本地瞬态字段不进镜像:
hitCount/lastUsedAt每次检索命中都会改,带上它们会让仓库在内容毫无变化时持续变脏。隔离记录(quarantined)整条不导出——隔离区的存在理由就是它可能含凭据类内容。 - 首次同步必须 clone。本地
git init出来的仓库与远端没有共同祖先,push 会被拒(not a simple fast-forward)。这条是 2026-11-11 第一次连真仓库时撞出来的。 - 传输层用
isomorphic-git(纯 JS,走 HTTPS + PAT),不依赖系统 git——移动端装的东西会被运行时更新清掉,不该把同步链的地基押在上面。
没有调度器。 目前靠模型显式调用 memory_sync;autoPull / autoPush 尚未实现。
使用流程(写入即生效,人为例外干预)
提示词模板库(0.6.0):prompt_search / prompt_get / prompt_list / prompt_add 四个工具管理模板库——
md 文件是唯一事实源(~/.dsh/prompt-library/*.md 为 global;<workspace>/.dsh/prompt-library/ 随工作区分享),
前端(记忆面板「模板」tab / 模型工具)检索同一份索引;模板存在即生效(source: agent 角标标识模型新增),
永不注入 system prompt。
模型 memory_save → status: approved,立即生效;命中密钥/凭据规则则隔离(不进检索也不进注入)
给 injected 则同时钉住常驻(0.8.0),不受自动规则影响
钉成常驻时返回 `resident`(total / injected / omitted / budget)——
钉的那一刻就知道挤掉了几条,不必等下一轮的出局诊断行
memory_search → 关键词/语义召回(只搜记忆;模板走 prompt_search 通道,不会混进候选池)
默认最多返回 20 条,`limit: 0` 取消上限;条数等于 limit 时可能还有更多
命中记账只记 BM25 前 5 名——`hitCount` 是"排前"的信号,不是"被检索到"的面;
一次查询的真实命中数看 `query-log.json` 的 `hits` 字段
人(面板 / 开关) → 钉住 / 删除 / 放行隔离记录 / 回滚
memory_forget → 随时删除(删除始终是人的动作)
常驻开关只由人(或模型显式传 injected)决定。 两条自动规则都已在 0.12.0 / 0.13.0 停用:命中次数不再自动升常驻(它只喂体检报告的候选提示),30 天未命中的自动撤下也改为只计量不生效——撤下来就基本不会自己回去,那种动作不该由一个没有分布依据的阈值决定(阈值来历见 docs/设计/2026-09-29-借阅-dsh-mneme-后的自查.md §二)。两者都不删除任何内容——记忆不会无限累积,也不会被系统自行清空。
低频但关键的记忆(长期约定 / 判据 / 委托)本来就够不着旧阈值,而自动规则停用之后,问题从「够不着」变成了「必须显式说」。这类走显式路径——在 memory_save / memory_update 里给 injected 即钉住,写的是与面板开关同一套语义(injectedAuto: false)。未审核与已隔离的记录不接受注入。
注入有字符预算(默认 1500,config injectionBudget):装不下的条目整条丢弃(不截内容),按「最近更新优先」取舍——所以 injected: true 不等于「每轮真的在场」,它只保证有资格排队。0.9.0 起这种出局不再无声:注入末尾会追加一行 (另有 N 条常驻因预算未注入:…),0.9.1 起列出的是每条的短摘要而不是 id——查 id 是什么的那一步最容易省略,省略了就等于没报;清理干净即自行消失。面板的注入预览里也能展开看明细。诊断行本身不占预算。
记忆还可声明有效性锚点(环境变量 / 工具清单 / 插件版本):所绑的值变化后,这条记忆被标记 stale 并撤下常驻,检索结果里也带出失效提示——环境变了,旧结论就不再被当成仍然正确。
模型写入不需要人点头,但人始终能看见并推翻:面板按「隔离区 / 常驻 / 冷数据」分组,被隔离的记录必须由人放行才会重新进入检索。
为什么明文 + BM25,而不是向量检索
向量检索的记忆本体是一串不可读的数字,过期信息会成为无法观测、无法修复的静默暗礁;BM25 + 明文让每一次召回都可解释、每一条记忆都可见可删。语义(向量)检索作为可插拔的可选项(0.5.2,见「可选配置」)——记忆本体仍是明文,向量仅作为检索辅助字段(vector,明文可读),且以"可观测 + 可修复"为门槛。
明文还有一层跟随仓库分享的好处:project 命名空间的记忆落在项目文件夹内(<cwd>/.dsh/storages/memory_project_<hash>.json),随 git 提交、分享给所有协作者;global 命名空间的记忆留在本地 $DSH_HOME。团队的共识("本项目统一用 Vue3 <script setup>")能沉淀进仓库,而不是散落在每个人的本地。FTS5 的 SQLite 二进制无法这样"跟着仓库走"。
可选配置
在 cordis.yml 的 config 里传给插件(均可省略):
- id: memory
name: '@max-null/dsh-memory'
config:
injectionBudget: 1500 # 常驻注入预算(字符;null = 不限制)
summaryChars: 80 # 单条注入摘要截断上限(字符)
semanticTopK: 5 # 语义侧参与融合的 topK(仅配置 embeddings 时生效)
# 混合语义检索(可插拔;缺省 = 纯 BM25)
# embeddings: { embed: '...' 提供 embed(texts) 的宿主函数 或 插件名 ... }
# 定时备份(2026-10-18;缺省启用,目标在 DSH_HOME 之外)
backup:
intervalMinutes: 60 # 间隔(分钟)
keep: 14 # 保留份数
# targetRoot: 'D:/dsh-backups/memory' # 自定义备份根
# enabled: false # 关掉
# 日记开关(2026-10-24;也可在插件设置页里直接改,等价)
diary:
enabled: true
embeddings 接受 { embed(texts): Promise<number[][]>, similarity? } 对象——由宿主装配层提供嵌入实现(如 DeepSeek 嵌入端点);配置后 memory_search 以 BM25 + 语义 RRF 融合,向量增量生成并持久化,嵌入调用失败自动降级为纯 BM25。
SSID 系列
本插件是 SSID(思灵 · Seek Soul in Darkness) 全家桶的一员;也可以单独安装到任意 DSH profile——除设置页的记忆面板外不依赖其它同系列插件。
开发
npm install
npm run typecheck # tsc 严格类型检查
npm test # vitest 单测
npm run build # 产出 dist/
node scripts/verify-loader.mjs # 用 Loader 端到端验证插件可加载
node scripts/scan-orphans.mjs [根目录] # 清点「当前代码打不开」的记忆文件(只读)
node scripts/unzstd-frames.mjs <源> <目标> # 解多帧 zstd(读 DSH 会话日志用)
node scripts/ancestor-probe.mjs plan [--cwd <cwd>] # 复算祖先链(只读);seed/check/clean 验 ④-A
node scripts/analyze-cooccurrence.mjs # 关键词共现分析(记忆之间有没有关系网,只读)
npm run check:memory # 记忆文件存量体检(只读;0 = 全绿 / 1 = 有不合规 / 2 = 用法错)
依赖(peerDependencies,由宿主提供)
@deepseek-ai/cordis(^4.0.1),以及 @deepseek-ai/dsh-storage、@deepseek-ai/dsh-storage-domain、@deepseek-ai/dsh-storage-json、@deepseek-ai/dsh-system-prompt、@deepseek-ai/dsh-tools、@deepseek-ai/dsh-typert-protocol——后面六个声明为 >=0.1.1-rc.1 <0.3.0。
为什么写显式范围而不是 caret:caret 的上界是「下一个 minor」——^0.1.1-rc.1 展开为 >=0.1.1-rc.1 <0.2.0-0,内核一跨 minor 就判定不满足。而 peer 不满足时插件不会被加载,也不会出现在 did not activate 列表里:它在进入 fiber 图之前就被跳过,界面不报错,只表现为少一批功能。写成显式范围让 0.1 线与 0.2 线都能装载。
验证过的内核:0.1.1-rc.2(原基线)与 0.2.0-rc.1(0.12.2 起——typecheck 用其类型定义零错误,并在隔离实例实机加载,三个失败信号全为 0)。下限保持原值不变:它是作者实际验证过的最早版本,比它更早的内核没验证过,不声称支持。
Links
More in this category
vectorize-io/hindsight#coding-agents★ 60916
Hindsight, agent memory that learns: long-term project memory with auto recall and retain, knowledge pages, deep reflection, and per-repo memory banks.
volcengine/OpenViking#examples/dsh-memory-plugin★ 42209
OpenViking memory and context bundle for DeepSeek Harness: pre-step auto-recall and profile injection, session capture, `viking://` URI guarding, and recall/write memory tools backed by an OpenViking server.
MemTensor/MemOS#memos-local-plugin★ 12392
Local-first memory plugin for DeepSeek Harness: layered L1/L2/L3 memory with automatic capture, manual memos_* retrieval tools, and a built-in memory viewer.
agentscope-ai/ReMe#dsh★ 3799
Connects DeepSeek Harness to ReMe's local-first, self-evolving personal knowledge base: automatically captures completed main-agent conversations as user-owned Markdown memory, searches conversations and source material through reme_search with BM25, optional embeddings, and wikilink expansion, and schedules daily memory consolidation.
zilliztech/memsearch#MemSearch★ 2873
Shared Markdown memory for DSH and other coding agents, with automatic capture, pre-step context injection, searchable recall, and memory-to-skill self-evolution through a review panel.
vshulcz/deja-vu#extensions/dsh★ 1544
Cross-session memory and context recovery across forty-five other coding agents (45+) on this machine, reading session files they already wrote before install (Claude Code, Codex, Cursor, Windsurf, Trae, Antigravity, VS Code Copilot Chat, opencode, OpenClaw, Hermes, Kimi, Qoder, Cline, Zed, Magpie and more): six tools (deja_recall, deja_session, deja_blame, deja_fix, deja_how, deja_remember), a /deja command, and optional automatic recall injected into runtime context. Local BM25 CLI/MCP index, no LLM, no embeddings, no network (dsh plugin --profile web add dsh-deja).


Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.