DSH Plugins Marketplace

DSH Plugins

Plugins

/

dsh-memory-delta

l

dsh-memory-delta

Manifest valid

给 AI 编码助手的跨会话长期记忆:自动注入、只推变化。DSH 插件 + 零依赖 CLI。

UI (client)hasBundlePatch

dsh-memory-delta

English | 中文

Layered, auto-injected cross-session memory for AI coding agents. A DSH (DeepSeek Harness) plugin, plus a zero-dependency standalone CLI. It borrows the spec / change / archive discipline from OpenSpec — but pushes instead of pulls.

Canonical repository: GitHub · Gitee is a read-only mirror — please file issues and pull requests on GitHub.

Status: M1–M4 done; M3/M4 (differential injection, both tools, the distillation nudge) were verified inside a real DSH session, and the M5 improvements below are covered by 419 assertions plus a real-machine preflight. See Verification.

Why

AI coding assistants have two recurring problems:

  1. A new session remembers nothing. You re-explain the background, your preferences, and every conclusion you already reached.
  2. What does get remembered is unmanaged. Everything piles into one or two Markdown files that grow without bound, cost more every session, and — worst of all — stale conclusions are never removed.

Existing spec-driven tools (OpenSpec and friends) solve "the code drifts away from the plan". But they are pull-based: the agent has to be told to go read the specs, so a fresh session does not spontaneously remember anything. dsh-memory-delta is push-based: at session start the agent is handed what it should know — but only the distilled part, and only what changed. Details stay retrievable on demand.

Design

        ┌─ PUSH: injected automatically at session start (hard byte budget)
        │    T0 identity & conventions   user preferences / machine facts / accounts
        │    T1 index & next actions     what to pick up
store ──┤
        └─ PULL: retrieved on demand (costs nothing by default)
             inbox/      candidate entries — **the only layer the model may write**
             facts/      current truth (only status=active is injected)
             decisions/  choices plus their reasons (append-only)
             archive/    superseded entries
             journal.md  activity log (never injected)

