dsh-auto-memory
asktheway/dsh-auto-memory
Auto-memória no estilo Claude Code, nativa: arquivos de memória tipados (user/feedback/project/reference) com um índice MEMORY.md injetado automaticamente no system prompt (pegada zero quando vazio); memory_write/read/list/delete seguem as regras de escrita do Claude Code (deduplicar e atualizar, datas absolutas, links cruzados); camadas por workspace e por usuário, bloqueio de arquivo entre processos com recuperação de lock órfão, Markdown puro em disco, sem serviços externos, sem embeddings.
Instalar
dsh plugin --profile web add github:asktheway/dsh-auto-memoryREADME
dsh-auto-memory
Your dsh agent forgets everything you tell it. Every. Single. Session.
Fix it with one command. Claude Code-style persistent memory for DeepSeek Harness — native, zero servers, zero embeddings, zero setup.
dsh plugin --profile web add dsh-auto-memory
Say "Remember: I'm a Python backend engineer preparing for interviews" today — open a brand-new session tomorrow, ask "what do you know about me?", and it remembers.

Highlights
1.6 — memory has a shelf life: time-sensitive facts (plans, prices,
versions) carry a validUntil; expired memories stop being injected, surface
on the health board with one-click renew, and are marked as historical when
recalled. High-recall memories now rank higher in the index (a gentle
reads multiplier), and writes carrying secrets get an explicit "redacted"
receipt instead of silent sanitization.
1.5 — a panel, not a wall: tabbed UI (Memories first-class, Insights, Health, Settings), monochrome SVG iconography, color-blind-checked identity colors — and health rows jump straight to the memory card they're about.
1.4 — runtime settings + a recall funnel: a Settings card (presets, sliders, per-field reset) changes the injection budget, value gate and eviction at runtime — next request, no restart; the recall funnel makes "injected but never actually read" visible and actionable (mute the budget hogs, pin the buried gems).
1.x — trustworthy memory + measured end-to-end: consolidation products are
born candidate with verbatim evidence quotes; only you can grant
verified; disputed memories are never re-injected; every write passes an
injection-pattern guard at the store choke point. And it's measured: four
evaluation layers (deterministic / recall / consolidation / end-to-end) plus a
LongMemEval-S alignment run — including an honestly published off-target score.
Full history: CHANGELOG.md ·
Releases
Claude Code has this. dsh didn't. Now it does.
DeepSeek Harness is the hottest open agent harness on GitHub right now — models, tools, sandboxes, everything is a plugin. But it ships with no memory subsystem at all. The official answer is three default-off MCP configs to third-party servers, which the official docs themselves qualify: not auto-injected, no forgetting policy, substring-only search. Your agent has amnesia by design.
dsh-auto-memory closes that gap natively:
| MCP bridge approach | dsh-auto-memory | |
|---|---|---|
| Memories injected into every system prompt, automatically | ✗ | ✓ (zero tokens when empty) |
| Typed memories: user / feedback / project / reference | ✗ | ✓ |
| Workspace + user scope layers — no cross-project leakage | ✗ | ✓ |
| Trust machine: candidate / verified / disputed / superseded + verbatim evidence | ✗ | ✓ |
| Auditability: every injection logged, recall funnel, health board | — | ✓ |
| Crash & concurrency safety (cross-process locks, orphan recovery) | — | ✓ |
| External services / databases / embeddings required | ✓✓✓ | none — just plain Markdown files |
Memories are ordinary files under $DSH_HOME/memory/ — hand-editable,
grep-able, git-friendly, yours.
Trustworthy, not just persistent (1.0.0)
Model-consolidated memories can be confidently wrong — and a wrong memory
gets re-injected every session. So memories carry a trust status:
consolidation products are born candidate (with a verbatim evidence quote
from the transcript — fabricated citations are dropped by checking against
it), verified can only be granted by you in the panel, disputed /
superseded are never injected. Every write path passes an injection-pattern
guard at the store choke point; content changes downgrade trust and expire
old evidence. A 🩺 health board surfaces unverified / zombie / conflicting
memories for review. Same flat files, same zero services — now with an audit
trail for whether to believe them.
One minute to feel it
node scripts/demo.mjs # no API key, no browser: watch write → index → inject → recall → forget
Or for real, in a chat: tell your agent things worth remembering. The model
calls memory_write / memory_read / memory_list / memory_search /
memory_delete, following Claude Code's write discipline: dedupe-and-update
over piling up, absolute dates only, [[name]] cross-links, feedback
memories carry Why: / How to apply: lines.
The memory panel
The whole memory system has one home in the dsh Web UI: user-scope and project-scope groups, per-memory read counts and trust badges, pin / disable / edit / delete, injection statistics (per memory and per project, with budget truncation boards), the health board, and the Claude Code import/export bridges.

