Add bounded, hierarchical, approval-gated, auditable cross-session memory to DeepSeek Harness: local
$ dsh plugin --profile web add github:PerryLink/dsh-mementoRun the command above in your terminal to install this plugin via the dsh CLI. You can switch Profile in the top-right corner. New to dsh? Read the beginner tutorial
dsh-memento is a cross-session memory capability seam plugin for DeepSeek Harness. It treats memory as a typed service (ctx.memory), allowing DSH to automatically inject past preferences and project conventions into the system prompt each time a new session starts, and enforces human approval gates before every write.
memory tool; all write paths go through a unified approval gatectx.memoryAdapters registry; import/export both go through approval.mjs; no TypeScript compilation step, type contracts provided via .d.ts)@deepseek-ai/cordis, @deepseek-ai/dsh-tools, @deepseek-ai/dsh-session, node:sqlite (Node built-in SQLite synchronous driver)ctx.memory, MemoryService in index.mjs) + Provider (lib/store.mjs local SQLite, WAL mode, permissions 0600) + Consumer (memory tool + systemPrompt.section frozen snapshot); integrated into host via inject: ['tools', 'systemPrompt', 'approval'], when disabled (enabled:false) the entire capability disappearsindex.mjs (the only file the plugin exposes to the host, lib/ maintains zero DSH dependencies)When you want DSH to remember user preferences across sessions (language, style, landmines), project conventions (build commands, directory structure), and lessons learned, but don't want the model to quietly stuff things into the system prompt—this plugin provides a "approve first, land later, auditable" workflow. It's especially suitable for developers and teams maintaining the same project long-term—the next time you open DSH, you won't need to repeat the background, all context is ready by layer.
| Dependency | Minimum Version | Description |
|---|---|---|
| DeepSeek Harness | 0.1.0-rc.6+ | Declared in package.json#dshWorkshop.compatibility.dshVersions |
| Node | ^22.19.0 || >=24.0.0 | package.json#engines.node |
| Platform | Cross-platform | Windows / macOS / Linux, zero native code compilation |
| Native Module | node:sqlite | Node built-in SQLite synchronous driver, zero external native dependencies |
| Peer Dependencies | @deepseek-ai/cordis ^4.0.1, @deepseek-ai/dsh-tools >=0.1.0-rc.6, @deepseek-ai/dsh-session >=0.1.0-rc.6, @deepseek-ai/schemastery >=3.0.0 | Provided by host |
dsh plugin --profile web add github:PerryLink/dsh-memento
| Config | Type | Description | Default |
|---|---|---|---|
enabled | Boolean | Master switch; when set to false, tools, injection, service, and approval answerer all disappear | true |
dbPath | String | Absolute path to memory database file; empty uses $DSH_HOME/dsh-memento/memory.db (falls back to ~/.dsh on Windows when $DSH_HOME is missing) | '' |
budgets.user.userGlobal | Number | Hard character budget for "global" layer of user-related facts | 2000 |
budgets.user.workspace | Number | Hard character budget for "workspace" layer of user-related facts | 2000 |
budgets.agent.userGlobal | Number | Hard character budget for "global" layer of environment/project facts | 4000 |
budgets.agent.workspace | Number | Hard character budget for "workspace" layer of environment/project facts | 4000 |
writePolicy | ask | auto | off | Global write approval policy (invisible and unmodifiable by model) | ask |
writePolicies | Dictionary | Granular write policy, keys can be track/scope or source:<name> | {} |
language | en | zh | Language for snapshot text, /memory commands, tool descriptions, and panels | en |
snapshotOrder | Number | Order of snapshot section in systemPrompt (smaller values appear earlier) | -50 |
maxEntriesPerQuery | Number | Upper limit for single memory query default return (Provider hard-capped at 1000) | 20 |
commandListLimit | Number | Number of entries rendered per /memory list / query | 50 |
commandAuditLimit | Number | Number of audit rows rendered per /memory audit | 10 |
recall.historyLimitDefault | Number | Default number of historical sessions scanned by memory_recall tool | 8 |
recall.snippetCap | Number | Upper limit of history snippets returned per session | 5 |
recall.snippetChars | Number | Character limit per snippet | 300 |
recall.windowDays | Number | Days to look back for history snippets | 30 |
panelEntriesLimit | Number | Web panel entry pagination size | 200 |
panelAuditLimit | Number | Web panel audit default row count | 20 |
auditRetentionDays | Number | Days to retain audit rows, 0 means permanent | 0 |
proposals.enabled | Boolean | Whether to auto-generate pending proposals after session compression | true |
proposals.maxChars | Number | Character limit per proposal | 2000 |
proposals.maxPending | Number | Upper limit for pending proposals | 8 |
Q: What happens when the budget is full? Does it auto-compress?
A: It doesn't auto-compress. Exceeding the budget throws a structured BUDGET_EXCEEDED error (carrying current usage and limit). Please use consolidate to merge multiple entries, or remove to delete unnecessary entries, then retry writing. The Provider layer never silently truncates.
Q: Can the model bypass write approval?
A: No. The approval gate is implemented inside the write methods of the ctx.memory service (MemoryProtocolCore), not at the tool layer. Any path (memory tool, /memory command, future plugins) calling add/replace/remove/seed must go through ctx.approval.request; writePolicy is a model-invisible configuration.
Q: Where is memory data stored? Is it uploaded to the network?
A: Stored by default in $DSH_HOME/dsh-memento/memory.db, POSIX permissions 0600, pure local SQLite. The plugin manifest explicitly declares network:none / credentials:none, and the entire lifecycle makes no network requests.
Q: Does uninstalling the plugin lose memory data?
A: No data loss. dsh plugin --profile web remove dsh-memento only uninstalls the plugin; the SQLite database and session logs are preserved. The plugin also never appends unregistered event types to session logs, so old sessions can load normally.
Q: If memory is modified mid-session, does the model's snapshot update immediately?
A: No. The snapshot is frozen once during the first systemPrompt assembly of each session; mid-session writes only land to disk and log, they don't rewrite to the already-injected system section—this stabilizes prefix caching and is part of "what the model sees is rebuildable from session logs."
Q: Does it support substring search for Chinese (CJK) memory entries?
A: Yes. Retrieval uses case-insensitive instr instead of FTS5, because SQLite's built-in tokenizer isn't friendly to single-character CJK indexing; instr is naturally correct for Chinese scenarios with zero plugin dependencies.
Q: What happens when two DSH processes under the same $DSH_HOME write simultaneously?
A: SQLite serializes writes within a single process via busy_timeout; cross-process consistency isn't guaranteed—first writer wins. This is inherent to SQLite file sharing behavior, consistent with official warnings from Hermes and similar terminal memories.
Q: What's the relationship with the officially recommended MCP memory server?
A: They can coexist. dsh-memento is DSH's native local first-party implementation (zero network, no external process dependency); MCP memory is the external server route recommended in official documentation; both target the same goal, are non-exclusive, and users can choose either or enable both based on the scenario.
Advanced — Many configuration options (budgets, approval policies, adapters), but default config works out of the box; advanced users need to understand the "track × layer × agent isolation" model and approval waterfall to tune the most fitting policy.
memory/added|updated|removed|recalled|snapshot) merged in types.d.ts, but DSH 0.1.0-rc.6 lacks plugin event registration surface; runtime doesn't append by default; audit chain is handled by approval pairs' approval/asked + approval/decided and the plugin's own audit table, will auto-enable once harness includes memory/*.ask policy requires a human answerer: When the profile doesn't configure a UI/ACP-style approval answerer, writes under ask policy fail closed; for unattended writes, change to auto or explicitly use off to disable entirely.instr substring matching (case-insensitive, CJK-friendly); in large data scenarios, query efficiency is lower than full-text search—use limit parameter to narrow the scope.$DSH_HOME across processes: SQLite file locks guarantee serialization within a single process, but cross-process consistency isn't guaranteed; multiple terminals editing the same directory simultaneously need external coordination.Bounded, layered, approval-gated, auditable cross-session memory for DeepSeek Harness.
A typed ctx.memory seam, a write-approval gate no model path can bypass, and audit trails rebuilt from the session log.
| Surface | Status |
|---|---|
| Harness | DeepSeek Harness 0.1.0-rc.6 |
| Node | `^22.19.0 |
| Platforms | Windows / macOS / Linux (pure host; no native code, no network) |
| Model | Any |
dsh-memento is a capability seam, not another memory warehouse: a typed ctx.memory service, a local SQLite provider (node:sqlite, WAL, 0600, at $DSH_HOME/dsh-memento/memory.db), and its consumers — the memory tool and a frozen snapshot injected into the system prompt.
add / replace / remove / seed) is forced through the approval waterfall inside the service, not in the tool layer. writePolicy: ask | auto | off is model-invisible configuration; replace / remove / consolidate carry the full text of the entries they change in the approval payload, and a denied write still lands a *-denied audit row.request/header.system; every write is reconstructable from approval/asked + approval/decided + the plugin's own audit table.Two tracks × two layers × per-agent key: a user track (facts about the user) and an agent track (environment facts and conventions), each split into user-global and workspace layers, isolated per agentPreset. The snapshot is frozen once per session at first prompt assembly and never changes mid-session.
# 1. install the bundle into your profile
dsh plugin --profile web add "github:PerryLink/dsh-memento#main"
# or from npm (published releases)
dsh plugin --profile web add dsh-memento
# 2. restart and verify the row
dsh --profile web --dump-config | grep -A3 'id: memento'
main): dsh plugin --profile web add git+https://github.com/PerryLink/dsh-memento.git.dsh plugin --profile web add dsh-memento.npm pack in this repo, then dsh plugin --profile web add ./dsh-memento-<version>.tgz.dsh plugin --profile web remove dsh-memento (the memory database and session logs are kept).All tunables are Schemastery Config fields (changeable from cordis.yml). Invalid values fail loudly at load. Override under the memento row.
| Key | Default | Meaning |
|---|---|---|
enabled | true | Master switch; false removes the service, tools, snapshot, command, panel, and answerer |
dbPath | '' → $DSH_HOME/dsh-memento/memory.db | Absolute, or relative to $DSH_HOME (falls back to ~/.dsh on Windows) |
budgets.user.userGlobal | 2000 | Hard character budget for the user track's user-global layer |
budgets.user.workspace | 2000 | Hard character budget for the user track's workspace layer |
budgets.agent.userGlobal | 4000 | Hard character budget for the agent track's user-global layer |
budgets.agent.workspace | 4000 | Hard character budget for the agent track's workspace layer |
writePolicy | 'ask' | Default write policy: ask / auto / off (model-invisible) |
writePolicies | {} | Per-track/scope or per-source overrides (e.g. user/workspace, source:claude) |
language | 'en' | Model-visible and command output language: en / zh |
snapshotOrder | -50 | Snapshot section order (after harness identity, before persona) |
maxEntriesPerQuery | 20 | Default per-query result cap (hard-capped at 1000) |
commandListLimit | 50 | Entries rendered per /memory list / query |
commandAuditLimit | 10 | Audit rows rendered per /memory audit |
recall.historyLimitDefault | 8 | memory_recall sessions scanned by default |
recall.snippetCap | 5 | memory_recall snippets per session |
recall.snippetChars | 300 | memory_recall snippet characters |
recall.windowDays | 30 | memory_recall recency window in days |
panelEntriesLimit | 200 | Web panel entries page size |
panelAuditLimit | 20 | Web panel audit rows by default |
auditRetentionDays | 0 | Audit retention (0 = keep forever) |
proposals.enabled | true | Auto-capture a memory proposal after each successful compaction |
proposals.maxChars | 2000 | Proposal character cap |
proposals.maxPending | 8 | Pending proposal cap |
| Surface | Kind | Notes |
|---|---|---|
memory | tool | add/replace/remove/consolidate/query with Save/Skip guidance; writes ride the approval gate |
memory_recall | tool | Bounded memory matches plus recent session-history matches |
/memory | command | list · query · add · remove · consolidate · proposals · budgets · audit · export · import <path> · adapters |
| web panel | client drawer | Read-only: browse entries, search, budget bars, audit tail |
| Plugin | What it is | dsh-memento's difference |
|---|---|---|
| dsh-memory-evolve | memory warehouse / evolution loops | a typed service seam, approval gate, and session-log audit; no warehouse ambition |
| dsh-mnemon | memory store helper | protocol + gate + audit, not another store |
| dsh-kb-sieve | knowledge-base sieving | no retrieval engineering: small-corpus substring search, cross-session recall via session_search/sessionQuery |
| dsh-tdai-memory | task-driven memory tooling | budgets are per track×layer and enforced in the service, not best-effort |
| claude-bridge | Claude Code bridging | DSH-native; a future seed(source:'claude') path lets a bridge feed the same store |
| dsh-external/Recall | external agent memory | local-first, zero-network, rides DSH's own approval seam |
| Official MCP memory examples | DSH's stated "memory = external MCP" position | the native first-party complement: same goal, no external server; both coexist |
The name is dsh-memento (published on npm and GitHub). Not dsh-recall (confusable with dsh-external/Recall), not the deleted legacy name dsh-memory.
dsh-memento is the community rehearsal of the DSH memory protocol — a candidate shape for an official ctx.memory seam. The protocol normalizes this plugin's seam into a cross-plugin contract:
Entry spec — two tracks × two layers × per-agent key, plus short tags (≤16 × ≤32 chars) and a per-entry version that increments on every replace.
Write semantics — idempotent unique-substring conditional writes; approve-what-you-see payloads (replace / remove / consolidate carry the full text they change).
Audit contract — every write reconstructable from approval/asked + approval/decided + the provider ledger.
Budget model — BUDGET_EXCEEDED / AMBIGUOUS_MATCH semantics.
Schema versioning — migration rules with loud version checks.
Spec — docs/protocol-v1.md (中文: protocol-v1.zh.md); normative JSON Schema at docs/schemas/dsh-memory-protocol-v1.schema.json.
Adapter registry — ctx.memoryAdapters (register / list / adapt / export) lets third-party memory plugins speak the protocol by registering a pure data converter (reversible register(); import rides the approval-gated seed, export is read-only). Onboarding: docs/adapters-guide.md (中文: adapters-guide.zh.md).
| Built-in adapter | External format | Notes |
|---|---|---|
mem0 | mem0 fact collections ({facts: [{memory, metadata?}]}) | metadata.category / metadata.tags become tags; raw messages arrays are rejected — adapters convert, never extract |
hermes-memory-md | Hermes memory.md (## section + bullets) | section names become tags; non-bullet prose fails loudly |
claude-code-memory-md | CLAUDE.md-style markdown (headings, bullets, paragraphs) | bullets and paragraphs become entries; section names become tags |
Conformance suite — test/protocol-conformance/: a distributable case set any provider claiming compatibility runs (node test/protocol-conformance/run.mjs --provider ./your-factory.mjs); this repo's CI runs it against its own provider as the golden reference (npm run test:conformance).
ctx.memory seam should adopt the protocol, the differences, and the migration path.harness:tool, filesystem:read, filesystem:write, and network:none / subprocess:none / shell:none / python:none / credentials:none in its workshop manifest. Write approval rides the official approval seam.0600), zero network, zero credentials.approval/asked + approval/decided) plus the plugin's own audit table.tools, systemPrompt, and the approval seam; no engine / agent-loop / apiproxy / official-UI changes.0600.$DSH_HOME write the same file (last-writer-wins under SQLite locking).memory/added|updated|removed|recalled|snapshot are merge-declared, but rc.6 has no registration surface for out-of-repo event types; emission turns on once a harness build registers them.ask policy needs an answerer. With no UI/ACP answerer composed, writes fail closed.instr (correct for CJK).dsh-memento is not a port of Claude Code, Codex, or Hermes — but its design deliberately absorbed the parts each got right, and refused the parts that hurt:
| Terminal memory | What it got right | What dsh-memento adopted |
|---|---|---|
Claude Code — CLAUDE.md | hierarchical plain-text memory files (user-level → project-level), human-readable and human-editable, merged automatically into every session | plain-text entries; user-global / workspace layers merged per session; a store you can browse, export, and audit — transparency as a feature |
Codex — AGENTS.md | per-directory scoped instructions auto-discovered and injected with zero model friction | the workspace layer keyed by the session cwd (Windows case-insensitive); the frozen snapshot injected automatically at session start |
Hermes — memory.md | proactive memory saves and the security lesson that a gate enforced only in the tool layer is bypassable by late tool injection | the memory tool with Save/Skip guidance + approval-gated auto-capture proposals; the gate lives inside ctx.memory's write methods, not in the tool layer |
Sources: Claude Code memory · Codex AGENTS.md · Hermes memory · Hermes #48181.
And the parts deliberately refused: hidden auto-summarization into model-private state (compaction summaries here become pending proposals that wait for a human approve/dismiss), warehouse/vector-store ambitions, and any write that lacks a human-visible approval or audit trail. Also adopted: Hermes's documented caveat that two processes sharing one home directory write the same memory file — see Security boundaries.
npm install # node ^22.19 || >=24
npm test # node --test: 133 tests
npm run test:conformance # dsh-memory-protocol v1 conformance suite
npm run typecheck # tsc --checkJs gate
npm run check:coverage # line-coverage gate
npm run check:readmes # five-language README consistency gate
lib/ is zero-DSH-dependency (node: builtins only); DSH imports exist only in index.mjs.
dsh, dsh-plugin, deepseek-harness, memory, agent-memory, approval, audit, sqlite, cordis, llm
~/.dsh fallback shipped in 0.3.1.This project is one of the 15 DeepSeek Harness plugins maintained by PerryLink. If this one helps you, the others likely will too:
| Plugin | One-liner |
|---|---|
| dsh-mcp-panel | Read-only MCP runtime panel: /mcp command + Settings tab with status, tools and errors |
| dsh-doublecheck | Engineering-discipline guard: requirements grill, test gates, adversary review |
| dsh-background-agents | Durable background child agents with a Web UI sidebar, messaging and interrupt |
| dsh-lsp-actions | LSP diagnostics, formatting, completion, code actions and rename over language servers |
| dsh-output-styles | Claude Code outputStyles-equivalent runtime style switching |
| dsh-checkpoint-rewind | Claude Code /rewind-equivalent: snapshots, session forks, one-shot restore |
| dsh-permission-rules | Claude Code-style declarative allow/deny/ask permission rules with audit |
| dsh-auto-review | Second-model auto-review on the approval chain, fail-closed by default |
| dsh-memento | Approval-gated cross-session memory: ctx.memory seam + SQLite + memory tool |
| dsh-skill-pack-security | Security-audit skill pack: secret scan, dependency and supply-chain review |
| dsh-session-pin | Pin sessions in the Web sidebar with durable ordering |
| dsh-composer-history | Terminal-style input history for the web composer: arrows, Ctrl+R search |
| dsh-github | GitHub PR/issues integration for DSH, every write gated by approval |
| dsh-plugin-guide | Plugin-development knowledge base as an on-demand agent skill |
| dsh-claude-move | Migrate Claude Code sessions, memory, skills and CLAUDE.md into DSH |
Apache License 2.0 © 2026 dsh-memento contributors