dsh-memento 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,166 +1,167 @@
1
- # dsh-memento
2
-
3
- **Bounded, layered, approval-gated, auditable cross-session memory for DeepSeek Harness.**
4
-
5
- [![license](https://img.shields.io/badge/license-Apache--2.0-3a7d44)](LICENSE)
6
- [![dsh](https://img.shields.io/badge/dsh-0.1.0--rc.6-4e51e8)](https://www.npmjs.com/package/@deepseek-ai/dsh)
7
- [![node](https://img.shields.io/badge/node-%5E22.19%20%7C%7C%20%3E%3D24-339933)](https://nodejs.org/)
8
- [![platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-lightgrey)]()
9
- [![no build step](https://img.shields.io/badge/build-none%20%28pure%20ESM%29-8a6d3b)]()
10
-
11
- [English](README.md) · [中文](README.zh.md) · [Español](README.es.md) · [Português](README.pt.md) · [हिन्दी](README.hi.md)
12
-
13
- > Other memory plugins sell a **warehouse**. dsh-memento sells the **seam**: a typed `ctx.memory` service, a write approval gate no model path can bypass, and audit trails you can rebuild from the session log. Native-first memory for DeepSeek Harness — protocol + trust gate + audit, with zero network and zero credentials.
14
-
15
- ## ✨ Why dsh-memento?
16
-
17
- - **It's a capability seam, not another store.** Service Definition (`ctx.memory`), local SQLite Provider (`node:sqlite`, WAL, `0600`), and Consumers (`memory` tool + frozen snapshot injection). Any future plugin — a `dsh-claude-move` seed integration, a bridge, a panel — feeds and reads the **same store through the same gate**.
18
- - **The gate cannot be bypassed.** Every write path (`add`/`replace`/`remove`/`seed`) is forced through the approval waterfall **inside the service**, not in the tool layer. `writePolicy: ask | auto | off` is configuration the model can neither see nor change; a session-level `never` stance still pre-empts everything.
19
- - **Model-visible ⟺ logged.** The injected snapshot lands verbatim in `request/header.system`; every write is reconstructable from `approval/asked` (full payload) + `approval/decided` (outcome) + the plugin's own audit table.
20
- - **Bounded and honest.** Hard per-track/per-layer character budgets (default user 2000 / agent 4000). A full store **fails with a structured error** (usage + limit) — the model consolidates and retries. Never truncated, never auto-compacted.
21
-
22
- ## ⚡ Quick start
23
-
24
- ```sh
25
- # requires Node ^22.19 || >=24 and DSH 0.1.0-rc.6
26
- dsh plugin --profile web add dsh-memento # or ./dsh-memento / a tarball / a GitHub URL
27
- dsh --profile web --dump-config # expect a "# == dsh-memento" layer, no FAILED at startup
28
- ```
29
-
30
- Then, in the Web UI: ask the model to remember something → approve the write → start a **new session** and ask what it remembers. That's the whole demo.
31
-
32
- ```yaml
33
- # optional override in the profile's cordis.patch.yml
34
- - id: memento
35
- config:
36
- writePolicy: ask # ask (default) | auto | off — model-invisible
37
- budgets:
38
- user: { userGlobal: 4000, workspace: 2000 } # Chinese-heavy memory: raise + note why
39
- agent: { userGlobal: 4000, workspace: 4000 }
40
- ```
41
-
42
- ## 🧠 What it does
43
-
44
- | | Component | What you get |
45
- | --- | --- | --- |
46
- | 🧩 Service Definition | `ctx.memory` — `add` / `replace` / `remove` / `query` / `seed` / `budgets()` | Typed, merge-declared service; write methods enforce the gate internally |
47
- | 💾 Provider | `lib/store.mjs` — `node:sqlite` single file (`$DSH_HOME/dsh-memento/memory.db`, WAL) | Zero dependencies, zero network; entry + audit tables; unique-substring match |
48
- | 🛠 Consumers | `memory` tool · frozen snapshot injection (system-prompt section, order `-50`) · `memory_recall` tool · `/memory` command · read-only Web panel | Model-facing writes/reads, budget-headed frozen snapshot, two-part recall, user-side command, browser drawer |
49
-
50
- **Two tracks × two layers × per-agent key.** `user` track = facts about the user (preferences, communication style, landmines); `agent` track = environment facts, project conventions, lessons learned. Each track has `user-global` (cross-workspace) and `workspace` (per-session cwd) layers — Codex-style merged layering, not Hermes-style global-only. A third dimension isolates entries by the session's `agentPreset` (per-agent scope); entries without a preset stay in the shared layer visible to everyone.
51
-
52
- **Frozen snapshots.** The snapshot is rendered once per session at first prompt assembly (synchronous SQLite read + per-session cache) and never changes mid-session — prefix-cache stable by construction. Session-internal changes persist to disk + audit only.
53
-
54
- ```
55
- Consumer: memory tool Consumer: frozen snapshot (systemPrompt section, order -50)
56
- add/replace/remove/query per-session freeze, budget-headed
57
- │ writes (agent+callId) │ reads (sync, session cwd)
58
- ▼ ▼
59
- Service Definition: ctx.memory — budgets/add/replace/remove/query/seed
60
- every write: budget precheck → ctx.approval.request (approval waterfall) → budget recheck → persist → audit
61
- │
62
- ▼
63
- Provider: lib/store.mjs — node:sqlite (WAL, 0600), entries + audit tables, unique-substring match
64
- ```
65
-
66
- ## 🧰 Install & uninstall
67
-
68
- ```sh
69
- dsh plugin --profile <name> add ./dsh-memento # local checkout (no build step)
70
- dsh plugin --profile <name> add git+https://github.com/PerryLink/dsh-memento.git # GitHub install; npm after first release
71
- dsh plugin --profile <name> remove dsh-memento # uninstall: DB + session logs are kept
72
- ```
73
-
74
- After uninstall the memory database and the session logs that recorded memory activity remain; old sessions stay loadable.
75
-
76
- ## ⚙️ Configuration
77
-
78
- Every field is a validated Schemastery `Config`; invalid values fail loudly at load. Override in cordis.yml under the `memento` row.
79
-
80
- | Field | Default | Meaning |
81
- | --- | --- | --- |
82
- | `enabled` | `true` | `false` removes the service, tools, snapshot, command, panel, and answerer entirely (no half-state) |
83
- | `dbPath` | `''` → `$DSH_HOME/dsh-memento/memory.db` | absolute, or relative to `$DSH_HOME` |
84
- | `budgets.user.userGlobal` / `budgets.user.workspace` | `2000` / `2000` | hard char budget per layer of the user track |
85
- | `budgets.agent.userGlobal` / `budgets.agent.workspace` | `4000` / `4000` | hard char budget per layer of the agent track |
86
- | `writePolicy` | `'ask'` | `'ask'` = user approval; `'auto'` = allow through (approval source recorded); `'off'` = reject. Model-invisible |
87
- | `writePolicies` | `{}` | per-track/scope or per-source overrides: keys `user/workspace`, `agent/user-global`, `source:claude`, … → `ask`/`auto`/`off`; unmatched falls back to `writePolicy` |
88
- | `language` | `'en'` | model-visible text and command output language: `'en'` (default) or `'zh'` — tool descriptions, frozen snapshot, `/memory` command, and web panel all follow it |
89
- | `snapshotOrder` | `-50` | snapshot section order: after harness identity (`-100`), before persona (`0`) |
90
- | `maxEntriesPerQuery` | `20` | default per-query result cap (explicit `limit` allowed, hard-capped at 1000) |
91
- | `commandListLimit` | `50` | entries rendered per `/memory list` / `query` command |
92
- | `commandAuditLimit` | `10` | audit rows rendered per `/memory audit` command |
93
- | `recall.historyLimitDefault` / `recall.snippetCap` / `recall.snippetChars` / `recall.windowDays` | `8` / `5` / `300` / `30` | `memory_recall` history defaults: sessions scanned, snippets per session, snippet chars, recency window in days |
94
- | `panelEntriesLimit` | `200` | web panel entries page size (and clamp) |
95
- | `panelAuditLimit` | `20` | web panel audit rows by default (ceiling 200) |
96
- | `auditRetentionDays` | `0` | audit retention: 0 = keep forever, >0 = prune rows older than N days at store open |
97
- | `proposals.enabled` / `proposals.maxChars` / `proposals.maxPending` | `true` / `2000` / `8` | auto-capture: pending memory proposal after each successful compaction (truncated, one per session); disable or tune caps |
98
-
99
- ## 🛠 Tools & surfaces
100
-
101
- - **`memory`** — add/replace/remove/consolidate/query with Save/Skip guidance embedded in the description (save user preferences, corrections, environment facts, conventions, lessons; skip trivia, re-derivable facts, dumps, one-off paths). Writes ride the approval gate; reads are free; replace/remove target a **unique substring** (ambiguous matches fail with the candidate list); consolidate merges 1..20 entries into one with a single approval and one atomic write.
102
- - **`memory_recall`** — two-part recall: bounded memory matches **plus** recent session-history matches via `ctx.sessionQuery` (degrades gracefully to memory-only where the service is absent).
103
- - **`/memory`** — user-triggered command (not a model turn): `list` · `query <word>` · `add [--track=user|agent] [--scope=user-global|workspace] <text>` · `remove [flags] <substring>` · `consolidate [flags] <substring...> => <text>` · `proposals [approve|dismiss <id>]` · `budgets` · `audit` · `export`. Command writes ride the same waterfall + policy; audit lands in the plugin audit table + `command/done`. `export` is read-only and dumps all entries + budgets as one JSON document (backup / migration).
104
- - **Auto-capture proposals** — after a successful session compaction, the summary lands as a pending memory proposal (`agent/workspace`); approving writes it through the approval gate, dismissing drops it. Pending proposals appear in the frozen snapshot and the panel.
105
- - **Web panel** — zero-build `dsh.client` drawer: browse entries by track/layer, search, budget bars, audit tail. Read-only by design: writes and approval happen through the `memory` tool and the built-in approval UI.
106
-
107
- ## 🎓 What we learned from the terminal memories
108
-
109
- dsh-memento is not a port of Claude Code, Codex, or Hermes — but its design deliberately absorbed the parts each of them got right, and refused the parts that hurt:
110
-
111
- | Terminal memory | What it got right | What dsh-memento adopted |
112
- | --- | --- | --- |
113
- | **Claude Code** — `CLAUDE.md` | hierarchical **plain-text memory files** (user-level → project-level) that are human-readable, human-editable, and merged automatically into every session — memory you can read and fix yourself | plain-text entries; `user-global` / `workspace` layers merged per session; a store you can browse, `export`, and audit — transparency as a feature |
114
- | **Codex** — `AGENTS.md` | **per-directory scoped instructions** auto-discovered and injected with zero model friction — locality beats volume, no tool call needed to "load" memory | `workspace` layer keyed by the session's cwd (Windows case-insensitive); the frozen snapshot is injected automatically at session start |
115
- | **Hermes** — `memory.md` | **proactive memory saves** (save/update/delete) and, in [issue #48181](https://github.com/NousResearch/hermes-agent/issues/48181), the security lesson that a gate enforced only in the tool layer is bypassable by late tool injection — enforce it where every write path meets | the `memory` tool with explicit Save/Skip guidance + approval-gated auto-capture proposals; the approval gate lives **inside** `ctx.memory`'s write methods, not in the tool layer |
116
-
117
- Sources: [Claude Code memory](https://code.claude.com/docs/en/memory) · [Codex AGENTS.md](https://developers.openai.com/codex/cli/agents-md) · [Hermes memory](https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/features/memory.md) · [Hermes #48181](https://github.com/NousResearch/hermes-agent/issues/48181).
118
-
119
- And the parts we 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.
120
-
121
- ## 🆚 How it's different
122
-
123
- | Plugin | What it is | dsh-memento's difference |
124
- | --- | --- | --- |
125
- | dsh-memory-evolve | memory warehouse / evolution loops | a typed service seam, approval gate, and session-log audit; no warehouse ambition |
126
- | dsh-mnemon | memory store helper | protocol + gate + audit, not another store |
127
- | dsh-kb-sieve | knowledge-base sieving | no retrieval engineering: small-corpus substring search, cross-session recall via `session_search`/`sessionQuery` |
128
- | dsh-tdai-memory | task-driven memory tooling | budgets are per track×layer and enforced in the service, not best-effort |
129
- | claude-bridge | Claude Code bridging | DSH-native; a future `seed(source:'claude')` path lets a bridge feed the same store |
130
- | dsh-external/Recall | external agent memory | local-first, zero-network, rides DSH's own approval seam |
131
- | Official MCP memory examples | DSH's stated "memory = external MCP" position | the **native first-party** complement: same goal, no external server; both coexist |
132
-
133
- The name is **`dsh-memento`** (free on npm and GitHub). Not `dsh-recall` (confusable with dsh-external/Recall), not the deleted legacy name `dsh-memory`.
134
-
135
- ## 🔒 Security boundaries
136
-
137
- - **Public services only** (`tools`, `systemPrompt`, the approval seam). No engine / agent-loop / apiproxy / official-UI changes.
138
- - **Zero network, zero credentials.** Local database; POSIX file mode `0600`.
139
- - **Fail loud.** Corrupt DB or newer schema fails at load; full budgets and ambiguous substring matches fail with structured errors. Nothing silently swallowed or truncated.
140
- - **One process, one store.** Multiple sessions in one process share the SQLite store (serialized writes, per-session audit). Two **processes** sharing one `$DSH_HOME` write the same file: last-writer-wins under SQLite locking — don't run two harness instances on one `$DSH_HOME` if you need cross-process consistency (same caveat the Hermes project documents).
141
-
142
- ## ⚠️ Known limitations
143
-
144
- - **Session events vocabulary is declared, not yet emitted (rc.6).** `memory/added|updated|removed|recalled|snapshot` are merge-declared in `types.d.ts`, but rc.6 has no registration surface for out-of-repo event types (unregistered appends would make persisted sessions unloadable). Audit completeness comes from the approval pair + the audit table; emission turns on automatically once a harness build registers the types. See [ARCHITECTURE.md](ARCHITECTURE.md) decision 4.
145
- - **`ask` policy needs an answerer.** With no UI/ACP answerer composed, writes fail closed (`unavailable`) — by design, the approval seam's fail-closed stance.
146
- - **No FTS5 indexing.** Substring search runs on case-insensitive `instr` (correct for CJK); recall ranking uses per-entry hit counts. FTS5's trigram tokenizer cannot index single-character CJK tokens, so it is not used — see [ARCHITECTURE.md](ARCHITECTURE.md) decision 10.
147
-
148
- ## 🧪 Development
149
-
150
- ```sh
151
- npm install
152
- npm test # node --test: 103 tests — budget, unique-substring, gate policy, store, snapshot, mock-ctx integration (S2/S3 invariants), V2 command/recall/panel
153
- npm run typecheck # tsc --checkJs gate over index.mjs / lib / scripts
154
- npm run check:coverage # line-coverage gate: lib ≥90%, index.mjs ≥85%, all files ≥90%
155
- npm run check:readmes # five-language README consistency gate
156
- ```
157
-
158
- `lib/` is zero-DSH-dependency (node: builtins only); DSH imports exist only in `index.mjs`. Full discipline in [AGENTS.md](AGENTS.md); design decisions in [ARCHITECTURE.md](ARCHITECTURE.md).
159
-
160
- ## 🏷 Topics
161
-
162
- Suggested GitHub topics: `dsh` · `dsh-plugin` · `deepseek-harness` · `memory` · `agent-memory` · `approval` · `audit` · `sqlite` · `cordis` · `llm`
163
-
164
- ## 📄 License
165
-
166
- Apache License 2.0 — see [LICENSE](LICENSE). No third-party code is redistributed; see [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
1
+ # dsh-memento
2
+
3
+ **Bounded, layered, approval-gated, auditable cross-session memory for DeepSeek Harness.**
4
+
5
+ [![license](https://img.shields.io/badge/license-Apache--2.0-3a7d44)](LICENSE)
6
+ [![dsh](https://img.shields.io/badge/dsh-0.1.0--rc.6-4e51e8)](https://www.npmjs.com/package/@deepseek-ai/dsh)
7
+ [![node](https://img.shields.io/badge/node-%5E22.19%20%7C%7C%20%3E%3D24-339933)](https://nodejs.org/)
8
+ [![platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-lightgrey)]()
9
+ [![no build step](https://img.shields.io/badge/build-none%20%28pure%20ESM%29-8a6d3b)]()
10
+
11
+ [English](README.md) · [中文](README.zh.md) · [Español](README.es.md) · [Português](README.pt.md) · [हिन्दी](README.hi.md)
12
+
13
+ > Other memory plugins sell a **warehouse**. dsh-memento sells the **seam**: a typed `ctx.memory` service, a write approval gate no model path can bypass, and audit trails you can rebuild from the session log. Native-first memory for DeepSeek Harness — protocol + trust gate + audit, with zero network and zero credentials.
14
+
15
+ ## ✨ Why dsh-memento?
16
+
17
+ - **It's a capability seam, not another store.** Service Definition (`ctx.memory`), local SQLite Provider (`node:sqlite`, WAL, `0600`), and Consumers (`memory` tool + frozen snapshot injection). Any future plugin — a `dsh-claude-move` seed integration, a bridge, a panel — feeds and reads the **same store through the same gate**.
18
+ - **The gate cannot be bypassed.** Every write path (`add`/`replace`/`remove`/`seed`) is forced through the approval waterfall **inside the service**, not in the tool layer. `writePolicy: ask | auto | off` is configuration the model can neither see nor change; a session-level `never` stance still pre-empts everything. `replace`/`remove`/`consolidate` carry the full text of the entries they will change in the approval payload — what you approve is what you see, and a denied write still lands a `*-denied` audit row.
19
+ - **Model-visible ⟺ logged.** The injected snapshot lands verbatim in `request/header.system`; every write is reconstructable from `approval/asked` (full payload) + `approval/decided` (outcome) + the plugin's own audit table.
20
+ - **Bounded and honest.** Hard per-track/per-layer character budgets (default user 2000 / agent 4000). A full store **fails with a structured error** (usage + limit) — the model consolidates and retries. Never truncated, never auto-compacted.
21
+
22
+ ## ⚡ Quick start
23
+
24
+ ```sh
25
+ # requires Node ^22.19 || >=24 and DSH 0.1.0-rc.6
26
+ dsh plugin --profile web add dsh-memento # or ./dsh-memento / a tarball / a GitHub URL
27
+ dsh --profile web --dump-config # expect a "# == dsh-memento" layer, no FAILED at startup
28
+ ```
29
+
30
+ Then, in the Web UI: ask the model to remember something → approve the write → start a **new session** and ask what it remembers. That's the whole demo.
31
+
32
+ ```yaml
33
+ # optional override in the profile's cordis.patch.yml
34
+ - id: memento
35
+ config:
36
+ writePolicy: ask # ask (default) | auto | off — model-invisible
37
+ budgets:
38
+ user: { userGlobal: 4000, workspace: 2000 } # Chinese-heavy memory: raise + note why
39
+ agent: { userGlobal: 4000, workspace: 4000 }
40
+ ```
41
+
42
+ ## 🧠 What it does
43
+
44
+ | | Component | What you get |
45
+ | --- | --- | --- |
46
+ | 🧩 Service Definition | `ctx.memory` — `add` / `replace` / `remove` / `query` / `seed` / `budgets()` | Typed, merge-declared service; write methods enforce the gate internally |
47
+ | 💾 Provider | `lib/store.mjs` — `node:sqlite` single file (`$DSH_HOME/dsh-memento/memory.db`, WAL) | Zero dependencies, zero network; entry + audit tables; unique-substring match |
48
+ | 🛠 Consumers | `memory` tool · frozen snapshot injection (system-prompt section, order `-50`) · `memory_recall` tool · `/memory` command · read-only Web panel | Model-facing writes/reads, budget-headed frozen snapshot, two-part recall, user-side command, browser drawer |
49
+
50
+ **Two tracks × two layers × per-agent key.** `user` track = facts about the user (preferences, communication style, landmines); `agent` track = environment facts, project conventions, lessons learned. Each track has `user-global` (cross-workspace) and `workspace` (per-session cwd) layers — Codex-style merged layering, not Hermes-style global-only. A third dimension isolates entries by the session's `agentPreset` (per-agent scope); entries without a preset stay in the shared layer visible to everyone. Session-scoped reads and write targeting follow the same visibility: a session sees — and `replace`/`remove` can only touch — shared entries plus its own agent's entries, and `workspace` entries only for its own cwd. The management surfaces (`/memory`, the panel) keep the full cross-agent view.
51
+
52
+ **Frozen snapshots.** The snapshot is rendered once per session at first prompt assembly (synchronous SQLite read + per-session cache) and never changes mid-session — prefix-cache stable by construction. Session-internal changes persist to disk + audit only.
53
+
54
+ ```
55
+ Consumer: memory tool Consumer: frozen snapshot (systemPrompt section, order -50)
56
+ add/replace/remove/query per-session freeze, budget-headed
57
+ │ writes (agent+callId) │ reads (sync, session cwd)
58
+ ▼ ▼
59
+ Service Definition: ctx.memory — budgets/add/replace/remove/query/seed
60
+ every write: budget precheck → ctx.approval.request (approval waterfall) → budget recheck → persist → audit
61
+ │
62
+ ▼
63
+ Provider: lib/store.mjs — node:sqlite (WAL, 0600), entries + audit tables, unique-substring match
64
+ ```
65
+
66
+ ## 🧰 Install & uninstall
67
+
68
+ ```sh
69
+ dsh plugin --profile <name> add ./dsh-memento # local checkout (no build step)
70
+ dsh plugin --profile <name> add dsh-memento # npm package (0.2.0+, published)
71
+ dsh plugin --profile <name> add git+https://github.com/PerryLink/dsh-memento.git # GitHub install
72
+ dsh plugin --profile <name> remove dsh-memento # uninstall: DB + session logs are kept
73
+ ```
74
+
75
+ After uninstall the memory database and the session logs that recorded memory activity remain; old sessions stay loadable.
76
+
77
+ ## ⚙️ Configuration
78
+
79
+ Every field is a validated Schemastery `Config`; invalid values fail loudly at load. Override in cordis.yml under the `memento` row.
80
+
81
+ | Field | Default | Meaning |
82
+ | --- | --- | --- |
83
+ | `enabled` | `true` | `false` removes the service, tools, snapshot, command, panel, and answerer entirely (no half-state) |
84
+ | `dbPath` | `''` → `$DSH_HOME/dsh-memento/memory.db` | absolute, or relative to `$DSH_HOME` |
85
+ | `budgets.user.userGlobal` / `budgets.user.workspace` | `2000` / `2000` | hard char budget per layer of the user track |
86
+ | `budgets.agent.userGlobal` / `budgets.agent.workspace` | `4000` / `4000` | hard char budget per layer of the agent track |
87
+ | `writePolicy` | `'ask'` | `'ask'` = user approval; `'auto'` = allow through (approval source recorded); `'off'` = reject. Model-invisible |
88
+ | `writePolicies` | `{}` | per-track/scope or per-source overrides: keys `user/workspace`, `agent/user-global`, `source:claude`, … → `ask`/`auto`/`off`; unmatched falls back to `writePolicy` |
89
+ | `language` | `'en'` | model-visible text and command output language: `'en'` (default) or `'zh'` — tool descriptions, frozen snapshot, `/memory` command, and web panel all follow it |
90
+ | `snapshotOrder` | `-50` | snapshot section order: after harness identity (`-100`), before persona (`0`) |
91
+ | `maxEntriesPerQuery` | `20` | default per-query result cap (explicit `limit` allowed, hard-capped at 1000) |
92
+ | `commandListLimit` | `50` | entries rendered per `/memory list` / `query` command |
93
+ | `commandAuditLimit` | `10` | audit rows rendered per `/memory audit` command |
94
+ | `recall.historyLimitDefault` / `recall.snippetCap` / `recall.snippetChars` / `recall.windowDays` | `8` / `5` / `300` / `30` | `memory_recall` history defaults: sessions scanned, snippets per session, snippet chars, recency window in days |
95
+ | `panelEntriesLimit` | `200` | web panel entries page size (and clamp) |
96
+ | `panelAuditLimit` | `20` | web panel audit rows by default (ceiling 200) |
97
+ | `auditRetentionDays` | `0` | audit retention: 0 = keep forever, >0 = prune rows older than N days at store open |
98
+ | `proposals.enabled` / `proposals.maxChars` / `proposals.maxPending` | `true` / `2000` / `8` | auto-capture: pending memory proposal after each successful compaction (truncated, one per session); disable or tune caps |
99
+
100
+ ## 🛠 Tools & surfaces
101
+
102
+ - **`memory`** — add/replace/remove/consolidate/query with Save/Skip guidance embedded in the description (save user preferences, corrections, environment facts, conventions, lessons; skip trivia, re-derivable facts, dumps, one-off paths). Writes ride the approval gate; reads are free; replace/remove target a **unique substring** (ambiguous matches fail with the candidate list); consolidate merges 1..20 entries into one with a single approval and one atomic write.
103
+ - **`memory_recall`** — two-part recall: bounded memory matches **plus** recent session-history matches via `ctx.sessionQuery` (degrades gracefully to memory-only where the service is absent).
104
+ - **`/memory`** — user-triggered command (not a model turn): `list` · `query <word>` · `add [--track=user|agent] [--scope=user-global|workspace] <text>` · `remove [flags] <substring>` · `consolidate [flags] <substring...> => <text>` · `proposals [approve|dismiss <id>]` · `budgets` · `audit` · `export` · `import <path>`. Command writes ride the same waterfall + policy; audit lands in the plugin audit table + `command/done`. `export` is read-only and dumps all entries + budgets as one JSON document; `import` restores it (file path or inline JSON, single approval, budget pre-checked) — a complete backup/migration round-trip. Imported entries get fresh ids and timestamps; proposals, audit rows and recall counts are not migrated.
105
+ - **Auto-capture proposals** — after a successful session compaction, the summary lands as a pending memory proposal (`agent/workspace`); approving writes it through the approval gate, dismissing drops it. Pending proposals appear in the frozen snapshot and the panel.
106
+ - **Web panel** — zero-build `dsh.client` drawer: browse entries by track/layer, search, budget bars, audit tail. Read-only by design: writes and approval happen through the `memory` tool and the built-in approval UI.
107
+
108
+ ## 🎓 What we learned from the terminal memories
109
+
110
+ dsh-memento is not a port of Claude Code, Codex, or Hermes — but its design deliberately absorbed the parts each of them got right, and refused the parts that hurt:
111
+
112
+ | Terminal memory | What it got right | What dsh-memento adopted |
113
+ | --- | --- | --- |
114
+ | **Claude Code** — `CLAUDE.md` | hierarchical **plain-text memory files** (user-level → project-level) that are human-readable, human-editable, and merged automatically into every session — memory you can read and fix yourself | plain-text entries; `user-global` / `workspace` layers merged per session; a store you can browse, `export`, and audit — transparency as a feature |
115
+ | **Codex** — `AGENTS.md` | **per-directory scoped instructions** auto-discovered and injected with zero model friction — locality beats volume, no tool call needed to "load" memory | `workspace` layer keyed by the session's cwd (Windows case-insensitive); the frozen snapshot is injected automatically at session start |
116
+ | **Hermes** — `memory.md` | **proactive memory saves** (save/update/delete) and, in [issue #48181](https://github.com/NousResearch/hermes-agent/issues/48181), the security lesson that a gate enforced only in the tool layer is bypassable by late tool injection — enforce it where every write path meets | the `memory` tool with explicit Save/Skip guidance + approval-gated auto-capture proposals; the approval gate lives **inside** `ctx.memory`'s write methods, not in the tool layer |
117
+
118
+ Sources: [Claude Code memory](https://code.claude.com/docs/en/memory) · [Codex AGENTS.md](https://developers.openai.com/codex/cli/agents-md) · [Hermes memory](https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/features/memory.md) · [Hermes #48181](https://github.com/NousResearch/hermes-agent/issues/48181).
119
+
120
+ And the parts we 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.
121
+
122
+ ## 🆚 How it's different
123
+
124
+ | Plugin | What it is | dsh-memento's difference |
125
+ | --- | --- | --- |
126
+ | dsh-memory-evolve | memory warehouse / evolution loops | a typed service seam, approval gate, and session-log audit; no warehouse ambition |
127
+ | dsh-mnemon | memory store helper | protocol + gate + audit, not another store |
128
+ | dsh-kb-sieve | knowledge-base sieving | no retrieval engineering: small-corpus substring search, cross-session recall via `session_search`/`sessionQuery` |
129
+ | dsh-tdai-memory | task-driven memory tooling | budgets are per track×layer and enforced in the service, not best-effort |
130
+ | claude-bridge | Claude Code bridging | DSH-native; a future `seed(source:'claude')` path lets a bridge feed the same store |
131
+ | dsh-external/Recall | external agent memory | local-first, zero-network, rides DSH's own approval seam |
132
+ | Official MCP memory examples | DSH's stated "memory = external MCP" position | the **native first-party** complement: same goal, no external server; both coexist |
133
+
134
+ 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`.
135
+
136
+ ## 🔒 Security boundaries
137
+
138
+ - **Public services only** (`tools`, `systemPrompt`, the approval seam). No engine / agent-loop / apiproxy / official-UI changes.
139
+ - **Zero network, zero credentials.** Local database; POSIX file mode `0600`.
140
+ - **Fail loud.** Corrupt DB or newer schema fails at load; full budgets and ambiguous substring matches fail with structured errors. Nothing silently swallowed or truncated.
141
+ - **One process, one store.** Multiple sessions in one process share the SQLite store (serialized writes, per-session audit). Two **processes** sharing one `$DSH_HOME` write the same file: last-writer-wins under SQLite locking — don't run two harness instances on one `$DSH_HOME` if you need cross-process consistency (same caveat the Hermes project documents).
142
+
143
+ ## ⚠️ Known limitations
144
+
145
+ - **Session events vocabulary is declared, not yet emitted (rc.6).** `memory/added|updated|removed|recalled|snapshot` are merge-declared in `types.d.ts`, but rc.6 has no registration surface for out-of-repo event types (unregistered appends would make persisted sessions unloadable). Audit completeness comes from the approval pair + the audit table; emission turns on automatically once a harness build registers the types. See [ARCHITECTURE.md](ARCHITECTURE.md) decision 4.
146
+ - **`ask` policy needs an answerer.** With no UI/ACP answerer composed, writes fail closed (`unavailable`) — by design, the approval seam's fail-closed stance.
147
+ - **No FTS5 indexing.** Substring search runs on case-insensitive `instr` (correct for CJK); recall ranking uses per-entry hit counts. FTS5's trigram tokenizer cannot index single-character CJK tokens, so it is not used — see [ARCHITECTURE.md](ARCHITECTURE.md) decision 10.
148
+
149
+ ## 🧪 Development
150
+
151
+ ```sh
152
+ npm install
153
+ npm test # node --test: 112 tests — budget, unique-substring, gate policy, store, snapshot, mock-ctx integration (S2/S3 invariants), V2 command/recall/panel/import
154
+ npm run typecheck # tsc --checkJs gate over index.mjs / lib / scripts
155
+ npm run check:coverage # line-coverage gate: lib ≥90%, index.mjs ≥85%, all files ≥90%
156
+ npm run check:readmes # five-language README consistency gate
157
+ ```
158
+
159
+ `lib/` is zero-DSH-dependency (node: builtins only); DSH imports exist only in `index.mjs`. Full discipline in [AGENTS.md](AGENTS.md); design decisions in [ARCHITECTURE.md](ARCHITECTURE.md).
160
+
161
+ ## 🏷 Topics
162
+
163
+ Suggested GitHub topics: `dsh` · `dsh-plugin` · `deepseek-harness` · `memory` · `agent-memory` · `approval` · `audit` · `sqlite` · `cordis` · `llm`
164
+
165
+ ## 📄 License
166
+
167
+ Apache License 2.0 — see [LICENSE](LICENSE). No third-party code is redistributed; see [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).