Import from Claude Code — and take it back
The panel's 📥 Import from Claude Code button scans
~/.claude/projects/*/memory read-only and lists every memory grouped by source
project. Tick what you want and apply: fields map one-to-one (same four types,
H1 → title, ISO modified → epoch), same-name memories are skipped, never
overwritten.
Imported files carry an origin block (kind / slug / file / imported /
hash) in their frontmatter, so the checkbox becomes reversible: uncheck an
already-imported memory and apply, and it is deleted from this plugin — the
Claude Code source stays untouched and can be re-imported any time. If the
memory was edited after import, the content fingerprint no longer matches; the
entry is marked modified and kept, never silently discarded. Memories
imported by earlier versions (no origin block) are recognised by exact content
equality and marked legacy import, so they can be revoked the same way.
You don't even have to check: the host scans the Claude memory directory every 5 minutes, and a hint bar appears above the list whenever sync-able memories are waiting.
Export to AGENTS.md — the bridge goes both ways
A panel button renders your trusted memories (verified or unset — candidates and disputed never leave) as an AGENTS.md snippet copied to the clipboard. Paste it into any tool that follows the AGENTS.md convention — Claude Code, Codex, Cursor — and what this plugin learned travels with you. Same flat files, two directions.
What the model actually sees
Every request, one system-prompt section (order 4000) carries the index — re-evaluated per step, byte-budgeted, and gone entirely when the store is empty:
# Persistent memory index
## Project memories
- [压测过 PostgreSQL](id-generator-benchmark.md) — psycopg2 连接池有踩坑经验 (2026-09)
- [用户是 Python 后端工程师](user-prefers-python.md) — 正在准备面试; 偏好中文交流
Chinese titles, YAML frontmatter, one file per memory — exactly the Claude
Code MEMORY.md model, rebuilt natively on dsh's prompt-assembly pipeline.
Hardened before first release
This plugin survived a 12-agent adversarial code review (680k tokens of source-level scrutiny) before v0.1.0. Five production-grade traps were caught and fixed — with regression tests — including two that would have been field incidents:
- The NTFS silent destroyer: a memory named
memorycollides withMEMORY.mdon case-insensitive filesystems — the write succeeds while destroying the record. Blocked by a reserved-name guard. - The poisoned-prompt bomb: three literal
{{{ }}}braces in any memory could crash every model request in the workspace — with no way for the model to self-recover. Neutralized by a converging sanitizer.
Plus: orphaned-lock self-healing (Ctrl+C can't brick your memory store), symlink-read protection, malformed-file tolerance, stable index ordering to protect KV-prefix caches, and a strict no-custom-session-events policy (they make dsh sessions refuse to resume).
Measured, not just claimed
A deterministic evaluation layer (evals/) runs in CI — no LLM, fully reproducible:
-
Injection cost is a flat line — measured on the real render path (typical ~40-char descriptions, default 4 KB budget):
memories 0 10 20 50 100 200 injected section 0 B 2.3 KB (~564 tok) 3.2 KB (~801 tok) 4.0 KB (~1008 tok) 4.0 KB 4.0 KB The byte budget is a hard ceiling — from 50 memories on, the section is pinned at the cap no matter how many exist (oldest/lowest-ranked rows truncate with a marker). An empty store injects 0 bytes: the whole section disappears.
-
Compaction-proof by construction: the index rides in a dynamic system-prompt section re-evaluated on every step assembly — context compaction only compresses conversation history, so memories survive every compaction without re-injection tricks. Approaches that store memories as session-log messages lose theirs on the first compaction pass.
-
End-to-end lift measured (L2, the layer above the pipeline): with the full consolidation → injection → QA loop, the model answers project-specific probe questions at 95.8% with memories vs 16.7% without — a +79.2 point net contribution (standard dataset, 30 probes, blind LLM judge). An honest off-target data point is published too: on LongMemEval-S (122k-token chat-haystack needle-search, 30-question stratified subset, official judge protocol) the plugin scores 23.3% overall — quantifying exactly how far this coding-scenario design is from the chat-needle task, instead of cherry-picking benchmarks. Results · methodology
-
Eviction never misfires: four-class mixed scenario — only stale-zero-read memories get hidden; zero files lost; one read revives.
-
Relevance-ranked truncation (#18, 1.3.0): when the index exceeds budget, entries are kept by relevance to the frozen per-session query profile — KV-safe, re-evaluated per session. Honest boundary: in "inject before the question is known" scenarios (like LongMemEval) there is no query signal at truncation time — the needle probe in evals/README pins those coordinates instead of hiding them.
This evaluation layer already caught a real bug: the byte budget used to exclude the policy text, overshooting by ~800 bytes (fixed, regression-tested).
Verified platform matrix (manually tested, not just declared):
| CLI | Desktop | Web profile | |
|---|---|---|---|
| dsh version | 0.1.5-rc.2 | 0.2.0-rc.2 | 0.2.1-rc.2 |
| status | ✓ tools/prompt/consolidation | ✓ panel + import + audit | ✓ panel + import |
188 tests + four evaluation layers (L0 deterministic / L1 recall / L3 consolidation / L2 end-to-end + LongMemEval alignment). 0 runtime deps beyond yaml, ~35 kB installed.
Install
The package lives on npm (dsh-auto-memory) —
dsh plugin add pulls it straight from the registry, no manual download:
# 1) install into a profile (web is the default graphical one;
# substitute your own profile name if you use another)
dsh plugin --profile web add dsh-auto-memory
# 2) restart the profile — done
dsh web
Desktop client instead? Open the built-in plugin manager (Plugins page),
search dsh-auto-memory, click install — same package, same versions.
From source: npm install && npm run build && dsh plugin --profile web add /abs/path.
Requires @deepseek-ai/dsh >= 0.1.5-rc.2 (Node ^22.19 || >=24). Works with
the official desktop client — verified on 0.2.0-rc.2.
Then just talk to your agent: say "remember that I prefer Chinese comments", open the Memory panel in the web UI, or watch the injection stats fill up after a few sessions.
Configuration
Two layers, hottest knobs first:
Runtime settings (1.4.0) — the panel's Settings card, no restart, no
YAML. Focus/Companion presets, sliders for index budget / value gate /
soft eviction, auto-consolidate toggle. Every field shows where its value
comes from (profile or your override) and resets to the profile default
with one ↺. Changes apply to the next request — the injection budget,
consolidation gate and eviction read a live config object, not a frozen
snapshot. Stored as overrides in the memory root's settings.json
(whitelist + range validated; memoryDir deliberately not settable):
| field | range | default | what it does |
|---|---|---|---|
maxBytes | 1024–65536 | 4096 | per-request injection budget |
minImportance | 0–10 | 4 | consolidation value gate (0 = off) |
staleAfterDays | 0–3650 | 0 | soft-hide zero-read memories (0 = off) |
autoSummarize | on/off | false | consolidate durable facts on session end |
autoSummarizeMaxMemories | 1–20 | 5 | cap per consolidation |
Profile config — override in your profile's cordis.patch.yml (config
replaces wholesale). This is where memoryDir and enableUserScope live,
and where the defaults the runtime layer falls back to come from:
- id: auto-memory
config:
maxBytes: 4096 # injection budget (index + guidance + wrapper)
memoryDir: D:/memories # default: $DSH_HOME/memory
enableUserScope: true # false: user layer off on every path
autoSummarize: false # true: on root-session end, a background LLM files durable new facts
autoSummarizeMaxMemories: 5 # cap per consolidation
autoSummarizeMaxTokens: 2048 # consolidation output cap
staleAfterDays: 0 # >0: soft-hide zero-read memories older than N days (files kept)
How it works (60 seconds)
- Write: tool
execute→ name normalized to[a-z0-9-](reserved names rejected) → cross-process file lock (officialdsh-atomic-write) → atomic write → full index rebuild inside the lock. - Inject: one dynamic section re-evaluated on every step assembly; reads
the index synchronously, enforces the byte budget, neutralizes
{{. Tool writes take effect on the very next request — no restart, ever. - Audit: no custom session events (third-party types make dsh refuse to
resume); everything flows through standard
tool/call/tool/result. Since 1.0, every injection is also logged toaudit.jsonl(which memories, which session, what the budget cut) and aggregated into the panel's statistics — memory with a paper trail.
Deep dives: design decisions · postmortem: shipping a PR to awesome-dsh-plugin
Roadmap
- P0 — typed store, four tools, prompt injection, scoped layers, crash safety
- P1 — auto-consolidation on session end, forgetting & eviction, recall expansion, human-gated bulk delete
- P2 — Web UI memory panel, token-cost / recall-quality benchmarks
- P3 — update-replace semantics, matching core shared with the eval harness
- 0.8–1.0 — Claude Code import bridge (revocable), injection audit + stats, trustworthy memory (status / evidence / guard / health)
- 1.1–1.2 — L2 end-to-end + LongMemEval alignment,
memory_search, import watcher, reverse AGENTS.md export - 1.3–1.4 — consolidation value gate + chitchat suppressor, relevance-ranked truncation, memory evolution lineage, runtime settings center, injection→recall funnel
- real-coding-session blind eval (#20), community-driven work
Changelog
Per-version notes: CHANGELOG.md · GitHub Releases
License
MIT