dsh-meow-memory
Manifest valid★ 322Cross-session memory plugin for DeepSeek Harness: seven-layer SQLite store (soul/user/project/fact/lesson/topic/rules), BM25 retrieval, per-window dream consolidation. Cross-session seven-layer long-term memory plugin.
meow-memory 🐱📝
| 中文 | English | MIT License |
|---|
A cross-session memory plugin for DeepSeek Harness (DSH).
Core idea: each workspace maintains a structured memory database (.dsh-meow/memory.db, based on node:sqlite).
The static memory handbook (data overview + tool usage + writing guidelines) is placed in the system prompt as a fixed section—
the text is constant, so it never invalidates the LLM provider's KV/context cache. Dynamic content (full soul/user, design principles,
memory navigation) is injected as a prefix of the first user message, and only long-term memory is injected on the first turn without keyword matching;
from the second turn onward, each user message undergoes keyword matching (top-2). The model uses memory_search /
memory_project as needed for deeper retrieval. Each window's primary agent organizes memories when idle ("dream")
(memories this window created + extracted), freezing its knowledge at the window's last conversation timestamp.
✨ Features
- Seven memory layers (
soul= the AI itself /user= user profile & preferences /project= project info, withsubcategory(overview/structure/decisions/quotes/ops/todo) /fact= atomic facts /lesson= lessons & corrections /topic= in-progress discussion topics with a goal statement /rules= design principles & behavioral guidelines). One SQLite table per layer; UUIDs carry a time prefix, and id order = creation order. - First-turn injection (long-term memory block): a fixed-format
===== 长期记忆 =====→【关于你】(full soul) →【关于user】(full user) →【设计原则】(global rules with importance≥2, a small set of imperative guidelines) →【记忆导引】(usage notes + a dynamic list of "user's projects" formemory_projectselection) is injected before the first user message. The memory is placed as an independent plugin snapshot message before the real user message, so the user's prompt is never rewritten. No keyword matching on the first turn (matching starts from the second turn). Even if the first user message arrives in the same batch as a plugin notification (e.g., an approval policy change notice), the snapshot is still inserted right before the real user message, and matching never runs early. - Per-message keyword matching: starting from the second user message, every real user message is matched against fact/lesson/rules/topic (scope = global + current project anchor); the top-2 hits are injected with the "Possibly relevant memories, for reference:" prefix. Matching is based on entry keywords (LLM-extracted or auto bigram) rather than full text — full-text matching is too noisy. Score = intersection × idf × coverage × Ebbinghaus decay (by memory timestamp) × importance weight × title bonus.
- Current project anchoring: passing a project parameter to
memory_remember/search/update/projectanchors the session's current project; when unanchored, matching only searches global (casual chat won't pollute results). - Cache-friendly design: the static
meow-memory:guidesection (order 130, immediately after eachtool:*description) is registered once in the system prompt — constant text, KV-cache friendly. Seen memories (injected+searched) are tracked per session (.dsh-meow/sessions/<id>.json): injection never repeats;memory_searchgreedily takes the top 5 by relevance (excluding neither seen nor memories created this session), and fills the rest from lower-ranked entries, skipping seen ones; on receiving a session compaction signal (compaction/*), seen records are released so compacted memories can be re-matched/extracted. - Re-injection after compaction: after a session is compacted (manual
/compactor auto due to token pressure), the next user message turn automatically re-injects the long-term memory snapshot + the project overview previously viewed viamemory_projectin this session + the raw text of memories written/updated by this session (all re-organized from the latest data) — the "memory" lost to compaction is recovered in a single turn; the AI won't suddenly lose memory out of compaction. - Toolset:
memory_remember(write; content/project/keywords/importance are required with errors prompting refill on missing fields, auto dedup/merge, returns read-back confirmation: keywords / project ownership) /memory_search(BM25 × recency weighting, supports level/project/status/days filters, default top10 = top 5 most relevant without excluding seen- next 5 skipping seen to fill, sorted by memory timestamp; returns a retrieval metadata view:
ownership + full id + relative time + keyword list, no raw text) /
memory_project(project overview injection paragraph: project parameter is required — which project's info do you want to see? grouped by sub-category, all non-expired entries with full id and last-updated timestamp, todo outputs "Completed:" last 5 + "To do list:", ends with memory DB & session history location notes) /memory_find_similar(dedup & conflict detection) /memory_read/memory_update(with status active/archived/stale, importance, goal, manual keyword correction) /memory_dream(manual trigger; users can also type the/dreamcommand directly in the input box).
- next 5 skipping seen to fill, sorted by memory timestamp; returns a retrieval metadata view:
ownership + full id + relative time + keyword list, no raw text) /
- Memory timestamps (
updated_at= last update time): updated when dream seals ormemory_updaterefreshes. Displayed timestamps are alwaysupdated_at; search (working view) shows relative timestamps, hit injection / memory_project (raw text view) shows relative + absolute timestamps (e.g., "2026-08-15 10:58 [2 days ago]"). - Project ownership: for globally applicable info, set project to
"全局"(distinguished from empty = unmarked); when applicable to multiple projects, separate with English commas (e.g.,"dsh, femwa") — matching/hits are judged by "contains current project name or 全局". - Per-window dream: when a window has been idle ≥
idleMinutes(default 180 minutes = 3 hours) it enters the triggerable state (replacing the old night-time window); each window whose last utterance is later than the last dream is organized by its own primary agent — processed in rounds (round 1: atomic memories project/fact/lesson/rules/soul/user, round 2: topic memories, round 3: project summary — appended when this window involves a specific project: call memory_project to re-check and condense into new long-term project memory, archive superseded old entries), project sub-heading sections, each memory gets a keywords line (for AI to verify/rewrite keywords), scope = memories created by + extracted through (injection/retrieval/memory_read) this window, using its full session context; long-stable rules are not re-reviewed repeatedly (dream.rulesReviewDaysdefault: only enter the checklist if updated within 2 days, preventing "making things up"-style updates). Peak-hour suppression (computed bytimeZone, default Beijing time):suppressWindows(default 09:00–12:00, 14:00–18:00, API peak/off-peak electricity peak hours) and thesuppressLeadMinutes(default 15) before each window start do not trigger; after peak hours end, the next check cycle auto-triggers; an in-progress dream is not interrupted. Stale windows without a live agent and older than 24h, as well as archived sessions, are not processed. /dreamcommand: Don't want to wait for the idle trigger? Type/dreamin the input box to immediately manually invoke memory organization for this window (same semantics as thememory_dreamtool, not subject to peak-hour suppression). Executed on the dsh command plane, not sent to the model; directly visible in the/completion menu; if an organization is already in progress, a clear notice is shown and no duplicate start occurs.- Skip dream organization (client side): Don't want a window's memories auto-organized? Click "Skip dream memory organization" in the "…"
menu on that session row in the left sidebar to skip; click "Unskip dream memory organization" to restore. Skipped
windows are no longer auto-dreamed by the idle timer (manual
/dreamandmemory_dreamtriggers are unaffected); the session list shows a muted gray "moon + slash" icon, clearly recognizable at a glance. Skip state persists and survives restarts; two instances sharing the same memory DB are naturally consistent in state. - Reflection: after 7 or more consecutive tool steps within a single task, the plugin asks the model whether there is anything worth remembering since the last organization.
If the last tool is
memory_*, it is considered already proactively memorized, so reflection does not repeat; cancelled turns never trigger. - Injection collapse UI (client side): first-turn long-term memory / per-message keyword hit injection text is collapsed in the frontend into a "▸ Injected memories (long-term / keyword hit)" bar (same width as user bubbles); click to expand and view the full injected text; user prompts display directly as bubbles, keeping the message flow clean from injection spam. Only plain-text messages are collapsed (messages with attachments remain as-is).
- Reflection turn collapse UI (client side): the prompt and subsequent think / tool call / report of memory reflection / dream turns are collapsed into a single bar (collapsed by default, showing "New memories: N" / "Memory dream task"); click to expand into a card for the full record — Think / tool call / context injection within the card can all be expanded for details.
- Session list dream icon (client side): in the left session list, session rows where "dream has organized memories and no new conversations
with new information since" show a pale yellow small moon 🌙; during a dream turn a white → gold breathing-light moon is shown (coexisting with the dsh
status dot, moon on the left, not confused with normal operation); sessions that have skipped dream organization show a muted gray "moon + slash"
(unskip automatically falls back; priority: breathing light > skip > moon); any new activity removes the icon. The icon is placed in the dsh session row's
status slot, left of the status dot — only appending / removing its own nodes, never rewriting React-owned child nodes (whole-slot replacement would desync
React's virtual DOM, causing commit to throw removeChild NotFoundError and unmount the entire sidebar tree).
Data flows through the page-wide shared 60s polling diff (v0.23.0 connection pool fix, replacing the old SSE long-lived connection):
one GET each for
/meow-memory/dreamed-sessionsand/meow-memory/skip-dreams, event semantics unchanged — dream start pushesstate:'dreaming', completion pushesstate:'dreamed', new activity pushesstate:'active', skip toggle pushesstate:'skip'/'unskip'; on client mount, a full reconciliation is done once. Row location with zero dsh changes: read React 18 fiber (__reactFiber$internal property) to get the session row render key = session id, no dependence on title matching. - Dream dedup: check gate (DB atomic 60s check throttling) + start idempotent claim (
dream_pending) + interruption self-healing (unfinished dreams auto-complete) + orphan cleanup (turn end after cross-instance / hot reload can still close out); plugin injection turn events do not refresh window activity — already-dreamed windows are not repeatedly dreamed. - Zero runtime dependencies:
node:sqlite(Node ≥22.13 available by default; 22.5–22.12 needs--experimental-sqlite) + self-contained esbuild artifact (lib/index.js). No native modules.
📦 Installation
One-click install (recommended)
dsh plugin --profile web add github:Phant0Meow/dsh-meow-memory
One command to install and it just works: auto-compiles on install (the prepare script is bundled), auto-mounts, and after restarting dsh web, new sessions load the plugin automatically.
pnpm ≥10 blocks install-time build scripts by default: the first
addmay fail with anallowBuildsnotice — add the keys it prints to the profile'spnpm-workspace.yamland re-run.
Uninstall
dsh plugin --profile web remove meow-memory
Manual install (developers, any DSH installation, no npm required)
- Copy (or symlink) this package into the profile's
node_modules:
(Windows:mkdir -p ~/.dsh/profiles/web/node_modules ln -s /path/to/meow-memory ~/.dsh/profiles/web/node_modules/meow-memoryNew-Item -ItemType Junction ...— NTFS junction, no admin rights required.) - Add
meow-memoryto the profilepackage.json'sdsh.profile.bundles(same as above). - Restart
dsh web. New sessions automatically load the plugin.
🔌 Compatibility
Supports dsh 0.1.5 (including the latest 0.1.5-rc.1), and remains backward-compatible with older versions — upgrading dsh requires no changes to this plugin and no configuration changes at all.
No hardcoded version numbers; the plugin probes host capabilities at runtime, so old and new versions each follow their own branch. Both generations tested: on 0.1.5-rc.1 initial injection, memory tool calls and client-side rendering all work; on 0.1.1-rc.2 behavior is identical to older versions.
⚙️ Configuration
All fields are optional (profile patch or cordis.patch.yml). No need to hand-edit files: the DSH settings page has a "Meow Memory" tab for this plugin (at the same level as "General" and "Model"); all the items below are editable via the GUI with per-field save and per-item reset-to-default (reset = back to the plugin's factory default, independent of the patch baseline); after saving, hot-reload or restart the meow-memory plugin to apply.
- id: meow-memory
name: 'meow-memory'
config:
enabled: true # 总开关
projectDir: '.dsh-meow' # 记忆目录(相对工作区)
promptLang: 'zh' # ⚠️ 首次使用建议显式配置(见下方说明)
hitTopK: 2 # 每条用户消息关键词命中的条目数上限(fact/lesson/rules/topic)
reflect: true # 连续 ≥reflectTurns 轮工具调用后自动反思
reflectTurns: 7 # 触发反思所需的连续工具轮数
dream:
enabled: true
idleMinutes: 180 # 窗口空闲 ≥180 分钟(3 小时)允许 dream
suppressWindows: # 峰时抑制时段(按下方 timeZone 计算,"HH:MM" 起止)
- start: '09:00' # API 峰谷电价峰时
end: '12:00'
- start: '14:00'
end: '18:00'
suppressLeadMinutes: 15 # 每个峰时开始前 15 分钟也不触发
checkMinutes: 15
timeZone: 'Asia/Shanghai' # 用户机器时钟为美区时间;抑制时段必须
# 按此固定时区计算
rulesReviewDays: 2 # updated_at 距今超该天数的稳定准则不进 dream 第 1 轮
# 清单(防反复整理不变化的条目);0 = 不过滤
delegate:
model: '' # 整理任务换模型(可选):填写后反思轮与梦境轮自动换用
# 该模型执行,轮次结束自动换回主模型;'provider/model'
# 指定 provider+model,'model' 只换模型(provider 继承
# 主会话);留空 = 全程主模型
Housekeeping task model switch (optional)
Reflection rounds and dream tasks always run in the main window (steer) — prompts, model responses, and tool calls land in the main session log (the collapsed UI handles visual tidiness). The standalone fork sub-agent execution mode was removed in v0.24; the "standalone execution" toggle is no longer available.
To let memory housekeeping run on a (cheaper) model: configure delegate.model, and every LLM request issued by reflection/dream turns will be automatically overridden via dsh's agent/request waterfall to switch provider/model, then automatically restored to the main model when the turn ends — normal conversation and tool turns are completely unaffected. The implementation is stateless: it judges per request based on "whether the current turn carries the reflection/dream instruction marker", so user aborts, crashes, and hot reloads never leave a dirty "stuck on the switched model" state.
promptLang: prompt & retrieval language (important)
promptLang controls two things: ① the language of injection / reflection / dream text; ② the language of tool descriptions. It also shapes the language the model writes memory entries in — keywords are extracted per entry language, so your spoken language wins.
So please configure it explicitly on first use: promptLang: 'zh' (default) or 'en' (built-in English language pack). If your conversation language differs from your UI language (e.g., UI in English, speaking Chinese), your spoken language takes precedence.
Retrieval-side note: BM25 tokenization has been language-agnostic since v0.20.0 (category routing), so mismatched entry/query languages no longer "break retrieval"; en mode additionally enables English normalization (stop-word filtering + Porter stemming), so inflected forms still match (tokenizers hits entries stored as tokenizer).
Custom / community language packs: prompt copy is a data file (src/prompts/), one subdirectory per language — edit files to take effect, no code changes needed — see src/prompts/README.md for details (including contribution guide and the npm run check-lang self-check). Per-instance customization: place same-slot-name files under <home>/.dsh-meow/prompts/<lang>/ to override (partial override allowed).
🧠 How it works
第一条用户消息(首轮) 第二条起的每条消息 空闲≥3h 且非峰时
┌────────────────────┐ ┌────────────────────┐ ┌──────────────────────┐
│ ===== 长期记忆 ===== │ │ 可能相关的记忆,仅供 │ │ 按窗口 dream: │
│ 【关于你】(soul) │ │ 参考:keywords 命中 │ │ 三轮(含项目总结) │
│ 【关于user】 │ │ top-2(全局+当前 │ │ 七层+提取过的, │
│ 【设计原则】(rules) │ │ project 锚定) │ │ updated_at 以 T 封存 │
│ 【记忆导引】 │ └────────────────────┘ └──────────────────────┘
└────────────────────┘ 已见 id 按会话记录
独立 plugin snapshot (sessions/<id>.json)
↓ 原样 user prompt 压缩信号 → 释放已见
每会话只注入一次,
首轮不做命中
🛠 Development
npm install
npm run build # esbuild 打包 → lib/index.js(自包含)
npm run test # 228 项逻辑测试:db / bm25 / migrate / inject / reflect / dream / tools / apply
@deepseek-ai/* packages live in the dsh-meow pnpm workspace, not in this package's node_modules.
On Windows, npm run link-workspace (or scripts/link-workspace.ps1) creates junction
mirrors of the workspace packages so esbuild can resolve them; build.mjs references them via nodePaths.
These links are only needed at build time.
🙏 Acknowledgments
Thanks to every contributor who has made meow-memory better:
- daveycodez — English language pack and English tokenization (PR #6, released in v0.22.0)
- chenmzh — memory injection changed to an independent plugin snapshot message, fixing session title pollution (PR #10)
- cuddly-guacamole — dual-version Session events compatibility for dsh 0.1.2-alpha.4 (PR #11)
📄 Licence
MIT — see LICENSE.
Compatibility
Versions
| Latest version | Published | Size |
|---|---|---|
| 0.1.0 | — | — |
| 0.2.0 | — | — |
| 0.5.1 | — | — |
| 0.10.0 | — | — |
| 0.9.0 | — | — |
| 0.11.0 | — | — |
| 0.17.0 | — | — |
| 0.19.0 | — | — |
| 0.23.0 | — | — |
| 0.24.2 | — | — |
| 0.26.0 | — | — |
| 0.21.0 | — | — |
| 0.25.0 | — | — |
| 0.22.0 | — | — |
| 0.25.1 | — | — |
| 0.24.1 | — | — |
| 0.20.0 | — | — |
| 0.27.0 | — | — |
| 0.24.0 | — | — |
| 0.15.0 | — | — |
Comments
Loading…
Similar plugins
by chenhw7
Cairn — long-term memory for the DeepSeek Harness (@chenhw7/dsh-memory). Persistent cross-session memory: facts, preferences, corrections, lessons. BM25 retrieval, three-layer scoping, human-confirmed
★ 3
↓ 114/wk
MIT
TypeScript
Sep 10, 2026
dsh plugin --profile web add @chenhw7/dsh-memoryby chucan1
Cross-session self-learning memory for DeepSeek Harness, ported from XT-AGENT packages/memory. BM25 relevance injection + background extraction (sanitize/dedupe/merge) + lifecycle archive + memory_rea
★ 0
MIT
TypeScript
Sep 4, 2026
dsh plugin --profile web add dsh-plugin-xt-memoryby guntur-d
Persistent cross-session memory plugin for DeepSeek Harness: workspace-scoped durable facts with memory_write/list/search/forget tools and bounded prompt injection
★ 0
TypeScript
Aug 14, 2026
dsh plugin --profile web add @guntur-d/dsh-memoryby Max-Null
Cross-session plaintext memory for DeepSeek Harness: suggested → human-approved, searchable, human owns the data · 跨会话明文记忆:模型写入待审核、人工确认生效,明文可审计
★ 6
↓ 797/wk
MIT
TypeScript
Oct 25, 2026
dsh plugin --profile web add @max-null/dsh-memoryby Qinling-Melon-Farmers
DeepSeek Harness (DSH) local-first cross-session project memory: zero bundled runtime deps, bounded cache-friendly Hot Memory, BM25 recall/cache, provenance, lifecycle and bilingual Web GUI; no embedd
★ 22
↓ 788/wk
Apache-2.0
TypeScript
Aug 24, 2026
dsh plugin --profile web add dsh-memoirby yan5236
Two-phase long-term memory plugin for DeepSeek Harness (DSH): per-session extraction + global consolidation, always-injected summary, and memory_list/read/search/add tools.
★ 3
MIT
TypeScript
Aug 25, 2026
dsh plugin --profile web add @nanmicoder/dsh-memory