Seven rules:

  • The pushed part must be tiny. Everything in the injected layer is paid for on every session, so the journal and design docs stay out of it. Measured on a real store (6 entries): 953 bytes total, 68% of it the entry lines themselves, ~300 bytes of framing — about 159 bytes per entry, so the 3 KB default budget holds ~19 entries. Entry ids are deliberately not written into the text (they ride along in the message's structured source.entries); inlining them used to eat 34% of the budget.
  • Only the delta is pushed. Every entry carries a 12-char content hash; the plugin remembers the previous round's state and next round pushes only added / updated / removed. When nothing changed it injects nothing at all. (The upstream dsh-agent-instructions plugin has no diffing: any file change re-injects the whole file — measured at ~58k wasted tokens for 15 edits of one 8.5 KB file.)
  • State is recovered from the conversation itself. No side-car state file: the plugin reads back the {id: hash} map from the message it previously injected, so session resume, replay and compaction all stay correct.
  • The model may only write to the inbox. A wrong conclusion that silently reaches the standing layer gets re-injected forever. Promotion is an explicit promote.
  • One key, one truth. Facts and decisions carry a semantic key, and only one active entry may exist per scope+key. A new conclusion must explicitly --supersedes the old one — that gate is what keeps memory rot out of the injected layer.
  • Entries have state: active / superseded / expired. Superseded entries get bidirectional links and are archived, never appended forever.
  • Plain Markdown + frontmatter: human-readable, diffable, reviewable, committable like code.

Install (as a DSH plugin)

⚠️ This section is the result of real trial and error — both wrong turns below are silent failures:

❌ Adding the package to package.json's dsh.profile.bundles
   → DSH regenerates that list from the market registry (.generations/desired.json) at boot;
     entries it does not know about are dropped.

❌ Writing a bare entry in cordis.patch.yml
   → silently ignored (a patch entry only targets an existing id for config/disable).

✅ Wrapping it in `- insert:` inside cordis.patch.yml

Steps (DSH_HOME is usually %APPDATA%\dsh-desktop\harness):

  1. Copy this package into the profile's node_modules:

    <DSH_HOME>\profiles\web\node_modules\dsh-memory-delta\
        package.json
        bin\mem.mjs
        src\plugin.mjs  src\hook.mjs  src\planner.mjs
    
  2. Append to <DSH_HOME>\profiles\web\cordis.patch.yml:

    - insert:
        - id: dsh-memory-delta
          name: dsh-memory-delta
          config:
            root: ''          # empty = <session cwd>/memory
            maxBytes: 3072    # byte budget for the baseline injection
            enabled: true
    
  3. Save. The patch layer is watched (watchUserPatches) — it hot-reloads, no restart needed.

Uninstall: remove that - insert: block and delete node_modules\dsh-memory-delta.

The market/registry publishing flow was not investigated yet; the above is the local install path.

What the plugin provides

CapabilityDetail
Differential injectionFirst round injects every active entry (baseline); afterwards only added / updated / removed; nothing at all when unchanged
memory_searchRelevance-ranked search across facts / decisions / inbox / archive / journal / session index. Field weights (key/id > tags > conclusion > body), a whole-phrase bonus, and Chinese matched by bigram so a query like 沙箱禁管道 hits 沙箱禁止命名管道 without spaces. Each hit carries a score and a snippet from its best-matching line
memory_writeRecord a candidate into the inbox — the model cannot touch the standing layer
Distillation nudgeOnce a session has run a few steps and memory is already current, it reminds the model to record conclusions with memory_write; one nudge per session, and the nudge message carries no state, so it cannot corrupt the diff baseline
Due-for-review reminderverify_when is no longer a dead field: when an entry reaches its review date, the session is told once — "this conclusion may be stale, re-check it" — with the exact command to supersede or expire it. Prose values (等换机器时) never trigger it, so the reminder can always be resolved; it fires only on a step that injects nothing else, and it carries no state either
Sidebar memory tabWith dsh-better-sidebar installed, a 记忆 tab lists the standing entries, the due-for-review items and the inbox candidates, plus the current injection size. The client half is a hand-written, zero-build browser bundle (a window.__ModuleLoader__.load({id, factory}) wrapper, no bundler); its data comes from a read-only POST /dsh-memory-delta/state route owned by this plugin — loopback-only, JSON in / JSON out, and it reads nothing but the memory store

The plugin never spawns the CLI: the DSH sandbox forbids named pipes (capturing a child's output fails with EPERM), and there is no need — it imports the same store module directly (bin/mem.mjs only runs the CLI when executed as the entry point). It also never requires the sidebar: webServer is read through ctx.get('webServer') (an optional capability), so a headless or CLI-only composition loads the plugin unchanged and simply skips the panel route.

CLI usage

# init (defaults to <cwd>/memory; override with --root or $DSH_MEMORY_ROOT)
mem init --root ./memory --scope "workspace:/path/to/project"

# record a candidate (lands in inbox, never injected)
#   --id  prefer an explicit short id; otherwise derived from the conclusion (capped at 20 chars)
#   --key semantic key: only one active truth per scope+key
mem new --type fact --id win-update-cache --key disk-cleanup \
        --conclusion "Cleaning the update cache reclaimed nothing measurable" \
        --reason "Directory emptied but free space did not move" --tags windows,disk --source session-abc

# promote it once confirmed; when the key already has an active entry you must say who supersedes whom
mem promote win-update-cache
mem promote win-update-cache-v2 --supersedes win-update-cache

mem set <id> --key k --tags a,b --conclusion "…"   # edit an entry (add a key, reword, mark expired)
mem list --status active --tag windows
mem show <id>
mem validate [--fix]   # format / ids / bidirectional links / cycles / same-key conflicts / index / budget
mem index              # rebuild index.md
mem inject [--json] [--budget 3072]   # render what should be injected; --json adds per-entry hashes
mem recall <keywords> [--where all|facts|decisions|inbox|archive|journal|sessions|index] [--limit N] [--json]
                       # relevance-ranked: Chinese is matched by bigram, no spaces needed
mem due [--within N] [--json]   # entries whose verify_when is due (--within N also warns N days ahead)
mem journal add "one line"

verify_when takes either a date (2027-03-01) or a relative phrase measured from the entry's own date (3个月后, 2周后, 立即); anything else is treated as prose and simply never auto-fires.

Verification

Checked item by item inside a real DSH session:

CapabilityLive evidence
baseline injectionthe session received every active entry
no change → zero injectionthe next step injected nothing, only the one-time nudge
delta · added"新增:", explicitly noting "the other N entries are unchanged"
delta · updatedafter editing one entry, only "已更新:" was pushed
due-for-review reminderadding an entry whose verify_when was 16 days overdue produced a one-time form='due' reminder on the next no-change step, and the step after it injected nothing (the reminder did not reset the diff baseline)
sidebar 记忆 tabthe tab opened on a live store and showed the real root, "常驻 12 条", "注入 1792 / 3072 字节", the facts/decisions split and the (empty) inbox
memory_search / memory_writeboth called successfully in the real runtime
writes land only in the inboxthe written candidate did not enter the injection payload; it appeared as a delta only after promotion

Development

npm test        # 419 assertions, zero dependencies
SuiteAssertionsCovers
test/run-tests.mjs109CLI end-to-end (incl. a non-ASCII path regression, ranked recall, mem due)
test/planner-tests.mjs43the diff algorithm (pure logic)
test/search-tests.mjs51tokenizing / scoring / snippet selection (pure logic)
test/due-tests.mjs93verify_when parsing (dates, relative phrases, prose) and due collection (pure logic)
test/hook-tests.mjs63plugin wiring (fake agent / decision): diff injection, nudge, due reminder
test/plugin-tests.mjs60plugin integration (stubbed DSH modules, real apply() + both tools)

test/plugin-tests.mjs replaces the four @deepseek-ai/* packages with the stubs in test/stubs/ (via test/stub-loader.mjs) and actually apply()s the plugin, so its behaviour is verifiable without a DSH installation. test/preflight-import.mjs goes one step further: run it from inside a profile and it exercises the real @deepseek-ai/* modules (does the real defineTool accept our tool definitions, does the real schemastery accept our config schema).

Regression tests baked in from real bugs:

  • With a non-ASCII path, Node's fs.rmSync fails silently (and can crash the process with recursive) — unlinkSync must be used instead;
  • The DSH sandbox forbids named pipes, so spawnSync with the default stdio: 'pipe' hits EPERM — tests must redirect child output to a file;
  • An entry written by the tool must carry the session workspace scope, not the harness process cwd;
  • new URL(import.meta.url).pathname percent-encodes a non-ASCII user name (C:\Users\李鹏飞C:\Users\%E6%9D%8E%E9%B9%8F%E9%A3%9E), which turns "write into my plugin folder" into "write into a path that does not exist" — always use fileURLToPath;
  • A field added to some early-return paths of an internal planner function (due) was destructured into undefined and threw on every step, which the outer try/catch silently reported as "failed to load memory" — hence the defensive read and the zero-warning assertion.

Roadmap

  • M1 ✅ CLI + structured entries + validate + index/injection budget
  • M2 ✅ explicit short ids, semantic keys and "one key one truth", inject --json diff payload, validate --fix, mem set
  • M3 ✅ DSH plugin: differential injection + both tools + the distillation nudge (verified live)
  • M4 ✅ published (GitHub primary / Gitee mirror)
  • M5 ✅ the memory got usable at scale: index-style injection (id-free text, ~159 bytes per entry), relevance-ranked search with Chinese bigrams, and verify_when turned into a real due-for-review reminder
  • Next official distribution (plugin market) and a memory tab in the DSH sidebar

License

MIT

Versions

Latest versionPublishedSize
1.0.0

Comments

Loading…

Similar plugins

dsh-layered-memory

by JunNanLYS

让 DeepSeek Harness 拥有跨会话长期记忆:AI 自动记住你是谁、你的项目和偏好,新会话直接带上背景,生活与工作记忆自动分开互不干扰,零配置无感运行 | Long-term memory for DeepSeek Harness: the AI remembers who you are, your projects and preferences across sessions,

Memory & ContextSessions & MessagesManifest valid

16

476/wk

MIT

TypeScript

Sep 7, 2026

dsh plugin --profile web add dsh-layered-memory

by Aik358

Proactive associative memory for DSH: zero-prompt recall injected before the model speaks, three-layer auto-consolidation, skill crystallization, and Astra-style context management - handoff ledgers,

Terminal & ClientsMemory & ContextSessions & MessagesManifest valid

71

BSD-3-Clause

JavaScript

Sep 19, 2026

dsh plugin --profile web add @a9i5k4/dsh-auto-memory

On-demand memory for DeepSeek Harness: session logs are distilled into layered markdown (a summary, an index and per-topic files), and a mem_query tool returns only the matched entries rather than the

Memory & ContextManifest valid

0

dsh plugin --profile web add dsh-memory-lite

by LittleBlackTong

Long-term cross-session markdown memory with an LLM-Wiki structure and a SOUL.md persona, injected at session start (by default only after the first user message and only in the active session), plus

Memory & ContextManifest valid

3

485/wk

MIT

JavaScript

Sep 9, 2026

dsh plugin --profile web add dsh-plugin-memory

by yangyongzhen

Long-term memory injected at session start: preferences/facts/summaries/knowledge in global and per-project scopes, budgeted recall via `agent/pre-step`, durable JSON store.

Memory & ContextSessions & MessagesManifest valid

1

538/wk

JavaScript

Aug 15, 2026

dsh plugin --profile web add dsh-memory

by SiriusWJ

DSH 简化版记忆插件:SQLite 条目化记忆(标题/重要程度/来源/内容,增删改查)+ 日历与到点提醒(月视图+时间轴+待执行/已完成双tab),对话面板记忆 tab 与设置二级菜单,中英双语自动跟随。

Memory & ContextSessions & MessagesManifest valid

2

BSD-3-Clause

JavaScript

Sep 15, 2026

dsh plugin --profile web add dsh-lite-memory