DSH Memory Vault icon
DSH Memory Vault · 长期记忆
Long-term memory for DeepSeek Harness, stored as a plain Markdown vault you own.
A first-level sidebar page, four model tools, token-budgeted recall, and a durable
history journal — no database, no telemetry, no cloud. Your memories stay as
Obsidian-compatible .md files on disk.
Install ·
Features ·
Configuration ·
Data safety ·
Privacy ·
Development ·
简体中文
Project status (2026-10-19). dsh-longterm-memory@0.1.3 is published on npm and
attached to the latest GitHub Release. The desktop-app plugin entry is now verified
end-to-end in the running desktop app (61/61 real-browser checks over all five tabs,
real vault, restored afterwards); the earlier "desktop path unverified" caveat is
resolved. See Install, What's new in 0.1.3 and
Project status & limitations.
What's new in 0.1.3
- Inbox is actionable. 确认入库 / Confirm and 拒绝 / Reject now work for
in-memory candidates. Confirming writes an active memory (
low confidence is raised
to medium), folds a duplicate/supplement into the existing memory instead of
duplicating it, and keeps both sides of a contradiction. Rejecting writes no file.
- Status changes on extracted memories work. A multi-line
evidence: block
scalar no longer blocks saves with unsafe-edit.
- The UI language setting works (it previously lost to the host locale).
- No more permanent "unsaved changes" hint on the settings page.
- New real-browser E2E suite (
npm run verify:e2e) covering all five tabs,
including the Inbox, detail, settings, batch and merge flows.
Full details: CHANGELOG.md ·
latest release
Inbox confirm/reject fixed
Merge dialog
Why DSH Memory Vault?
Most "agent memory" features are a vector store or a black-box service. DSH Memory
Vault makes the opposite trade-off:
- Markdown is the only source of truth. Each memory is a human-readable note with
YAML frontmatter. Read, edit, link and back it up in Obsidian or any text editor.
There is no proprietary format and no export step.
- The index is disposable.
.dsh-memory/index.json is derived data; delete it and
the plugin rebuilds it from Markdown. Losing the index never loses a memory.
- Recall is budgeted and transparent. Injection is computed per assembly from the
current index with explicit token/item caps; the page shows exactly what the model
would receive.
- History is durable. Every change is recorded in a journal with before/after
digests, reasons and timestamps. A write-ahead outbox keeps history safe across
crashes — including the verified window where the Markdown is committed but the
journal append has not happened yet.
- Local by default. No network requests, no model calls, no telemetry; the only
traffic is the local RPC between the management page and the DSH host.
Features
| |
|---|
| First-level sidebar page | Registers a sidebar.panellist entry and a full management page (overview, search, detail, manage, inbox, settings) — not a chat tab. |
| Four model tools | memory_search, memory_get, memory_save, memory_forget (save refuses credential-like content). |
| Automatic extraction | Rule-based, fully local candidate extraction at the real turn/end boundary. Optional model-assisted extraction is off by default. |
| Ranked recall | BM25-style lexical ranking over Markdown, with type/status/tag filters and a token budget. |
| Memory lifecycle | pending → active → stale / superseded / archived / conflict, plus low/medium/high confidence. Low confidence and sensitive content wait in the Inbox for human confirmation. |
| Safe updates | Deterministic dedupe/merge rules: duplicates merge, additions update while keeping sources, contradictions keep both sides, low confidence goes to Inbox. |
| Conflict handling | Conflicting facts are kept side by side, never silently overwritten; a conflict-resolution page supports keep / merge / both. |
| Crash-safe writes | Single-file atomic replace, per-file lock, optimistic checksum concurrency, durable journal outbox with idempotent recovery. |
| Obsidian compatible | The vault carries .obsidian/, [[wikilinks]], stable frontmatter and a human-readable Index/. |
| Bilingual UI | Chinese and English (`language: zh |
No runtime dependency on @deepseek-ai/* | The host half only uses the Cordis context passed in, so DSH internals can change without breaking the bundle. |
Interface
Real acceptance screenshots (headless Chrome, real host data — no mocks):
More: overview · search · manage · inbox · settings · dark theme
How it works
flowchart LR
A[DSH host] -->|tools / prompt section| P[Memory Vault host plugin]
U[Sidebar page] -->|local HTTP RPC| P
P --> V[(Markdown vault<br/>source of truth)]
P --> I[(index.json<br/>derived)]
P --> J[(journal outbox<br/>history)]
P --> Q[(quarantine<br/>unreadable notes)]
V -. rebuild .-> I
J -. recover .-> V
- The host owns the vault, index, journal and extraction; it never calls a model
unless you explicitly enable model-assisted extraction with your own provider.
- The management page reads and writes through a loopback-only RPC surface
(
POST /dsh-longterm-memory/api/<method>, 27 methods) guarded by a browser-trust
fence (loopback / trusted hosts, Origin, sec-fetch-site).
- Recall:
memory_search and automatic injection share one engine; explicit
recall may include retired memories (labelled history), injection only uses current
facts. POST /api/previewStanding and POST /api/previewRecall return the exact
text that would be injected.
Install
Option A — install the latest from npm (recommended)
dsh plugin --profile web add dsh-longterm-memory@latest
Latest release: dsh-longterm-memory@0.1.3. Use @latest (or omit the version) so
an installation always fetches the newest published version. Inside the official
desktop app, install or update the plugin from 插件 / Plugin manager or
插件市场 / Plugin Market, then restart the app when prompted. The desktop path is
verified end-to-end in 0.1.3.
Option A2 — download the latest packaged tarball (no build required)
The newest .tgz (and its .sha256) is attached to every GitHub Release:
# Download the latest release asset into the current directory
gh release download --repo bufan528/dsh-longterm-memory --pattern "*.tgz" --dir .
dsh plugin --profile web add .\dsh-longterm-memory-0.1.3.tgz
No gh? Open
https://github.com/bufan528/dsh-longterm-memory/releases/latest and download the
asset manually.
Option B — build this repository and install the tarball (verified)
Requires Node.js ≥ 20 and a DSH CLI. Use an isolated DSH_HOME so the test
profile cannot touch your daily DSH profile:
# 1. Build and pack
npm install
npm run build
npm pack # -> dsh-longterm-memory-0.1.3.tgz
# 2. Install into an isolated profile
$env:DSH_HOME = "<temporary dir>" # PowerShell
# export DSH_HOME="<temporary dir>" # bash
dsh plugin --profile lmv add .\dsh-longterm-memory-0.1.3.tgz
# 3. Start
dsh --profile lmv --no-open --port 0
Open the Web UI, click 长期记忆 / Long-term Memory in the sidebar, and choose a
vault directory (or let the plugin initialize one). You can also pre-set it:
# <DSH_HOME>/profiles/lmv/cordis.patch.yml
- id: longterm-memory
name: dsh-longterm-memory
config:
vaultPath: 'D:/path/to/DSH Memory'
language: zh
Data-location warning. If vaultPath is unset, the plugin initializes the
default location (~/Documents/DSH Memory on Windows). DSH_HOME isolates the
profile, not the vault. Always set vaultPath when testing.
Quick start
- Install as above and open the page.
- Click 初始化 / Initialize and pick a folder (an existing Obsidian vault works).
- Create a memory with 新建 / New, or let automatic extraction collect candidates
at the end of a turn; low-confidence items appear in 收件箱 / Inbox.
- Ask the model something related; the plugin injects the standing section and the
tools return ranked hits from the vault.
- Open 设置 / Settings to tune budgets, per-step recall, extraction and language.
Vault layout
DSH Memory/
├── README.md # generated; user edits are preserved
├── Memory Home.md # entry page
├── .obsidian/ # makes Obsidian treat it as a vault
├── Profile/ Preferences/ # profile, long-term preferences
├── Projects/ Projects/State/ # projects and project state
├── Decisions/ Knowledge/ # decisions, knowledge
├── Experiences/ Tasks/ # lessons, tasks
├── Inbox/ # candidates awaiting confirmation
├── Archive/ Conflicts/ # retired / contradictory memories
├── Index/ # human-readable index (regenerated)
├── History/ # change-history notes
└── .dsh-memory/ # config.json, index.json, journal/, quarantine/
Memory types: profile, preference, project, project-state, decision,
knowledge, experience, task. Status: active, pending, stale,
superseded, conflict, archived. Format details:
docs/storage-format.md.
Configuration
All settings are optional; defaults live in src/host/config.ts and can be overridden
from your profile's cordis.patch.yml.
| Key | Default | Meaning |
|---|
vaultPath | auto-discover / initialize | Absolute memory vault path |
language | zh | UI language (zh / en) |
enableTools | true | Expose the four model tools |
enablePromptSection | true | Inject the standing memory section |
standingBudgetTokens | 700 | Token cap for the standing section |
standingMaxItems | 12 | Item cap for the standing section |
recallBudgetTokens | 600 | Token cap for one recall answer |
recallMaxItems | 8 | Item cap for one recall answer |
enablePerStepRecall | false | Splice retrieved memory into each step (replayed for the rest of the session) |
enableAutoExtraction | true | Rule-based extraction at turn end (local) |
enableLlmExtraction | false | Optional model-assisted extraction (sends turn text to your provider) |
redactSecrets | true | Refuse credential-like candidates |
sensitiveNeedsConfirmation | true | Health/finance/identity content waits for confirmation |
maxCandidatesPerTurn | 5 | Extraction cap per turn |
Data safety
- Atomic single-file writes (temp file + rename) and a per-file lock with
stale-lock detection; the lock is released on failure, never leaked.
- Optimistic concurrency: edits carry the expected checksum; a stale edit is
rejected with a reload hint instead of overwriting someone else's change.
- Index is rebuildable:
rebuildIndex / syncIndex recover from a deleted or
corrupt index. Corruption diagnostics are surfaced, not hidden.
- Durable journal: every change gets a before/after digest, reason and timestamp.
A write-ahead outbox recovers when the monthly archive fails, with idempotent
re-archiving and content-conflict detection (same event id + different payload is an
explicit integrity error, never a silent merge).
- Crash-window tests: commit boundaries are exercised with real child-process
SIGKILLs, not simulated exceptions — including "Markdown written, commit marker not
yet persisted" and "recovery itself interrupted".
- Quarantine: notes that cannot be parsed safely are isolated, never deleted.
Privacy
- No telemetry and no outbound network calls by default. Host-side acceptance runs
showed zero network calls; the only client request target is the local DSH RPC. The
0.1.3 desktop E2E run exercised the real page and asserted zero page errors / uncaught
exceptions; it is not a packet-level network audit.
- Model use is opt-in: automatic extraction is rule-based and local; model-assisted
extraction (
enableLlmExtraction) is off and, when enabled, uses the provider you
configured in DSH.
- Secrets are refused: credential-like content (passwords, API keys, tokens,
private keys) is blocked before it reaches the vault; sensitive categories require
explicit confirmation.
- Injection is data, not instructions: memories are wrapped and labelled as
untrusted data for the model. See docs/privacy.md.
Compatibility
| |
|---|
| DSH runtime | >=0.2.1-rc.2 <0.2.2 (engines.dsh; 0.2.0-rc.2 is not supported) |
| Verified DSH version | 0.2.1-rc.2 (host + real headless Chrome acceptance) |
| Node.js | >=20 |
| Platform | Windows (primary), Node path rules elsewhere |
| npm package | dsh-longterm-memory — 0.1.3 published (latest) |
| Downloads | npm @latest, or the tarball on the latest GitHub Release |
The dsh.compatibility field is informational; engines.dsh is the only decision
source. Details and evidence: docs/compatibility.md.
Development
npm install
npm run build # tsc -> lib/host, esbuild -> lib/client.js
npm run typecheck # tsc --noEmit
npm run lint # contract checks + version-consistency check
npm test # full suite (currently 41 files / 595 cases)
npm run check # build -> typecheck -> lint -> test (clean checkout safe)
npm run pack:plugin # build + npm pack
npm run verify:e2e -- --url "http://127.0.0.1:<port>/?token=..." # real-browser E2E (61 checks)
verify:e2e drives a real Chrome over CDP, clicks the real management page and checks
every effect through the plugin RPC and the Markdown files on disk. For a token-guarded
desktop listener, mint the desktop launch cookie for the current machine and pass it
with --cookie-file <name=value file>.
Tests cover unit, integration, packaging, real HTTP-route validation, and deterministic
fault injection (including real subprocess kills). The browser acceptance scripts are
zero-dependency CDP clients using Node's built-in WebSocket.
Project status & limitations
- Preview: API and storage format may still change;
0.1.0 was the first public
version, 0.1.3 is the current release.
- npm published:
dsh-longterm-memory@0.1.3 (2026-10-19); registry installation
verified. The npm latest dist-tag and the GitHub Release always point at the newest
version.
- Desktop verified: the full real-browser E2E suite passes against the running
desktop app (61/61 checks, all five tabs, real vault restored afterwards).
- Known follow-ups: in-memory Inbox candidates are drafts — they are not counted in
待确认数量 / the Inbox/ folder until confirmed; they leave the Inbox when
confirmed or rejected. A few concurrency and move-atomicity edge cases remain open;
frontmatter and lock regressions found in review were fixed in 0.1.0 and are locked
by tests.
- Vault location: with
vaultPath unset the plugin initializes
~/Documents/DSH Memory. Set it explicitly to keep test data isolated.
Documentation
License
MIT