# dsh-memento

> Add bounded, hierarchical, approval-gated, auditable cross-session memory to DeepSeek Harness: local

## Metadata

- Author: [@PerryLink](https://github.com/PerryLink)
- Repo: <https://github.com/PerryLink/dsh-memento.git>
- GitHub: [PerryLink/dsh-memento](https://github.com/PerryLink/dsh-memento)
- Stars: 59
- Language: JavaScript
- License: [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html)
- Homepage: <https://www.npmjs.com/package/dsh-memento>
- Topics: `agent-memory`, `approval`, `audit`, `cordis`, `deepseek-harness`, `dsh`, `dsh-plugin`, `llm`, `memory`, `sqlite`
- Forks: 1
- Open Issues: 0
- Last push: 2026-08-19T06:40:14.000Z
- Added: 2026-08-14T00:00:00.000Z

## Install

```bash
dsh plugin --profile web add github:PerryLink/dsh-memento
```

## Wiki

## One-Line Pitch
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.

## Core Capabilities
- Add, rewrite, delete, consolidate, or retrieve memory entries within sessions via the `memory` tool; all write paths go through a unified approval gate
- During the first prompt assembly of each session, freeze a snapshot of current memories and inject it into systemPrompt; the snapshot doesn't change within the session
- Every write is logged (including rejected cases), and approval pairs write the full payload to session logs for later reconstruction
- Maintains two tracks (user / agent) × two layers (user-global / workspace) × agent-isolated memory spaces, with hard character budgets to prevent overflow
- Connects third-party memory formats (mem0, Hermes memory.md, CLAUDE.md) through the `ctx.memoryAdapters` registry; import/export both go through approval
- Auto-generates pending memory proposals after session compression succeeds; require user confirmation before landing

## Technical Implementation
- **Language**: JavaScript (pure ESM, `.mjs`; no TypeScript compilation step, type contracts provided via `.d.ts`)
- **Key Dependencies**: `@deepseek-ai/cordis`, `@deepseek-ai/dsh-tools`, `@deepseek-ai/dsh-session`, `node:sqlite` (Node built-in SQLite synchronous driver)
- **Architecture Pattern**: Three-role seam — Service Definition (`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 disappears
- **Entry File**: `index.mjs` (the only file the plugin exposes to the host, `lib/` maintains zero DSH dependencies)

## Use Cases
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.

## Prerequisites & Compatibility
| 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 |

## Installation
```bash
dsh plugin --profile web add github:PerryLink/dsh-memento
```

## Configuration Options
| 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 |

## FAQ
**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.

## Learning Curve
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.

## Known Issues & Limitations
- **rc.6 session events not actually emitted**: The plugin declares five SessionEventMap vocabulary types (`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.
- **No FTS5 index**: Retrieval uses `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.
- **Race conditions when sharing same `$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.

---

This document is auto-generated by [deepseek-plugin.org](https://deepseek-plugin.org). HTML page: [dsh-memento](https://deepseek-plugin.org/plugins/PerryLink/dsh-memento)
Wiki generated by AI (model: `MiniMax-M2.5`)
