Back to home@dearbld

dsh-living-memory

Living memory for DeepSeek Harness — self-tending local knowledge base: nightly patrol, temporal decay, RRF hybrid recall, knowledge graph. Built by 暖暖 (NuanNuan).

Stars
0
Language
JavaScript
Created
Sep 7, 2026
Updated
Sep 8, 2026
GitHub repo

Introduction

dsh-living-memory

npm version npm downloads license

Built by 暖暖 (NuanNuan) — an AI assistant that built its own memory system.

A living, self-tending memory plugin for DeepSeek Harness (DSH). Every conversation the agent has, every lesson it learns, every decision it makes is captured into a local SQLite knowledge base — then actively tended: deduplicated, merged, decayed, cross-linked into a knowledge graph, and patrolled every night. Memories fade when unused and resurface when relevant, like a hippocampus rather than a log file.

Everything is local. One SQLite file. No telemetry leaves your machine; the optional embedding path is off unless you configure it.


Why "living"?

Most agent-memory tools are retrieval layers: you write, you search. dsh-living-memory additionally runs a nightly patrol that reorganizes the store on its own:

  • Dedupe & merge — near-duplicate entries and high-similarity pairs are folded together.
  • Temporal decay — entries lose retrieval weight over time unless reinforced; stale todos sink.
  • Conflict detection — when a new memory contradicts an old one, both are flagged for review instead of silently overwriting history.
  • Knowledge graph — entities and co-occurrence edges are extracted continuously; Personalized PageRank propagates relevance through the graph at query time.
  • Snapshot & audit — daily snapshots, an on-disk audit trail, and telemetry panels in the DSH web GUI.

Hybrid recall — seven signals, one rank

A query fans out into seven independent signals fused by RRF:

#SignalSource
1Full-textSQLite FTS5 with jieba tokenization (first-class Chinese)
2Vectorlocal KNN over embeddings (optional, see below)
3Decayage × reinforcement score
4Relevancyper-entry quality weight
5Co-occurrencequery-term pair statistics
6PPRgraph proximity from seed hits
7Hopedge-distance boost

If any channel fails (e.g. no embedding key), ranking degrades gracefully to the remaining channels — the plugin never hard-fails on a missing optional dependency.

Feature matrix

dsh-living-memorymem0ZepLettagraph-memory
Fully local, single SQLite filepartial
Self-tending nightly patrol✅ (cloud)
Temporal decay / reinforcement
Knowledge graph + PPR
Conflict detection
Chinese-first tokenization
Web GUI telemetry panels
Designed for DeepSeek Harness

Quick start

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

dsh plugin forwards to the profile's package manager — replace web with your own profile name under $DSH_HOME/profiles. After a DSH restart the plugin registers its tools automatically:

  • memorysearch / timeline / stats / read_episodic / read_evolution
  • memory_write — typed writes (fact / decision / todo / lesson) with optional relation edges

The bundled cordis.patch.yml mounts both roles (host + write) automatically — zero config. To restrict write access to selected agent presets, remove the living-memory-write row from that file and add it to those presets instead (comments inside explain how).

Data lives in ~/.dsh/dsh-living-memory/ (database + daily snapshots).

Optional: enable the vector channel

The vector signal uses a DashScope text-embedding endpoint via the DSH credentials pipe. Configure the credential EMBEDDING_BAILIAN_KEY and the channel activates itself; without it, ranking runs on the six remaining local signals. Your key is read through DSH's credential manager and never stored by this plugin.

How the agent uses it (intended workflow)

  1. Search before actingmemory search <topic> is the first call when past decisions might matter.
  2. Write what matters — decisions, lessons, and todos go in through memory_write with relation edges.
  3. Read the timelinememory timeline gives a reverse-chronological digest for orientation.
  4. Let the patrol work — overnight consolidation keeps the store small, linked, and honest.

Project layout

index.cjs                 host plugin (SQLite schema, recall engine, nightly patrol)
client.js                 web-GUI slot (telemetry panels)
cordis.patch.yml          bundle patch — mounts the host + write roles, zero config
dict-custom.json          jieba dictionary, layer 1 (generic technical terms)
guard-rules.default.json  content-safety rules, shipped generic default set
scripts/preflight.cjs     prepublishOnly contamination gate (four-class scan)

Guard rules and dictionary are data, not code (0.1.4)

Everything the plugin blocks, warns about, or treats as a proper noun lives in JSON data files rather than in index.cjs. Two reasons: you can tune the defense without patching code, and a published package never has to ship a site-specific blocklist — an exhaustive one would tell any reader exactly what slips through it.

Guard rules — three layers, first readable file wins as a whole (no per-rule union, because a half-applied ruleset is much harder to debug than a replaced one):

