Memory
dsh-memory-lite-alanzhao
alanzhao0128/dsh-memory-lite · ★ 2
Lightweight zero-dependency memory plugin for DeepSeek Harness: Markdown-file memory store, L0 catalog injection, and 5 model-facing tools.
Manifest details
安装
dsh plugin --profile web add '@alanzhao/dsh-memory-lite@0.4.1'从 npm 安装预构建包
For install and environment setup, plus plugin development tutorials, see DSH community docs ↗
README
Table of contents
English | 简体中文
Cross-session long-term memory for your dsh agent. Plain Markdown files, zero dependencies: preferences, decisions and constraints from a session survive restarts, upgrades and profile switches.
Every dsh session starts from a blank page — transcripts are persisted for audit, but the model never sees them again. This plugin adds that layer back: one resident memory catalog + 5 memory tools + background extraction.
Quick start (3 steps)
# 1. Install (= pnpm add inside the profile; the package is added to
# dsh.profile.bundles automatically)
dsh plugin --profile web add @alanzhao/dsh-memory-lite
# 2. Restart dsh (the host half and the browser half load at boot)
# 3. Verify — the three checks below
After installing you should see:
- a
memory-litecapsule in the session header: green = last extraction ok, gray = extraction off, red = last extraction failed - a full settings page under Settings → memory-lite (screenshot below)
- ask the model to remember something ("remember that I use pnpm") — it calls
rememberand a file lands under~/.agent-memory/peers/<peer>/memories/preferences/(directories are created on first use)
Compatibility: dsh ≥ 0.1.5 (full support for dsh 0.2.1-rc.2 specification and engines constraint, seamless integration with Working Directory decoupling and managed Subagent Activation isolation; dual-track compatibility for 0.1.7+ SettingsForms/configForms, backward compatible to 0.1.5), Node ≥ 22.
Features
| What | Notes |
|---|---|
| Explicit memory (5 tools) | read_memory / search_memory / remember / update_memory / forget_memory; update archives the previous content into ## History, forget is a soft delete into .trash/ |
| L0 catalog injection | one resident catalog message per session (rendered from _index.md); re-published when the index changes or compaction moves it off the visible surface |
| Implicit extraction | a background run feeds the new conversation window to the model and applies a create / merge / update / skip decision, with dedup search and parse-failure repair |
| Cross-peer sharing | mount another peer's memories read-only under shared/<name>/ |
| Header indicator | capsule + three-state dot in the session header, polling the run status every 10 s |
| Peer isolation | the peer name derives from the session cwd (basename + short hash); cwd-less sessions fall back to defaultPeer |
What it costs
| When | Cost |
|---|---|
| Every step | the 5 tool schemas are injected (~300–800 tokens) |
| Every session | one resident catalog message, capped at 1200 tokens by default; near-zero incremental cost once the KV cache hits |
| Every explicit remember | one tool call; the index rebuild costs no LLM |
| Every implicit extraction | one LLM call (two when the answer needs a repair), using your default model or a pinned one |
Default triggers: 50 new conversation messages (windowTurns) or 30 minutes idle (idleTimeoutMin); turn-boundary and flush triggers are off by default.
Tip: extraction is a structured JSON task and needs no reasoning. Set reasoning effort = off — otherwise the model can spend the whole output budget thinking (two calls and 20–80 s per run).
Configuration
Everything is edited in Settings → memory-lite and stored under the dsh-memory-lite: section of ~/.dsh/settings.yaml. Most fields apply on save; root and sharing.* need a restart.
| Group | Field | Notes |
|---|---|---|
| Extraction | Mode | incremental (default, implicit extraction on) / explicit_only / off |
| Triggers | Window turns | one run once this many new messages accumulate; default 50; 0 = trigger on every message (not a disable) |
| Idle timeout (min) | extract the remaining window after N idle minutes; default 30 | |
| Turn-boundary / flush triggers | off by default (more expensive) | |
| Min messages / debounce | thresholds for the two triggers above | |
| Content | Max messages | most messages fed to the model per run; default 20 |
| Message scope | default user + assistant; raw tool results excluded (cost and noise only); all three unchecked = the default | |
| Tool-result truncation (bytes) | only used when "tool results" is checked | |
| Include digest / dedup search | both on by default | |
| Model | Extraction model | follows the live global default model unless pinned to a provider/model |
| Reasoning effort | read from the selected model's declaration; off is recommended | |
| Reliability | Parse retry | one cheap repair call when the answer is not valid JSON (on by default) |
| Audit log | writes peers/{peer}/sessions/{session-id}.json and extraction.log |
|
| Catalog | Token cap | truncation cap for the L0 catalog, default 1200 |
| UI | Indicator order | header utilities slot order; refresh the page after saving |
| Storage | Memory root | ~/.agent-memory by default, restart to apply |
| Default peer | owner of cwd-less sessions | |
| Cross-peer sharing | master switch, restart to apply; individual mounts are picked below |
Full schema and defaults: src/config.ts.
What the memory looks like
~/.agent-memory/
├── peers/{peer}/
│ ├── memories/
│ │ ├── _index.md # L0 catalog (generated; hand-editable)
│ │ ├── preferences/ entities/ events/ experiences/
│ │ │ └── {slug}.md # L1 full text
│ └── sessions/ # extraction bookmark + audit + extraction.log
└── .trash/ # soft-deleted memories (dated folders)
Each memory file uses ## Current / ## History / ## Related; updates archive instead of overwriting. The four categories are retrieval paths for the model: preferences (always relevant), entities (mentioned knowledge), events (time-related), experiences (task lessons).
How it works (short version)
- Catalog injection: in the
agent/pre-stepwaterfall the peer's_index.mdis rendered into oneuser/messagecarrying the officialpluginsource kind with thecatalogform; subagent sessions get none. - Extraction: triggered by
windowTurnsor idle → select the window → grep existing memories for dedup → one LLM decision → write files → advance the per-session bookmark. A failed run keeps the bookmark, so the same window is retried on the next trigger. - Safety: every file access goes through a containment boundary (relative paths only;
.., absolute paths and symlink escapes are rejected at the executor). - Concurrency: all writes go through a serial queue with atomic tmp+rename replacement.
Details (design tradeoffs, incident notes, known deviations) live in IMPLEMENTATION.md.
Troubleshooting
| Symptom | What to do |
|---|---|
| Nothing happens after installing; the model has no memory tools | Restart dsh; check that dsh plugin --profile web ls lists the package and that it is in dsh.profile.bundles |
| No memory-lite capsule in the header | Refresh the page; the slot lives in the header utilities row (order is configurable) |
| The capsule is red | The last extraction failed: read the note of the last line in ~/.agent-memory/peers/<peer>/sessions/extraction.log |
| Extraction never fires | Check, in order: extraction mode, window turns, message scope, max messages (0 = nothing is extracted) |
The log says no configured model, or the panel shows 已失效 (stale) |
The extraction model was renamed or removed — pick another one |
Extraction is slow / always parse-error-recovered |
Reasoning effort is eating the output budget: set it to off |
| Memories land under the wrong peer | The peer derives from the session cwd; cwd-less sessions use defaultPeer (configurable) |
Uninstall
dsh plugin --profile web remove @alanzhao/dsh-memory-lite
# restart dsh
Your memories are not deleted: ~/.agent-memory is left for you to keep or remove.
Known limits
forget_memoryhas no approval gate (the model can soft-delete directly).## Relatedauto-maintenance is not implemented.- No CLI / MCP entry point.
- The browser half (indicator + settings page) needs the web profile's
connectionservice; under a headless profile the plugin still loads (tools + extraction work) and simply has no browser surfaces. - Extraction quality depends on the model you pick; small models produce JSON that needs repair more often.
Changelog
v0.4.1
- DSH 0.2.1-rc.2 Specification & Engines Alignment: Declared
"engines": { "dsh": ">=0.1.2-rc.1" }inpackage.json, adhering to the official priority evaluation ofengines.dsh. - Fixed Volatile Reference Validation Bug: Resolved
ValidationErrorduringinstallSettingsCompatby cleanly unwrapping volatile references withunwrapConfig. - L0 Catalog Category Priority & Anti-Truncation: Enforced stable output priority in
renderIndex(preferences -> entities -> experiences -> events) and expanded defaultindex.maxTokensto 2000, preventing core rules and assets from being dropped by token budgets. - Triple Gate for Background Extraction: Overhauled
extractionSystemwith a strict negative exclusion list, 30-day durability test, and aggressive merge requirement to eliminate over-extraction and memory fragmentation.
Development
npm install
npm run build # tsc -> lib/
npm test # node:test + tsx, including a real Loader boot smoke test
License
MIT