DeepSeek Harness plugin

dsh-persist

Persistent memory for DeepSeek Harness: per-conversation notes, selective injection, project memory, semantic Vault search (bge-m3, free optional key) and automatic retrieval — agents stop forgetting.

Jump to install

Source facts

Repository
tluoluo/dsh-persist
Latest update
Aug 16, 2026
Category
Docs & Rendering
GitHub stars
1
Format
plugin
Catalog evidence
Upstream dsh.bundle evidence
Evidence path
package.json#dsh.bundle
Checked against
0.1.0-rc.8
Upstream check date
2026-08-20

This evidence comes from the upstream catalog. This site has not installed, run, or security-reviewed the plugin.

Install

Start with a prompt that asks an agent to review the GitHub repository and source. Switch to the command if you want to install it yourself.

Copy this prompt into DSH, Codex, or another agent and ask it to review the GitHub repository and source first.

Do not install or run any commands yet. Read this plugin's GitHub repository, README, and relevant source code. Then answer the questions below clearly and directly so I can decide whether it fits my needs:

1. What is this plugin, and what problem does it solve?
2. Who is it for, and what are its typical use cases?
3. How is it used after installation? Include one minimal example.
4. What known limitations or privacy, security, compatibility, or maintenance risks does it have?
5. Give a clear recommendation: recommend, conditionally recommend, or do not recommend, with reasons.

Distinguish statements documented by the repository, inferences from source code, and unknowns. If evidence is insufficient, say so explicitly. Do not guess or simply repeat the README.

GitHub: https://github.com/tluoluo/dsh-persist
Plugin: dsh-persist
Author: tluoluo

Check the source files

Read the README and other files from this plugin directory before installing.

File explorer4 files
README.en.mdSource · read only
README language

dsh-persist

简体中文 | English

> Persistent memory for DeepSeek Harness agents — agents stop forgetting.

What problem does it solve?

By default, DSH agents forget everything when you switch conversations — close the context, and nothing is remembered. dsh-persist turns memory into a layered, filesystem-based structure with on-demand injection per conversation:

LayerStorageAnalogyHow it's read
User profile + long-term memoryUSER.md / MEMORY.mdLTM (long-term memory)Optional injection per conversation
Key-value memorymemory.jsonQuick-reference cardsOptional injection; read/write via tool
Conversation memorysessions/<id>/memory.mdWorking memoryInjected into its own conversation only
Project memoryprojects/<key>/memory.mdPARA Project layerOpt-in injection per project
Vault semantic memoryvault.md + vault.dbZettelkasten card boxOn-demand semantic recall (tool or the "auto retrieval" tick; never injected verbatim)

Features

  • Per-conversation memory — each conversation gets its own "notebook" that never gets lost; other conversations can't see or inject it
  • Selective injection — inject only what you tick; new conversations default to zero injection, saving context
  • Shared project memory — conversations in the same working directory share project experience; named projects split multi-project directories
  • Vault semantic search — pair with a free key to find memories "by meaning"; falls back to keyword search without a key
  • Automatic recall (three-state mode) — pick the auto-retrieval mode in the Memory tab: Off (default) / Smart (pure-local rules filter chatter — only history/project/topic-related messages retrieve; zero network) / LLM judge (a model call decides; more accurate but sends the message to the model provider; timeout/failure degrades to Smart)
  • Memory tab UI — visual editing + injection config + live preview at the top of a conversation (WYSIWYG); the "Open memory admin" link in the tab's top-right corner opens the full management page in a new tab (all sessions/projects/Vault/global files)
  • Plain text everywhere — every file under ~/.dsh-memory/ is human-readable and editable; easy to back up, migrate, and export

Quick start

dsh plugin --profile web add dsh-persist   # 1. install
# 2. restart DSH (dsh --profile web)
# 3. open a conversation → "Memory" tab at the top → tick the blocks to inject

Tell the agent "remember this" and it writes into the current conversation's memory automatically. For semantic search: get a free key from SiliconFlow, set SILICONFLOW_API_KEY=..., and restart (works without it too — falls back to keyword search).

Installation