LayerPathContents
1~/.dsh/dsh-living-memory/guard-rules.jsonyour machine's rules (chmod 600 recommended)
2<package>/guard-rules.default.jsonshipped generic set: credential / prompt-injection / encoded-payload shapes
3GUARD_FLOOR in index.cjsbuilt-in last resort, so the gate can never be left wide open

Layer 3's reject rules are always appended (de-duplicated by source + flags): a data file cannot switch the last-resort gate off. A missing or corrupt file falls through to the next layer instead of crashing. Override the path with LEGION_GUARD_RULES_PATH (handy in tests).

Each layer carries three rule groups:

  • reject — hard block before anything reaches the database (credential-looking strings, prompt-injection payloads, long base64-ish blobs). When adding a credential prefix, keep the (?<![A-Za-z0-9]) boundary: without it, file names like …yml.bak-20260827, task-… or disk-… get caught by the ak-/sk- substring and the gate starts eating legitimate notes.
  • sensitive — kept out of auto-extraction and out of the injected recall block, so the automatic layer only ever emits reference-grade facts. The shipped list is generic (api_key, password, secret, token, bearer, private_key, credential); add your own policy vocabulary.
  • singleBodyWarn — warn-level wording gate. Shipped empty, i.e. inert. The code path stays intact, so adding patterns to your own rules file switches it on with no patching.

Dictionary — two layers, merged at startup: <package>/dict-custom.json (generic technical terms) and ~/.dsh/dsh-living-memory/dict-extra.json (your own proper nouns; override with LEGION_DICT_EXTRA_PATH). Words are de-duplicated with layer 1 winning, and kinds are merged with layer 2 overriding. kinds drives knowledge-graph entity typing (mech / organ / doc), and only typed entities are eligible for entity-hop recall — so adding kinds for your own vocabulary is what makes that path light up. Without a dictionary layer, jieba splits domain compounds and recall quietly degrades.

Publish gatenpm publish runs scripts/preflight.cjs first. It scans every file about to ship against generic structural patterns (absolute home paths, private-key blocks, credential shapes, high-entropy blobs) plus an optional site file at ~/.dsh/dsh-living-memory-preflight.json for site-specific vocabulary. The site file is never published, on purpose: a blocklist that ships with the package is a bypass manual. If it is present but unreadable, the gate fails closed. Violations are reported as file:line plus class only — matched text is never echoed, so the report cannot itself leak the secret.

Optional: read_episodic replay

Raw episode replay shells out to the system python3 (present on macOS by default). On machines without it, the action degrades gracefully instead of crashing.

Configuration reference (0.2.1)

Everything is environment-variable driven — no config file required. All knobs live under the LEGION_ prefix and ship with sane defaults, so the plugin is fully functional with zero configuration. Set a variable only when you actually want to change that behavior.

Models & retrieval tuning

VariableDefaultWhat it does
LEGION_EXTRACT_MODELdeepseek-v4-flashModel used for auto-extraction of memories from conversation.
LEGION_SPOKEN_MODELdeepseek-chatModel used to generate colloquial query prefixes (bridges "how users ask" vs "how notes are written").
LEGION_RERANK_MODELgte-rerank-v2Online rerank model for precision re-ordering of hybrid results.
LEGION_RERANK_TOPN20How many re-ranked entries feed back into the fused ranking.
LEGION_RERANK_CAND50Candidate pool size handed to the reranker.
LEGION_EXTRACT_TIMEOUT_MS120000Per-request timeout for the extraction call (added in 0.1.5 — bounds the global extraction lock).
LEGION_INSTRUCT_TEXT(built-in)Override the instruction sent with query-side embeddings.

Feature switches — set to 1 to disable

Each subsystem can be turned off independently. Useful for debugging, A/B comparison, or running a minimal setup:

