penglai-doll
dsh-memory
Hermes-style persistent memory + self-learning for DeepSeek Harness: MEMORY.md/USER.md stores injected as a system-prompt section refreshed every session, a single memory tool (add/replace/remove/atomic batch) with threat scanning, and a post-turn background review loop that replays the transcript through the llm service with a memory-only tool whi
- Stars
- 0
- Language
- JavaScript
- Created
- Aug 17, 2026
- Updated
- Aug 17, 2026
Introduction
dsh-memory
中英双语 Bilingual:中文在前,English below.
中文
Hermes 风格(NousResearch/hermes-agent)的持久记忆 + 自学习插件,按 DeepSeek Harness 的 Cordis 插件约定重写。
对照移植关系:
| Hermes 源码 | dsh-memory 实现 |
|---|---|
tools/memory_tool.py MemoryStore(MEMORY.md / USER.md、§ 条目、2200/1375 字符预算) | lib/store.js — 纯函数存储逻辑(解析、序列化、add/replace/remove、原子 batch、预算、威胁扫描) |
tools/memory_tool.py MEMORY_SCHEMA + memory_tool() | lib/schema.js + lib/index.js 里的 memory 工具注册 |
| 会话开始冻结快照注入 system prompt(prefix-cache 不变式) | apply() 里 agent/session-start 触发 refresh,systemPrompt.section 注册快照 |
tools/threat_patterns.py(strict 作用域注入扫描) | lib/store.js scanThreats()(保守子集)+ 注入时 [BLOCKED] 占位 |
agent/background_review.py(每轮后 fork 一个工具白名单只有 memory 的 review agent) | lib/review.js — agent/turn-stopping 后,用 llm 服务跑迷你工具循环(白名单只有 memory) |
_MEMORY_REVIEW_PROMPT / _COMBINED_REVIEW_PROMPT(含 do-not-capture 规则) | lib/review.js REVIEW_PROMPT |
💾 Self-improvement review: … 摘要 | summarizeReview() + logger + reviews.jsonl |
memory.nudge_interval(每 N 轮提醒使用记忆) | nudgeInterval + {{memory_usage_state}} prompt 变量 |
功能
记忆
$DSH_HOME/memories/MEMORY.md(agent 自己的笔记:环境、约定、工具坑、经验)+USER.md(用户画像:偏好、风格、期望)。- 会话开始时读取并注册为一个有序 system-prompt section(冻结快照:会话内写入立即落盘,但只在下一个会话进入提示词,与 Hermes 的 prefix-cache 不变式一致)。
- 单个
memory工具,Hermes 同款语义:action(add / replace / remove)或原子operations批处理——预算只对最终结果校验,一次调用即可腾空间 + 写入;replace/remove用短唯一子串匹配;超预算时返回当前条目供整合后重试;成功响应是终止性的(防止模型重复调用)。 - 每条写入都过注入扫描(不可见/双向控制字符 + 已知指令覆盖短语);注入提示词前再次扫描,命中条目以
[BLOCKED: …]占位,磁盘原文保留供用户检查删除。 - 写入通过主机
fsprovider(version guard 防并发覆盖)+fs/write-intent/fs/observed约定;没有 provider 时回退 Node 文件系统(原子 rename)。
自学习
- 每个 turn 关闭时(
agent/turn-stopping),若距上次 review 达到review.interval,就在后台把会话转录(session.deriveMessages(),尾部截断到review.maxTranscriptChars)重放给模型,工具白名单只有memory——Hermes 白名单 fork 的等价物。 - 模型在 review 中的工具调用走与交互工具完全相同的守卫路径(威胁扫描、预算、版本 guard),结果回灌循环,直到 "Nothing to save."、迭代预算耗尽或失败。
- 摘要写入日志与
$DSH_HOME/memories/reviews.jsonl。 - 子代理会话(
session.header.origin === 'subagent')自动跳过;插件停止时 in-flight review 被中止。
挂载
在 agent preset / host composition 中加一行:
- id: dsh-memory
name: dsh-memory
config:
# memoryCharLimit: 2200 # MEMORY.md 字符预算
# userCharLimit: 1375 # USER.md 字符预算
# sectionName: memory # system-prompt section 名
# order: 2 # section 顺序
# nudgeInterval: 10 # 连续多少轮未用 memory 工具后注入提醒
# tools: true # 注册 memory 工具
# review: # 自学习 review
# enabled: true # 默认开启(与 Hermes 一致)
# interval: 1 # 每 N 轮 review 一次
# maxIterations: 3 # review 工具调用迭代预算(Hermes 默认 16)
# maxTranscriptChars: 40000
# provider: deepseek # 可选:指定 review 模型路由
# model: deepseek-chat
# maxTokens: 4096 # 可选
review.provider / review.model 未配置时,依次回退到:会话自身的 request context(主对话模型,对应 Hermes 的 cache-warm 策略)→ 默认模型选择。没有任何可解析路由时跳过 review 并告警,记忆功能不受影响。
与 dsh-soul 的分工
- dsh-soul(SOUL.md):人设 / 身份 / 语气规范——"你是谁"。
- dsh-memory(MEMORY.md / USER.md):跨会话事实、用户偏好、环境约定、经验教训——"你知道什么"。条目式、带预算、可批量整合;persona 内容请继续写 SOUL.md。
安全
- 记忆文件是 prompt-injection 载体:写入时扫描拒绝,注入时二次扫描
[BLOCKED]占位(磁盘原文保留)。 - 字节预算硬限制(读取与写入双重);读取失败时保留旧快照而不是注入坏数据。
- Review 是 fire-and-forget 的后台 LLM 调用:注意成本。默认每轮一次(Hermes 语义);对话密集时可调大
review.interval或review.enabled: false。 reviews.jsonl与记忆文件都在$DSH_HOME/memories/下,归部署者所有。
测试
npm test # node --test(常规环境)
node test/smoke.mjs # 纯断言冒烟(无 runner 依赖)
node test/host-smoke.mjs # Host 集成冒烟(mock ctx/fs/llm)
限制与路线图
- v0.1 聚焦记忆自学习;Hermes 的技能自学习(skill review、
/learn、curator)未移植——DSH 侧的技能持久化由 dsh-skills-manager 管理,可作为后续版本集成(review 写技能文件并触发skills/change)。 - 暂无 Web 设置面板(dsh-soul 有
/soul编辑器);记忆文件可直接编辑,下一个会话自动生效。 - 威胁扫描是 Hermes threat_patterns 的保守子集;扩展只需在
lib/store.js的INJECTION_PHRASES加一行。
English
A Hermes-style (NousResearch/hermes-agent) persistent memory + self-learning plugin, rewritten to DeepSeek Harness's Cordis plugin conventions.
Porting relationship:
| Hermes source | dsh-memory implementation |
|---|---|
tools/memory_tool.py MemoryStore (MEMORY.md / USER.md, § entries, 2200/1375 char budgets) | lib/store.js — pure-function storage logic (parse, serialize, add/replace/remove, atomic batch, budgets, threat scanning) |
tools/memory_tool.py MEMORY_SCHEMA + memory_tool() | lib/schema.js + the memory tool registration in lib/index.js |
| Frozen snapshot injected into the system prompt at session start (prefix-cache invariant) | agent/session-start triggers a refresh in apply(); systemPrompt.section registers the snapshot |
tools/threat_patterns.py (strict-scope injection scan) | lib/store.js scanThreats() (conservative subset) + [BLOCKED] placeholder on injection |
agent/background_review.py (after each turn, fork a review agent whose only tool is memory) | lib/review.js — after agent/turn-stopping, run a mini tool loop over the llm service (only memory whitelisted) |
_MEMORY_REVIEW_PROMPT / _COMBINED_REVIEW_PROMPT (with do-not-capture rules) | lib/review.js REVIEW_PROMPT |
💾 Self-improvement review: … summary | summarizeReview() + logger + reviews.jsonl |
memory.nudge_interval (remind to use memory every N turns) | nudgeInterval + the {{memory_usage_state}} prompt variable |
Features
Memory
$DSH_HOME/memories/MEMORY.md(the agent's own notes: environment, conventions, tool quirks, lessons) +USER.md(user profile: preferences, style, expectations).- Read at session start and registered as an ordered system-prompt section (frozen snapshot: in-session writes hit disk immediately but only enter the prompt in the next session, matching Hermes's prefix-cache invariant).
- A single
memorytool with Hermes-identical semantics: anaction(add / replace / remove) or an atomicoperationsbatch — the budget is validated only on the final result, so one call can free room and write;replace/removematch by a short unique substring; on over-budget it returns the current entries for consolidation and retry; the success response is terminal (prevents the model from calling repeatedly). - Every write goes through injection scanning (invisible/bidirectional control chars + known instruction-override phrases); scanned again before injection, with hits rendered as
[BLOCKED: …]placeholders while the on-disk original is kept for the user to inspect/remove. - Writes go through the host
fsprovider (version guard against concurrent overwrites) +fs/write-intent/fs/observedconventions; without a provider it falls back to the Node filesystem (atomic rename).
Self-learning
- At each turn close (
agent/turn-stopping), ifreview.intervalhas elapsed since the last review, the session transcript (session.deriveMessages(), tail-truncated toreview.maxTranscriptChars) is replayed to the model in the background with a tool whitelist of onlymemory— the equivalent of Hermes's whitelisted fork. - The model's tool calls inside a review go through the exact same guard path as interactive tools (threat scanning, budget, version guard), and results loop back until "Nothing to save.", the iteration budget is exhausted, or failure.
- Summaries are written to the log and
$DSH_HOME/memories/reviews.jsonl. - Subagent sessions (
session.header.origin === 'subagent') are skipped automatically; an in-flight review is aborted when the plugin stops.
Mounting
Add one row to an agent preset / host composition:
- id: dsh-memory
name: dsh-memory
config:
# memoryCharLimit: 2200 # MEMORY.md char budget
# userCharLimit: 1375 # USER.md char budget
# sectionName: memory # system-prompt section name
# order: 2 # section order
# nudgeInterval: 10 # turns without the memory tool before injecting a reminder
# tools: true # register the memory tool
# review: # self-learning review
# enabled: true # on by default (matches Hermes)
# interval: 1 # review every N turns
# maxIterations: 3 # review tool-call iteration budget (Hermes default 16)
# maxTranscriptChars: 40000
# provider: deepseek # optional: route the review model
# model: deepseek-chat
# maxTokens: 4096 # optional
When review.provider / review.model are unset, they fall back in order to: the session's own request context (the main conversation model, matching Hermes's cache-warm strategy) → the default model selection. When no route is resolvable, the review is skipped with a warning and memory still works.
Division of labor with dsh-soul
- dsh-soul (SOUL.md): persona / identity / tone rules — "who you are".
- dsh-memory (MEMORY.md / USER.md): cross-session facts, user preferences, environment conventions, lessons — "what you know". Entry-based, budgeted, batch-consolidatable; keep persona content in SOUL.md.
Security
- Memory files are a prompt-injection vector: writes are scan-rejected, and injection is scanned a second time with
[BLOCKED]placeholders (the on-disk original is kept). - Hard byte budgets (enforced on both read and write); on read failure the old snapshot is kept rather than injecting bad data.
- Review is a fire-and-forget background LLM call: mind the cost. Default is once per turn (Hermes semantics); for chat-heavy use, raise
review.intervalor setreview.enabled: false. reviews.jsonland the memory files live under$DSH_HOME/memories/and belong to the deployer.
Testing
npm test # node --test (regular environment)
node test/smoke.mjs # pure-assertion smoke (no runner dependency)
node test/host-smoke.mjs # host integration smoke (mock ctx/fs/llm)
Limitations & roadmap
- v0.1 focuses on memory self-learning; Hermes's skill self-learning (skill review,
/learn, curator) is not ported — skill persistence on the DSH side is managed by dsh-skills-manager and can be integrated in a later version (review writes skill files and triggersskills/change). - No Web settings panel yet (dsh-soul has a
/souleditor); memory files can be edited directly and take effect next session. - The threat scan is a conservative subset of Hermes threat_patterns; extend it by adding a line to
INJECTION_PHRASESinlib/store.js.