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:

  1. The User is the Owner: The model can only write memory in the suggested state and can never self-improve; only manual confirmation (setStatus) can make memory effective.
  2. Observability Before Precision: Every memory is plaintext; memory_list is visible anytime; memory_forget can delete anytime; there are no “silent reefs” (hidden issues).
  3. 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:memory guide section + memory:recall recall context
  • Retrieval: BM25 keyword retrieval, CJK single-char + 2-gram splitting, content and keywords fields 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_search are used without a namespace filter, 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

  1. Install:
npm install @max-null/dsh-memory
  1. 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 embeddings implementation 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