Introduction¶
When developing agents using harnesses like DSH, a common frustration is the lack of persistence between sessions: if you tell the model in the previous round that “this project uniformly uses Vue3 <script setup>”, it will forget in a new session. A common remedy is stuffing memory into a vector database and relying on similarity search—the cost is that the memory itself becomes a string of unreadable numbers, and expired information is hidden in the library, unobservable and unfixable.
@max-null/dsh-memory takes a different path: memory is entirely plaintext, retrieval uses deterministic BM25, and writing requires passing a manual confirmation gate. Below is an introduction to its design and usage.
What is this¶
@max-null/dsh-memory is a cross-session plaintext memory plugin for DeepSeek Harness, maintained by Max-Null, under the MIT license, current version 0.6.0. It belongs to the @max-null/* plugin series and integrates with the SSID (DeepSeek · Seek Soul in Darkness) desktop experience.
It follows the DSH philosophy of “everything is a plugin”: it does not modify DSH source code, declares name / inject / apply, and is loaded by the Loader from cordis.yml.
Core Design: The User is the Owner¶
The design principles of the plugin can be summarized in three points:
- The User is the Owner: The model can only write memory in the
suggestedstate and can never self-improve; only manual confirmation (setStatus) can make memory effective. - Observability Before Precision: Every memory is plaintext;
memory_listis visible anytime;memory_forgetcan delete anytime; there are no “silent reefs” (hidden issues). - Deterministic and Cache-Safe: BM25 keyword retrieval is a pure function of storage; no LLM calls are involved.
Note that approved and injected are two independent states: confirming effectiveness is one thing, whether it is constantly injected every round is another, decided by an independent switch.
Provided Services, Tools, and Injection¶
- Services
ctx.memory:remember/list/search/forget/setStatus - Tools:
memory_save,memory_list,memory_search,memory_confirm,memory_forget,memory_update - Injection:
tool:memoryguide section +memory:recallrecall context - Retrieval: BM25 keyword retrieval, CJK single-char + 2-gram splitting,
contentandkeywordsfields separated and weighted
memory:recall injects approved + injected memory from global and the current session workspace, with a source tag [memory:<id>:<namespace>] for each entry, entering the system prompt as a single-line summary and truncated by the injection budget; when over budget, truncation prioritizes recent usage, and the omission count is visible in the panel. memory_search hits update lastUsedAt for cold/hot tracking.
Two-Layer Storage¶
Memory is physically stored in two layers based on namespace, each falling into independent plaintext JSON files:
| namespace | Default Location | Purpose |
|---|---|---|
global |
$DSH_HOME/storages/memory.json |
Cross-project personal preferences |
project |
<cwd>/.dsh/storages/memory_project_<hash>.json |
Project consensus following the repo, shared via git |
A few details:
- Both roots can be overridden via config (
globalRoot/projectRoot). - When
memory_list/memory_searchare used without anamespacefilter, they query both layers. - Old dual-prefix filenames are automatically migrated to canonical names upon opening.
Plaintext + falling inside the project folder means that project layer memory can be shared with all collaborators via git commits, and team consensus can be cemented into the repository.
Installation and Activation¶
- Install:
npm install @max-null/dsh-memory
- Add a line to
cordis.yml. The memory storage backend is registered by the plugin itself; storage / system-prompt / tools, etc., are provided by the host as peerDependencies:
- id: memory
name: '@max-null/dsh-memory'
Optional Configuration¶
Passed to the plugin in config within cordis.yml; the following can be omitted:
- id: memory
name: '@max-null/dsh-memory'
config:
injectionBudget: 1500 # Permanent injection budget (chars; null = unlimited)
summaryChars: 80 # Single entry summary truncation limit (chars)
semanticTopK: 5 # topK participating in semantic fusion (only effective when embeddings are configured)
# embeddings: { embed(texts): Promise<number[][]>, similarity? }
The default is pure BM25. Semantic fusion is a pluggable option introduced in 0.5.2: after configuring embeddings, memory_search uses BM25 + semantic RRF fusion, vectors are incrementally generated and persisted, and automatic fallback to pure BM25 occurs if embedding calls fail. The memory itself remains plaintext, and vectors are only auxiliary retrieval fields (vector, plaintext readable).
Typical Usage Flow¶
Model memory_save → status: suggested (just a suggestion, not effective)
Human memory_confirm → status: approved (audited; whether it is constantly injected depends on independent switch injected)
Human (Panel/Switch) → injected: true (injected every round, summarized + truncated by budget)
memory_search → Keyword/semantic recall of any state memory (hit marks lastUsedAt)
memory_forget → Delete anytime
Two points to note: memory_search is not limited by state; memory of any state can be recalled, but only approved + injected memory enters permanent injection; the model side only has the “right to propose” from start to finish, and whether it takes effect is always decided by humans.
Prompt Template Library (0.6.0)¶
0.6.0 adds a prompt template library managed by four tools: prompt_search / prompt_get / prompt_list / prompt_add.
.md files are the single source of truth:
- global:
~/.dsh/prompt-library/*.md - shared with workspace:
<workspace>/.dsh/prompt-library/
Templates take effect as long as they exist; the source: agent badge identifies templates added by the model; the frontend (Memory Panel “Templates” tab) and the model tool retrieval index the same index. Prompt templates are never injected into the system prompt.
Development and Verification¶
If you wish to participate in development or build it yourself:
npm install
npm run typecheck # tsc strict type checking
npm test # vitest unit tests
npm run build # produces dist/
node scripts/verify-loader.mjs # end-to-end verification that the plugin can be loaded via Loader
Applicable Scenarios and Notes¶
Suitable for:
- DSH users who want agents to remember personal preferences and project consensus across sessions
- Teams who want to share project-level consensus via git repositories
- Scenarios where memory auditability, explainability of recall, and distrust of vector black boxes are concerns
Notes:
- The plugin runs with the permissions of the current dsh process and can read/write its storage directory. Before installing any third-party plugins, it is recommended to check the source code and license first (this plugin is MIT).
- Semantic retrieval requires providing an
embeddingsimplementation in the config; the default is pure BM25. - The community plugin directory is an independent site with no official affiliation to DeepSeek or Huafan.
Conclusion¶
dsh-memory pulls “memory” back from the black box into plaintext: model proposes, human confirms, BM25 deterministic recall, every entry is searchable and deletable. If you are using DSH and suffering from session-to-session amnesia, you can try integrating it following the steps above.
- Directory: https://www.skillhub.cn/plugins/Max-Null/dsh-memory
- GitHub: https://github.com/Max-Null/dsh-memory