Memory

dsh-memory

vpromise/dsh-memory · ★ 3

vpromise

Persistent context across sessions for every agent: a DeepSeek Harness Cordis plugin that captures activity, c

License
MIT
Added
2026-08-23
Maintenance
active
Install kind
github

No manifest check is available; listing does not imply compatibility or security review.

安装

dsh plugin --profile web add github:vpromise/dsh-memory

GitHub 源码安装:首次需按提示配置 allowBuilds 构建授权后重试

For install and environment setup, plus plugin development tutorials, see DSH community docs ↗

README

Source README · body: en · Read Chinese README

Table of contents
  1. Why
  2. How it works
  3. Installation
  4. Configuration
  5. Storage
  6. Development
  7. How it compares to claude-mem
  8. License

Persistent context across sessions for every agent.

English | 中文

A DeepSeek Harness plugin (Cordis) that gives every agent long-term memory: it watches each session's activity, distills it into durable semantic memories with the LLM, stores them per project, and injects the relevant ones into future sessions automatically.

Inspired by the core loop of claude-mem — capture session activity → compress it with the LLM → inject relevant context into new sessions — but implemented from scratch on DeepSeek Harness primitives: native session events, the agent lifecycle waterfall, and the built-in LLM service. No external processes, no database servers.

license version

Why

  • Every session starts cold. dsh-memory closes the loop: what one session learned — decisions and their reasons, user preferences, codebase facts, solved problems — is automatically recalled by the next one.
  • Zero infrastructure: one JSONL file per project under $DSH_HOME/memory, an in-memory index, and the LLM you already have. Nothing to install beyond the plugin itself.
  • Model-callable tools (memory_search / memory_save / memory_list) let agents explicitly query and write the same memory store.

How it works

  1. Capture — subscribes to each session's persisted event stream (session/event):
    • direct human prompts (user/message, only source.kind === 'user', so plugin-injected context is never re-memorized),
    • assistant reply summaries (assistant/message, truncated),
    • tool calls and their results (tool/call + tool/result, paired by callId, with failure flags).
  2. Compress — at the end of each turn (or when the buffer reaches its threshold), the buffered observations are sent to the LLM (reusing ctx.llm.stream), asking for a JSON array of [{text, tags}]. The request routes through the agent's default model, or an explicitly configured provider/model. Every failure is logged and silently dropped — compression never blocks or breaks the agent loop.
  3. Store — one JSONL file per project ($DSH_HOME/memory/<project>-<sha1-8>.jsonl), one memory per line with id, text, tags, timestamp, and source session. The index lives in memory (synchronous reads), appends are serialized, and corrupted lines are skipped without errors.
  4. Inject — in the agent/pre-step waterfall, on the first step and every N turns, the most recent memories plus the memories relevant to the current prompt's keywords are rendered into a system-reminder frame and folded into the step batch as a persisted user message with source.kind === 'memory', form recall, right after the direct prompt. Byte-budgeted; the same payload is never injected twice into the visible surface.
  5. Tools — memory_search, memory_save, and memory_list are exposed as model-callable tools so agents can explicitly query and write the same memory store (the equivalent of claude-mem's MCP search tool).

Installation

Requirements: a running DeepSeek Harness installation. Building from source requires Node.js ≥ 20.

The published bundle dist/index.js is self-contained: it uses only Node built-ins plus a bare side-effect import of @deepseek-ai/cordis, a peer dependency that every DSH installation already provides. The bundle is committed to this repository, so no build step is needed to install.

  1. Clone the repository (or download the source of the latest tag):
    git clone https://github.com/vpromise/dsh-memory
    
  2. Add one entry to your profile patch file:
    - insert:
        - id: memory
          name: /path/to/dsh-memory/dist/index.js
    
    • web profile on this machine: $DSH_HOME/profiles/web/cordis.patch.yml
    • all profiles: $DSH_HOME/cordis.patch.yml
  3. Restart the DSH host. Memory context appears before the first reply of a new session; when a session ends (turn/end — natural completion or a full buffer), the background compression runs and the memories are visible to the next session.

One-shot headless CLI note: a long-running host (the web GUI, etc.) compresses reliably in the background. A one-shot headless CLI only gets about 4 seconds of drain at exit (bounded by the CLI's 5-second graceful shutdown), so an in-flight compression request that has not returned by then is dropped. Explicit memory_save calls are unaffected — they persist synchronously.

Configuration

All fields have defaults; the config block can be omitted entirely.

Field Default Meaning
enabled true master switch
autoCapture true capture session events
autoCompress true compress buffered observations with the LLM
autoInject true inject memories into new/continued sessions
maxInjectedBytes 4096 max bytes of context injected at once
maxRecentMemories 6 recent memories injected first
maxRelevantMemories 4 additional memories matched by keyword relevance
refreshEveryTurns 6 re-inject every N turns within the same session
compressAfterItems 5 buffer size that triggers compression
maxCompressInputBytes 24000 upper bound of one compression input
maxObservationChars 3000 per-observation character cap
maxMemoryChars 800 per-memory character cap
maxOutputTokens 1024 compression output token cap
timeoutMs 60000 compression request timeout
provider / model empty compression route; defaults to the agent's model
storageDir empty memory directory; defaults to $DSH_HOME/memory

Storage

  • The project root is detected by walking up for .git, .hg, .svn, or pnpm-workspace.yaml.
  • One JSONL file per project: $DSH_HOME/memory/<basename>-<sha1-8>.jsonl; sessions without a project root write to global.jsonl.
  • Files are plain JSONL — view and edit them directly; delete a line and restart to make it disappear.

Development

pnpm install        # dev deps (tsdown / vitest / typescript)
pnpm test           # 46 unit + integration tests
pnpm typecheck      # tsc --noEmit
pnpm build          # emits dist/index.js (self-contained ESM)
  • Type resolution: tsconfig.json maps @deepseek-ai/* to the built lib types of your local DeepSeek Harness checkout; vitest aliases to the checkout sources via vitest.config.ts.
  • Integration tests drive the full loop — capture → compress → persist → inject → dedupe → tool read/write — against a mock LLM adapter, and verify graceful degradation when the llm/tools services are missing.
  • tests/dist.spec.ts checks the bundle against the loader contract (name / inject / Config / apply).
  • Real loader smoke test: with DSH_CHECKOUT pointing at a compiled checkout, run from smoke/:
    DSH_CHECKOUT=/path/to/deepseek-harness node smoke.mjs
    
  • Real-LLM e2e: from the repo root, a real deepseek-v4-flash call through the plugin's compression pipeline. Credentials come from DEEPSEEK_API_KEY or DSH_CREDENTIALS pointing at a .credentials.yaml:
    DSH_CHECKOUT=/path/to/deepseek-harness node e2e/compress-live.mjs
    

How it compares to claude-mem

claude-mem dsh-memory
5 lifecycle hook scripts native session/event + agent/pre-step events
standalone Bun worker + HTTP API in-process Cordis plugin, zero external processes
SQLite + FTS5 + Chroma vector store per-project JSONL + in-memory index + token-overlap scoring
Stop hook triggers compression turn/end triggers compression, serialized tail dedupe
SessionStart hook injection folded into the agent/pre-step waterfall with a recall context form
MCP search tool memory_search / memory_save / memory_list
per-project worker data per-project JSONL file

License

MIT — see LICENSE.