Third-party project Independent open-source project — not affiliated with DeepSeek, Doubao or their official teams, and not authorised or endorsed by them.

DSH plugin · local-first memory

Memory that
earns its place
in the prompt

Long-term memory for DeepSeek Harness.

  • 5memory kinds
  • 3languages
  • 0dependencies
  • ~34MBsingle file
  • MITlicense
01 / video

Official promo

Two minutes, five chapters: what it is, how it works, and how the data survives.

Streaming only — the video is served for playback in this page and cannot be downloaded.

02 / features

What it actually does

Every feature exists to answer one question.

Five memory kinds

global and persona every turn.

Relevance, not bulk

Only matching entries enter the prompt.

Multi-topic coverage

Each topic gets its own representative entries.

Explicit recall

Ask for everything about a topic.

Review inbox

Inferences wait for your approval.

Token dashboard

See the byte and token split.

Model decides, with evidence

Persona entries must be dated and anchored.

Update, never duplicate

One fact, one entry.

Single-file manager

One exe with Node embedded.

03 / how it works

From one message to one prompt

Every turn re-reads the store from disk.

Read the store

Local store read every assembly (SQLite, JSON fallback).

Tokenize the message

CJK grams plus latin tokens.

Score and gate

Tag hits weigh the most.

Budget the bytes

Always-on first, then per cluster.

Render the section

Labelled sections plus index.

Nothing is injected silently.

04 / quick start

Running in three steps

Windows 10/11.

  1. 1

    Run the manager

    Double-click the exe.

  2. 2

    Attach to DSH

    Tray ▸ Attach to DSH.

  3. 3

    Restart DSH, then talk

    Bundles load at boot only.

same thing from a terminal
:: attach the plugin to every detected DSH profile
dsh-memory-manager.exe --install

:: memory page only
dsh-memory-manager.exe --start      :: --stop / --restart / --status

:: keep the store outside the plugin folder
dsh-memory-manager.exe --store D:\dsh-memory-data

:: port · language · diagnostics
dsh-memory-manager.exe --port 7900   :: refuses a port already taken
dsh-memory-manager.exe --lang zh-CN
dsh-memory-manager.exe --dir / --data / --version
05 / download

Everything is pre-hosted here

Program, source package and documentation are served from this site.

dsh-memory-manager.exe

…

The single-file manager.

dsh-thirdparty-memory-0.4.3.zip

…

Full source release.

Tutorial

Markdown · TXT · 3 languages

Install, configure, daily use and troubleshooting.

README

Markdown · TXT · 3 languages

Feature overview and configuration table.

06 / faq

The questions that come up

Do I have to restart DSH to change a memory?

No.

Where is my data stored?

One local database: SQLite (JSON fallback).

Can it delete everything at once?

No, and that is deliberate.

Does it work on a machine without Node?

Yes.

Which DSH builds are supported?

DSH Desktop, portable and CLI.

07 / changelog

Version history

What changed, release by release. Entries are kept for reference only: historical versions are not offered for download here.

  1. v0.4.3 2026-11-10
    • Concurrency: rows, settings and revision are read in one transaction, so a write can no longer accept a stale snapshot and delete a fresh entry. Reproduced with four writers (revision 101, table 100) and verified over three rounds.
    • storageInfo() no longer opens a missing database file, so the lost-store degraded state survives a page refresh instead of being replaced by an empty store.
    • Memory injection is per conversation: a subagent or a second window can no longer change which entries another session receives.
    • Delete-by-number re-checks the target and refuses with MEMORY_CONFIRM_MISMATCH when the numbering moved; ephemeral entries finally carry their session; oversized request bodies answer 413.
  2. v0.4.2 2026-11-10
    • The web delete endpoint now honours the same rule as the CLI: a delete without confirm: true is rejected with 409.
    • storageInfo no longer reports zeros. It read revision, last-backup time and vector count from an unopened database handle, so the memory page always claimed there was no backup.
    • Regression pass over every endpoint, validation limits, and 5000-entry performance (load 15 ms, injection 48 ms).
  3. v0.4.1 2026-11-10
    • Data-safety release. Every write now ends with PRAGMA wal_checkpoint(TRUNCATE), so memory.sqlite itself holds all memory: backing up that one file is enough.
    • An unreadable database no longer falls back to the old JSON snapshot silently: the plugin enters a degraded state, warns in the injected block and on the memory page, and refuses to write.
    • Migration sentinel memory.sqlite.migrated: once a store has been migrated it is never resurrected from the old JSON, even if the database file goes missing.
    • Automatic daily backup (VACUUM INTO) into backups/, one self-contained file, last 30 kept. Writes take an optimistic lock (revision), so the host and the memory page can no longer swallow each other's entries.
  4. v0.4.0 2026-11-09
    • Storage moved to SQLite (node:sqlite, WAL, zero dependencies); an existing JSON store is migrated read-only on first read, and the plugin falls back to JSON when the runtime has no node:sqlite.
    • New local vector index: character / word n-gram hashing with TF weights, vectors stored as Float32 BLOBs in SQLite, retrieval scored by cosine similarity blended with keyword hits.
    • Default data directory moved out of the plugin folder (%APPDATA%\dsh-thirdparty-memory\), so updating the plugin no longer touches memory files.
  5. v0.3.0 2026-11-09
    • Five memory kinds, an inbox for reviewing inferred entries, and keyword-relevant injection.
    • Memory page plus a single-file manager exe.
    • Documentation in three languages, English as the root.