> Formerly dsh-memory (npm name taken, renamed to dsh-persist). Storage path ~/.dsh-memory/ and route /dsh-memory/ are unchanged — no migration needed.

Requirements: Node.js >= 22.6 (24+ recommended; --experimental-strip-types for tests is available since 22.6). The Vault layer uses built-in node:sqlite: available by default since 23.4, needs --experimental-sqlite on 22.6–23.3; on older versions the plugin still loads but Vault tools are disabled (memory/injection/UI unaffected). The host is DeepSeek Harness (provides tools / systemPrompt / webServer / sessions / agents services and the conversation.view UI slot).

dsh-persist is a standard DSH bundle: it declares dsh.bundle and installs into any profile via dsh plugin. @deepseek-ai/* are optional peer dependencies provided by the DSH host at runtime — npm doesn't (and doesn't need to) install them.

From npm (ships with prebuilt lib/, no build needed):

# install into your web profile (recommended)
dsh plugin --profile web add dsh-persist
# or any other profile
dsh plugin --profile demo add dsh-persist

Restart (dsh --profile web) after installing and the plugin takes effect.

From GitHub (pulls source and runs the prepare build, see below):

dsh plugin --profile web add github:tluoluo/dsh-persist

> When installing from git, pnpm may ask you to authorize the prepare build script on first add: copy the package key dsh prints into allowBuilds in your profile's pnpm-workspace.yaml, then re-run add (see the official DSH docs publish.md).

Optional environment variables (all optional — it works right after install):

  • SILICONFLOW_API_KEY=...optional, enables "semantic search". bge-m3 embeds memories into vectors and finds them by meaning (smarter than plain keywords). Works without it too: Vault memory falls back to keyword search. Get a free key at SiliconFlow. It's the only optional "external brain" — makes search smarter, not required.
  • DSH_MEMORY_INJECT=0 → disable automatic injection (enabled by default)
  • DSH_MEMORY_ALLOW_REMOTE=1 → allow non-loopback access to the memory API (default: 403 for everything remote; only needed when the DSH web server binds 0.0.0.0 — assess the privacy risk yourself)
  • DSH_MEMORY_AUTO_VAULT=0force off automatic semantic retrieval for every conversation; =1 → force on (unset = follow each conversation's "Auto retrieval" mode in the Memory tab, default off. Note: with =1 and no explicit GATE, the smart gate always applies — the tab's LLM choice is ignored)
  • DSH_MEMORY_AUTO_VAULT_GATE=heuristic|llm|off → globally override every conversation's auto-retrieval mode (heuristic smart gate: pure-local rules filter chatter — zero cost, zero network, zero latency; llm calls a model judge (see DSH_MEMORY_JUDGE_MODEL), any failure degrades to heuristic; off disables the gate — retrieve on every turn). Unset = follow each conversation's Memory-tab selection — note the tab's Off means "no retrieval", which differs from this env's off: the env value only takes effect when set explicitly. The word lists are heuristic with known edge cases (short messages without trigger words aren't retrieved; long chatter can slip through) — a deliberate trade-off
  • DSH_MEMORY_JUDGE_MODEL=provider/model → model used by the LLM-judge mode (default deepseek-official/deepseek-chat, cheap and fast; format provider/model)
  • DSH_MEMORY_JUDGE_TIMEOUT_MS=5000 → LLM-judge timeout (default 5000ms; on timeout the mode degrades to Smart)
  • DSH_MEMORY_AUTO_VAULT_NAMESPACES=user,dsh-persist → restrict automatic retrieval to these namespaces (default: all, including project archives)

> Privacy: with semantic search enabled (SILICONFLOW_API_KEY), every user message and memory entry is sent to SiliconFlow for embedding; automatic retrieval does the same. LLM-judge mode additionally sends the current message to the model provider configured by DSH_MEMORY_JUDGE_MODEL for a "needs retrieval?" decision. If that's a concern, don't configure a key, or set DSH_MEMORY_AUTO_VAULT=0 to disable automatic retrieval (manual vault search still works).

In one sentence: dsh plugin add + restart = memory. Want smarter search? Grab a free SiliconFlow key.

First steps after installing

1. Restart DSH (dsh --profile web — don't reuse the old running process). 2. Open the web UI, enter any conversation — a "Memory" tab appears at the top (next to the trace tab). That's your per-conversation memory and injection switches. 3. Tell the agent what to remember — it writes into the conversation memory via the memory tool automatically; or edit manually in the Memory tab. By default nothing is injected (no context cost) until you tick blocks in the tab.

> Optional: for semantic search, grab a free SiliconFlow key, set SILICONFLOW_API_KEY=..., restart, and the Vault layer upgrades from keyword to semantic search.

Security

/dsh-memory/api/* is loopback-only (non-local requests get 403 unless you explicitly set DSH_MEMORY_ALLOW_REMOTE=1). Memory contains personal information — don't expose it on shared networks.

Building from source

Users don't need to build — published packages ship with lib/, dsh plugin add just works.

External developers: after cloning, npm install runs prepare automatically (tsdown transpile-only, no @deepseek-ai types needed to produce lib/), so the package builds self-containedly:

npm install        # runs prepare → lib/index.js + lib/client.js
npm run prepare    # explicit rebuild of self-contained artifacts (host: tsdown.host.config.ts, client: tsdown.config.ts)
npm test           # smoke tests (node --experimental-strip-types src/smoke.ts)

prepare is transpile-only (no type-check): it strips import type of @deepseek-ai/* from source, so the runtime artifact keeps just two host-provided imports (@deepseek-ai/dsh-tools.defineTool and @deepseek-ai/dsh-llm.createUserMessage) — buildable without host types, loaded by the DSH host.

Maintainers (inside a DSH host tree, junctioned to host deps for @deepseek-ai types) can run the full build for .d.ts + full type-checking:

npm run build      # typecheck + typecheck:client + build:host + bundle + dts

> End-to-end semantic tests live in src/smoke-semantic.ts (need a real SILICONFLOW_API_KEY) and are not part of npm test — run manually: > - PowerShell: $env:SILICONFLOW_API_KEY=...; node --experimental-strip-types src/smoke-semantic.ts > - bash: SILICONFLOW_API_KEY=... node --experimental-strip-types src/smoke-semantic.ts

> Note: lib/ is gitignored; a running DSH needs a restart to load new host code (client bundle just needs a page refresh).

Usage

Tool actions (model-called)

The memory tool's scope parameter decides where things are written/read:

  • scope=conversation (default) → current conversation memory: add appends a note, list/search read it all
  • scope=project → project memory of the current working directory (shared across conversations in the same directory)
  • scope=global → global keyed memory: add/get/search/delete (by key)
ActionArgsEffect
addscope, content (plus key for global)store a memory
get / search / deletescope, keyglobal keyed operations
listscoperead all conversation/project memory
profileprofileKind, profileOp, contentread/write global user profile (USER.md/MEMORY.md)
vaultvaultOp, content/query, namespacesemantic storage: add / search / list / export / import (namespace-isolated)

Memory files (all human-readable/editable)

FileContentHow to edit
~/.dsh-memory/sessions/<id>/memory.mdthis conversation's memoryMemory tab, or memory add
~/.dsh-memory/sessions/<id>/inject.jsonper-conversation injection configtick boxes in the Memory tab
All historical sessionsper-conversation memory + inject config"Session memories" tab on /dsh-memory/: browse / edit / delete (the Memory tab only edits the current conversation)
~/.dsh-memory/projects/<key>/memory.mdproject experiencememory add (scope=project) or direct edit
~/.dsh-memory/USER.md / MEMORY.mdglobal pool/dsh-memory/ page or direct edit
~/.dsh-memory/memory.jsonglobal keyededit JSON directly
~/.dsh-memory/vault.mdVault semantic memoryTool add/delete (memory(vaultOp="add"/"delete")) auto-syncs this file; after hand-editing, memory(vaultOp="import") (or "Vault sync" in the admin page) writes back to the DB

Examples

Conversation A (cwd /work/projA):
- Memory tab ticks: user profile + conversation memory + project memory(projA)
- each turn injects those three blocks; conversation B never sees A's memory

agent: user prefers spaces over tabs
agent: memory(action="add", content="user prefers spaces")   # writes to conversation A's memory

agent: user asks "do you remember my indentation preference?" (same conversation)
agent: memory(action="list") → reads conversation A's memory back

agent: record project experience
agent: memory(action="add", scope="project", content="build script is build.ps1")
       # writes to /work/projA's project memory, shareable with other conversations there

agent: global keyed memory (shared across conversations)
agent: memory(action="add", scope="global", key="user-name", content="Xiaoming")

Technical design (maps to the DSH course)

PartImplementationCourse module
StorageMemoryStore / ProfileStore / SessionMemoryStore, files + atomic writesmodule ⑤ option A
VaultVaultStore + embedder (SQLite + bge-m3)module ⑤ option C
Toolsctx.tools.register(defineTool({...})), three scopesmodules ③④
Selective injectionctx.systemPrompt.context() renders inject.json per session (sub-agents inherit via parentSession)module ⑥
Multi-agent isolationnamespace + conversation/project isolationmodule ⑦
Client UIconversation.view slot id memory order 20 (right of trace), self-contained tsdown bundlemodule ⑧
Hybrid retrievalsemantic when vectors exist, keyword otherwise, merged & rankedmodule ⑤
Plugin anatomyname / inject / applymodule ②

> After code changes you need to restart DSH to load new host code (the running process holds the old modules; client bundle just needs a page refresh).

Troubleshooting

SymptomFix
Corrupted memory files (memory.json / inject.json won't parse)The plugin backs up the broken file to *.corrupt-<timestamp> in the same directory and resets to empty; the console prints the backup path — recover from the backup
Memory API returns 403Loopback guard is active (localhost only by default). Confirm DSH binds 127.0.0.1; for remote access set DSH_MEMORY_ALLOW_REMOTE=1 (evaluate privacy risk yourself)
Want automatic injection fully offDSH_MEMORY_INJECT=0, restart DSH
Host code changes don't take effectThe DSH process holds old modules — restart DSH (client bundle just needs a page refresh)
Injected content too longStatic blocks (USER/MEMORY/conversation/project) have a 100-line cap; overflow isn't injected — trim the corresponding file on the /dsh-memory/ page. The Vault has no static injection: it is recalled on demand only (auto retrieval topK=3 or the tool), so no line cap applies
Conversation/project memory files growingInjection truncates at 100 lines, but the on-disk memory.md keeps growing — trim regularly (e.g. per milestone) with the memory tool or by editing; move stale entries into the Vault archive
Hand-edited vault.md content disappearsTool add/delete (memory(vaultOp="add"/"delete")) rewrites vault.md wholesale (auto-synced mirror). Hand-edit during a quiet moment, then immediately memory(vaultOp="import") or use the management page's "Vault sync" to write back to the DB

Contributing

  • Code layout: top-level src/ = host logic + shared pure functions, src/client/ = browser side; the two artifacts build independently (host via tsdown.host.config.ts, client via tsdown.config.ts), type declarations (.d.ts) generated by tsc
  • Dev flow: edit → npm run build (full: typecheck + host + client + d.ts) → npm test all green; for client-only changes npm run bundle alone iterates faster
  • Tests always use temp directories (as src/smoke.ts does) — never touch real ~/.dsh-memory/ data
  • Before committing, run npm pack --dry-run to check publish contents (prepack hook builds automatically)

Roadmap

  • [x] v1 base: memory tool + file storage
  • [x] Builtin layer: user profile + automatic injection (context())
  • [x] Vault layer: vector semantic retrieval (bge-m3, keyword fallback)
  • [x] Conversation layer: per-conversation memory + injection config + project (cwd) memory
  • [x] Client UI: Memory tab (right of trace) + edit/injection-config panel
  • [x] True automatic vector injection: agent/pre-step retrieves topK=3 per message (tick "Auto retrieval" in the memory tab; off by default; DSH_MEMORY_AUTO_VAULT=0/1 forces globally)

License

MIT