dsh-memory-layered
Layered (global + per-project) bounded file-backed memory for DeepSeek Harness — model-curated with user intervention, versioned writes, TTL expiry, session search, and an in-chat memory panel
- Stars
- 0
- Language
- TypeScript
- Created
- Aug 15, 2026
- Updated
- Aug 24, 2026
Introduction
@deepseek-ai/dsh-memory-layered
DeepSeek Harness 的双层(全局 + 项目)有界文件记忆插件:模型自主维护 + 用户干预, 带版本化回滚、TTL 过期、会话搜索、对话页记忆面板。
设计文档见
DESIGN.md。
能力
- 双层记忆:
global(跨项目的事实/约定)+project:<workspace-uuid>(当前工作区,映射 DSHworkspaceRegistry) - 有界注入:每层字符预算(默认 2000+2000),超限拒写 + 模型当回合整合(hermes 式)
- 溢出归档:整合不动推入
ARCHIVE.jsonl(可检索、不注入、永不真删) - 版本化:每次写前快照,
/memory undo回滚 - TTL:临时事实自动过期进归档
- 会话搜索:
memory search搜注入层 + 归档 + DSH 会话历史(ctx.sessionQuery) - 用户干预:
/memory斜杠命令 + 手改MEMORY.md(漂移检测兜底) - 记忆面板:对话页输入行 🧠 按钮,弹层显示全局/项目两层 + 使用率
- 会话交接摘要:长对话切换前在 🧠 弹层手动"生成"→ LLM 把最近 N 轮蒸馏成结构化交接(独立
handoffs.jsonl,不占记忆预算,按全局/项目分层)→ 新对话自动携带(项目层优先、全局兜底),或手动"带入"预填输入框
工作原理
存储
$DSH_HOME/memories/
├── global/
│ ├── MEMORY.md # 注入层:§ 分隔条目,人类可读可手改
│ ├── memory.meta.json # 元数据:条目 id → {addedAt, ttl?}
│ ├── ARCHIVE.jsonl # 归档层:溢出/过期条目,append-only
│ ├── handoffs.jsonl # 交接摘要:独立存储,保留最近 N 条
│ └── versions/ # 每次写前的快照(undo 用)
└── projects/<workspace-uuid>/
└── (同结构)
会话循环
- 会话开始:注入一条冻结快照(
<system-reminder>块,含两层条目 + 使用率)。中途写入只改文件、不改快照(前缀稳定,KV cache 整会话有效)。 - 写入:模型调
memory工具(或你发"记住…"触发它)→ 预算检查 → 原子写盘。 - 超限:拒写并返回当前条目,模型当回合合并/删减(整合);整合不动的推 ARCHIVE。
- 跨会话:下一会话重新注入当前文件内容。
会话交接摘要
长对话切换新对话前,在 🧠 弹层的"交接摘要"分区点生成:
- host 端读当前会话日志最近
handoffTurns轮(turn/start定界,跳过插件注入/工具消息),经ctx.llm.stream用会话自身路由(session.requestContext())发起一次模型调用。 - 模型产出结构化摘要(首行 ≤40 字标题 + 目标/结论/关键事实/未完成/偏好/下一步 小节),总长 ≤
handoffCharLimit(默认 1500 字符),存入作用域handoffs.jsonl(与MEMORY.md预算隔离,保留handoffMaxKeep条淘汰最旧)。 - 自动携带:新会话首个 admitted step,若日志无 handoff 消息且存在"非本会话"的最新交接,注入一条
source {kind:'memory', form:'handoff'}消息(与快照同语义:resume 复用、compaction 后重注入)。层级:项目层优先(同项目延续更具体的上下文),项目层无交接时回退全局层(跨项目切换兜底)。 - 手动带入:弹层列表每条有"带入"按钮 → 优先
inputActions.setDraft预填输入框(可编辑后发送),不可用时退化剪贴板。 - 多选管理:每条交接前有复选框,勾选后出现批量工具栏——「带入」(多条用
---分隔合并预填输入框)、「删除」、「取消」;切换层级时勾选自动清空。 - 层级切换:交接按作用域分层存储(全局
handoffs.jsonl+ 每个项目自己的handoffs.jsonl),弹层交接分区有「全局 / 项目」tab——生成、删除、带入都作用于当前 tab 的层;会话无项目层时只显示「全局」。全局层交接适合跨项目通用的项目背景/团队约定。 - 会话关闭也能生成:生成时优先用存活会话(
agents/sessions服务)的日志;找不到时回退ctx.sessionPersistence.loadStored()从磁盘日志恢复(不要求会话仍在内存),模型路由从日志的request/context事件提取。 - 时序注意:快照/交接在会话开始时冻结,所以要在旧对话生成、之后新建的对话才自动携带;已切新对话可用"带入"补救。
三个入口
| 入口 | 谁 | 动作 |
|---|---|---|
memory 工具 | 模型自主 | add / replace / remove / batch / archive / search |
/memory 命令 | 你 | list / undo / drop / set-ttl / search(直执行,不进模型) |
手改 MEMORY.md | 你 | 任意编辑;round-trip 则生效,否则备份 + 拒绝(漂移检测) |
安装
dsh plugin --profile web add "link:D:/dsh_plugin/dsh-memory-layered"
或 npm:
dsh plugin --profile web add @deepseek-ai/dsh-memory-layered
装完重启 dsh web,刷新页面;并检查 profile 的 node_modules/@deepseek-ai 是否被建成真实目录(见 hyls9527 仓库 README 的注意项)。
配置
按 id 覆盖(profile 的 cordis.patch.yml):
- id: memory-layered
config:
globalCharLimit: 4000
nudgeInterval: 0
| 字段 | 默认 | 含义 |
|---|---|---|
memoryEnabled | true | 开关 |
globalCharLimit | 2000 | global 层字符预算 |
projectCharLimit | 2000 | project 层字符预算 |
nudgeInterval | 10 | 无写入多少轮后提醒;0 关闭 |
versionsKeep | 100 | 版本快照保留数 |
recallLimit | 10 | search 默认返回条数 |
recallSessionHistory | true | search 是否含会话历史 |
dir | $DSH_HOME/memories | 记忆根目录 |
handoffEnabled | true | 交接摘要开关 |
handoffCharLimit | 1500 | 交接摘要字符上限(标题 + 正文) |
handoffMaxKeep | 10 | 每作用域保留的交接条数,超限淘汰最旧 |
handoffTurns | 20 | 生成时摘取最近多少轮对话 |
handoffAutoCarry | true | 新会话是否自动携带最近一条交接 |
会话历史搜索(默认关)
DSH 的 session-query-sqlite 默认 openAt: never,memory search 的会话历史部分会静默降级。要开启,在 profile 补丁层加:
- id: session-query-sqlite
config:
path: ':memory:'
openAt: first-search
命令
/memory list [global|project]
/memory undo [n]
/memory drop <id|子串>
/memory set-ttl <id|子串> <1d|7d|ISO日期>
/memory search <query>
安全不变量
- 存在但读不了的文件 ≠ 空文件(fatal UTF-8 解码,写入拒绝)
- 外部漂移(不 round-trip / 单条目超限)→ 快照
.bak.<ts>+ 拒绝 - 每次提交:跨进程锁 + 同目录原子 rename(0o600/0o700)
- 整批 all-or-nothing;超预算拒绝 + 当回合整合;每回合失败上限 3 次
- 注入转义
</system-reminder>;replace/remove 子串匹配 + 歧义保护 - 版本化:写前必快照,任何整合可回滚;TTL 只归档不真删
错误码
| code | 说明 |
|---|---|
ERR_UNREADABLE | 文件存在但读不了,拒绝写 |
ERR_DRIFT | 外部编辑不 round-trip,已存 .bak |
ERR_OVER_BUDGET | 超字符预算,需整合 |
ERR_CONSOLIDATION_LIMIT | 本回合整合失败超 3 次,强制停止 |
ERR_NO_MATCH / ERR_AMBIGUOUS_MATCH | 子串未命中 / 命中多条 |
ERR_NO_VERSION | undo 目标版本不存在 |
ERR_TTL_FORMAT | ttl 格式非法 |
ERR_NO_WORKSPACE | 显式 target:project 但无工作区 |
开发
corepack pnpm install
corepack pnpm typecheck # tsc --noEmit
corepack pnpm test # vitest
corepack pnpm build # tsc + 复制 client.js
- 覆盖率门禁:行/语句/函数 100%,分支 ≥95%(见 vitest.config.ts 说明)
- 测试 198 例 / 11 个 spec
- Node
^22.19 || >=24
已知限制
- 单目录单进程集:per-agent 记忆需挂多个插件行、每个不同
dir - 无 prompt 注入扫描(信任文件,同 DSH 信任 AGENTS.md)
- nudge 按轮次,非墙钟
- 快照是持久 user message 而非系统提示词段(仓库外插件无法注册新事件词表)
License
MIT(大脑图标路径取自 Lucide,ISC,见 THIRD_PARTY_NOTICES.md)