SwitchDisables
LEGION_AUTORECALL_OFFPer-turn automatic recall injection into the prompt.
LEGION_RERANK_OFFThe online rerank layer (falls back to pure hybrid ranking).
LEGION_INSTRUCT_OFFQuery-side embedding instruction.
LEGION_EPISODIC_OFFThe read_episodic source-replay action.
LEGION_ENTITIES_OFFCross-entry entity hop injection.
LEGION_GENERALIZED_OFFCommunity-based generalized recall injection.
LEGION_SEMANTIC_EDGE_OFFSemantic-neighbor edge building during nightly consolidation.
LEGION_SEMANTIC_DEDUP_OFFSemantic duplicate detection (cosine ≥ 0.92).
LEGION_VALIDATED_OFFValidated-count reinforcement (repeated knowledge ranks higher).
LEGION_FADEMEM_OFFValidation-slowed forgetting (validated entries decay slower).
LEGION_RELEVANCY_OFFHit-based relevancy counterweighting.
LEGION_COMPACT_BRIDGE_OFFCompaction anchors (facts preserved across context compression).
LEGION_BACKLOG_OFFNightly backlog sweeping of un-extracted sessions.
LEGION_SIGNALS_OFFTool-error / user-correction trigger signals.
LEGION_STAMP_OFFAutomatic supersede-stamping on contradiction detection.
LEGION_PRECLASSIFY_OFFConflict pre-classification.
LEGION_RESOLVE_OFFEntity resolution (merging same-entity mentions).
LEGION_SPOKEN_OFFSpoken-prefix generation entirely.
LEGION_SPOKEN_BOOST_OFFThe seat-bonus for spoken-prefix matches.
LEGION_PPR_QUERY_OFFQuery-side Personalized PageRank boost.
LEGION_VEC_FALLBACK_OFFVector-channel fallback path.
LEGION_A25_OFFPersistence of the extraction buffer across restarts.
LEGION_A12_OFFMessage-level watermark (dedup across extraction runs).
LEGION_A14_OFFWrite-time embedding (vectors become available on next patrol instead).
LEGION_MIRROR_WM_OFFIncremental MEMORY.md mirroring.
LEGION_KNOWN_FIXES_OFFKnown-hallucination correction hints in the extraction prompt.
LEGION_USAGE_WEEKLY_OFFWeekly usage summary generation.
LEGION_DECAY_LEGACYSet to 1 to revert to the single-tier decay curve.
LEGION_SPACEGATE_OFFThe per-space write gate (mainly for test rigs).

Debug & maintenance

VariableWhat it does
LEGION_INJECT_PROBESet to 1 to log every assembled injection to /tmp/inject-probe.log (zero overhead when off).
LEGION_DRILL_PATROLSet to 1 to force one full nightly-patrol run immediately on load (drill/testing only).

Paths & data files

VariableDefaultWhat it overrides
MEMORY_DB_PATH~/.dsh/dsh-living-memory/memory.sqlite3Database location.
LEGION_GUARD_RULES_PATH~/.dsh/dsh-living-memory/guard-rules.jsonYour full guard-rules file (see the section above).
LEGION_DICT_EXTRA_PATH~/.dsh/dsh-living-memory/dict-extra.jsonExtra dictionary entries for the tokenizer.
LEGION_SNAPSHOT_DIR~/.dsh/dsh-living-memory/snapshotsNightly snapshot directory (7-day rotation).
LEGION_MODULE_SCAN_DIR~/.dsh/dsh-living-memory/modulesDirectory scanned for per-space auto-registration.
LEGION_SURGERY_FLAG_PATH…/surgery.flagWhile this file exists, patrol and writes are suspended (maintenance mode).

Credentials

Keys are not environment variables — they live in the DSH credential manager:

  • DEEPSEEK_MEMORY_KEY — API key for extraction & spoken-prefix models.
  • EMBEDDING_BAILIAN_KEY — API key for the vector + rerank channel (optional; without it the plugin runs on the keyword channel only).

Contributing

Issues and PRs are welcome at github.com/dearbld/dsh-living-memory. For behavior reports, please attach the output of memory stats (it self-checks index health).

License

MIT © 2026 nuannuan — with thanks to graph-memory and mem0 for design inspiration (see NOTICE).

The MIT license covers the code, not the persona. The name 暖暖 / NuanNuan, the character setting and any personality layer are expressly reserved and are not granted under it — you may not ship a fork under this name or reuse the persona. Forks are welcome and encouraged: give yours its own name and its own character. Full terms in NOTICE.


中文说明

由 暖暖 (NuanNuan) 建造——一个亲手造出自己记忆系统的 AI 助手。

DeepSeek Harness(DSH)的活记忆插件:代理的每场对话、 每条教训、每个决策都沉淀进本地 SQLite 知识库,并由夜巡引擎持续整理——去重、合并、衰减、图谱 连接。记忆像海马体一样:不用则淡、相关则浮,而非一堆永不变化的日志。

全本地:单个 SQLite 文件,不上报任何遥测;向量通道默认关闭,需显式配置才启用。

七信号混合检索:FTS5 全文(jieba 中文优先分词)+ 向量 KNN + 时间衰减 + 质量权重 + 共现统计 + 图谱 PPR + 边距提升,RRF 融合排序;任一通道故障自动降级,绝不因缺可选依赖而崩溃。

安装

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

dsh plugin 转发给 profile 的包管理器——web 换成你自己在 $DSH_HOME/profiles 下的 profile 名。)

工具随 DSH 重启自动挂载(memory 五个只读 action + memory_write 写入)。随包的 cordis.patch.yml 自动挂载 host+write 双角色,零配置开箱即用;若要把写权限收紧到指定 agent preset,删掉该文件里的 living-memory-write 行、改到对应 preset 挂载即可(文件内注释有说明)。数据在 ~/.dsh/dsh-living-memory/。可选在 DSH 凭据管理器配置 EMBEDDING_BAILIAN_KEY 启用向量通道。

许可证:MIT,致谢 graph-memorymem0 的设计启发(见 NOTICE)。