Earlier releases: see CHANGELOG in the repository.

08 / bug fixes

Fixed bugs

Sorted by severity. Each entry states what broke, why it broke, and what changed in the code.

P levels

  • P0crash / data loss
  • P1feature unavailable
  • P2UX / edge cases
  1. P0

    Concurrent writes could silently drop an entry: rows and revision were read in two separate statements

    The host's remember tool and the memory page always write at the same time. If one committed between the read of the rows and the read of the revision, the other got old rows with a new revision; the optimistic lock accepted that stale copy and the entry that had just been added vanished — with no error. Four processes × 25 entries reproduced it: revision 101, table 100.

    fixRows, settings and revision are now read inside one transaction (BEGIN … COMMIT), so a write is always validated against the exact snapshot it read.

  2. P1

    storageInfo() created an empty database and quietly cancelled the degraded state

    The page's status call opened the SQLite file. Opening a missing path creates an empty database, so after one refresh a lost store looked like an ordinary empty store — and writes were accepted into it.

    fixA missing file is left alone; the status reports zeros instead of creating a store.

  3. P1

    One conversation's injection was selected by another conversation's text

    “Latest user message” was one process-wide value. A subagent or a second window overwrote it, so the main session's memory block was chosen by somebody else's prompt (observed: the main session asked about a plugin bug, and its injection switched to another project's memories).

    fixThe last message is cached per session and looked up through the assembly context; the process-wide fallback only accepts top-level sessions.

  4. P1

    Deleting by number could delete a different entry once the list shifted

    remove echoes the entry first and deletes on confirm=true. If a write moved the numbering in between, the confirmed number pointed at another entry and the wrong memory was deleted (reproduced: preview #2, delete #1, confirm #2 — the entry that had been #3 was deleted).

    fixThe preview records the entry id; a confirm that resolves to a different id is refused with MEMORY_CONFIRM_MISMATCH, and deleting by id skips the check.

  5. P2

    Smaller 0.4.3 fixes: ephemeral entries never injected, oversized body answered with a dropped connection

    ephemeral entries were stored without a session id, so they never matched the running session and were never injected. A request body over 128 KB ended with a connection reset instead of the intended 413.

    fixThe session id now comes from the tool call itself, and oversized bodies answer 413 after draining the request.

  6. P0

    All memories actually lived in memory.sqlite-wal; the database file was a 4 KB shell

    The WAL was never checkpointed, so memory.sqlite held no data at all: copying that one file produced an empty database, and deleting -wal lost everything — in both cases without an error.

    fixA wal_checkpoint(TRUNCATE) now runs after every write, so the main file is the real store.

  7. P0

    Any database read error silently rolled the whole store back to the pre-migration JSON snapshot

    On a read failure the loader returned memory.json — the snapshot taken before the SQLite migration — with no warning. The next write then persisted that stale snapshot, permanently deleting everything added since.

    fixA read failure now yields a degraded store with a visible warning and writes are refused; a migration sentinel stops the old JSON from being resurrected.

  8. P0

    Two writers overwrote each other: the store was replaced wholesale on every save

    Each save ran DELETE FROM entries and reinserted the in-memory copy, with no lock and no version check. The plugin always has two writers — the host's remember tool and its own memory page — so a concurrent save dropped whole entries, silently.

    fixA revision value in the meta table acts as an optimistic lock; a conflicting write is retried against a fresh read.

  9. P2

    Smaller correctness fixes in the remember tool

    search printed hit ordinals instead of global numbers, so read <number> opened the wrong entry; update skipped the 2048-byte limit; status=archived was silently rewritten to active; a global entry could not be un-pinned; and the tag 画像 was dropped on save.

    fixAll five corrected. remove now asks for a second confirmation, which is what this page always claimed it did.

  10. P0

    Store/MSIX DSH Desktop crashed on every restart after installing

    The profile's package.json was written as UTF-8 with a BOM. The host's readProfileManifest hands the file straight to JSON.parse without stripping the BOM, so it throws SyntaxError: Unexpected token '\uFEFF' and the app exits with code 1. After three consecutive crashes the host stops restarting it.

    fixThe installer writes manifests without a BOM, the reader now tolerates a BOM, and -Repair rewrites existing manifests.

  11. P1

    Replacing the plugin folder also overwrote data/memory.json inside it

    The store defaulted to data/memory.json inside the plugin's own directory, so unpacking a new version over the old one destroyed the memories together with the old code.

    fixThe default data directory now lives outside the plugin folder; existing stores are copied during migration and the source file is left byte-for-byte unchanged.

  12. P2

    attach.ps1 without -DataFile silently dropped the configured dataFile line

    Called without -DataFile, the script rewrote the profile config without that field, so the plugin quietly switched to another memory file: the old entries were still on disk but no longer read.

    fixExisting configuration is preserved; the field is only written when the parameter is passed.