@ddtcorex/dsh-maestro-memory 1.0.1 → 1.1.1

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.
Files changed (45) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +39 -397
  3. package/lib/auto-memory.d.ts +21 -0
  4. package/lib/auto-memory.d.ts.map +1 -0
  5. package/lib/auto-memory.js +105 -0
  6. package/lib/auto-memory.js.map +1 -0
  7. package/lib/client.js +57 -2
  8. package/lib/health-score.d.ts +23 -0
  9. package/lib/health-score.d.ts.map +1 -0
  10. package/lib/health-score.js +28 -0
  11. package/lib/health-score.js.map +1 -0
  12. package/lib/index.d.ts +4 -0
  13. package/lib/index.d.ts.map +1 -1
  14. package/lib/index.js +123 -0
  15. package/lib/index.js.map +1 -1
  16. package/lib/memory/sanitize.d.ts +25 -0
  17. package/lib/memory/sanitize.d.ts.map +1 -0
  18. package/lib/memory/sanitize.js +44 -0
  19. package/lib/memory/sanitize.js.map +1 -0
  20. package/lib/memory/store.d.ts +4 -1
  21. package/lib/memory/store.d.ts.map +1 -1
  22. package/lib/memory/store.js +88 -11
  23. package/lib/memory/store.js.map +1 -1
  24. package/lib/prompt/snapshot.d.ts +6 -3
  25. package/lib/prompt/snapshot.d.ts.map +1 -1
  26. package/lib/prompt/snapshot.js +43 -5
  27. package/lib/prompt/snapshot.js.map +1 -1
  28. package/lib/skills-browser.d.ts.map +1 -1
  29. package/lib/skills-browser.js +4 -2
  30. package/lib/skills-browser.js.map +1 -1
  31. package/lib/storage/layout.d.ts +3 -0
  32. package/lib/storage/layout.d.ts.map +1 -1
  33. package/lib/storage/layout.js +11 -0
  34. package/lib/storage/layout.js.map +1 -1
  35. package/lib/types/client/index.d.ts.map +1 -1
  36. package/package.json +2 -1
  37. package/src/client/index.tsx +87 -3
  38. package/src/host/auto-memory.ts +97 -0
  39. package/src/host/health-score.ts +47 -0
  40. package/src/host/index.ts +111 -1
  41. package/src/host/memory/sanitize.ts +43 -0
  42. package/src/host/memory/store.ts +80 -10
  43. package/src/host/prompt/snapshot.ts +39 -5
  44. package/src/host/skills-browser.ts +5 -3
  45. package/src/host/storage/layout.ts +10 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,23 @@ All notable changes to this project are documented in this file. Format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this project uses
5
5
  [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [1.1.0] - 2026-08-29
8
+
9
+ Adopts proven mechanisms from `FuRongJun-1999/dsh-memory` in file-native form (no Python). Live-validated in chat, 268 tests.
10
+
11
+ ### Added
12
+
13
+ - **Desensitize sanitizer** (`src/host/memory/sanitize.ts`, 7 patterns: `sk-`, `api_key`, `password`, `Bearer`, `ID`, `phone` with English `[Filtered:...]`; pure-credential → `content filtered`).
14
+ - **Opt-in auto-memory hook** (`src/host/auto-memory.ts`, `config.autoMemory`, `session/event` → project/daily, desensitize + dedupe, default `enabled:false`).
15
+ - **Snapshot auto-recall top-4** (`src/host/prompt/snapshot.ts`, new `Project Context` section, newest 4 project entries 600 chars each, cap `autoRecall:1024`, keeps `recentDaily:512`).
16
+ - **Concurrency + abort gating** (`READ_ACTIONS`, `isMemoryConcurrencySafe`, `isConcurrencySafe` for `memory`/`dtodo`/`memory_suggest` + `signal.aborted` checks).
17
+ - **Health 5-dim scoring** (`src/host/health-score.ts`, `S/R/J/C/Safety` 0-10, `composite = min*0.4+mean*0.6`, client `HealthView` 5 cards).
18
+
19
+ ### Changed
20
+
21
+ - `store.add` desensitizes by default (`{desensitize:false}` to opt-out for tests).
22
+ - Snapshot caps now `2K/4K/6K+0.5K+1K` (autoRecall) with bounded `Project Context`.
23
+
7
24
  ## [1.0.1] - 2026-08-25
8
25
 
9
26
  Fix a live gap in the `memory` tool: the `daily` track only honored an explicit
package/README.md CHANGED
@@ -1,448 +1,90 @@
1
1
  # dsh-maestro-memory
2
2
 
3
- ## Purpose
3
+ Durable memory and todos for DeepSeek Harness (DSH) — preserves `~/.dsh/memories` in place.
4
4
 
5
- Durable, user-governed memory and todos for DeepSeek Harness (DSH) that preserves your existing `~/.dsh/memories` files in place.
6
-
7
- > **One sentence:** Give the AI in DSH cross-session durable memory and todos — the more you use it, the more it understands you, and switching sessions never loses context.
5
+ > Give the AI cross-session durable memory and todos the more you use it, the more it understands you.
8
6
 
9
7
  - **Package:** `@ddtcorex/dsh-maestro-memory` (`cordis.patch.yml` id `maestro-memory`)
10
- - **Changelog:** `CHANGELOG.md`
11
- - **Version:** `1.0.1`
12
-
13
- ---
8
+ - **Version:** `1.1.0` · **Changelog:** `CHANGELOG.md`
14
9
 
15
10
  ## Requirements
16
11
 
17
12
  - Node.js 22+, pnpm 11+
18
- - DSH `deepseek-harness` master (for `cordis`, `dsh-client-*` peers)
19
- - Existing `~/.dsh/memories` directory (created lazily if absent)
20
-
21
- ---
13
+ - DSH `deepseek-harness` master
22
14
 
23
15
  ## Install
24
16
 
25
- From the checked-out repo:
26
-
27
- ```sh
28
- pnpm install # install deps (frozen lockfile in CI)
29
- pnpm run build # tsc host + tsc client + build-client.mjs -> lib/
30
- pnpm run verify # tsc --noEmit host + client (typecheck)
31
- pnpm test # full Vitest suite (13 files, 199 tests)
32
- ```
33
-
34
- Manual verification of the client bundle:
35
-
36
17
  ```sh
37
- test -f lib/client.js && head -n 2 lib/client.js | grep -q "ModuleLoader" && echo "bundle ok"
38
- ls -lh lib/client.js lib/index.js
18
+ pnpm install
19
+ pnpm run build # -> lib/
20
+ pnpm test # 268 tests
39
21
  ```
40
22
 
41
- ### From a DSH profile (operator)
42
-
43
- The package is consumed as a DSH plugin via `cordis.patch.yml`. Two install paths:
44
-
45
- **Local link (development / recommended for cutover rehearsal):**
23
+ **DSH profile (operator):**
46
24
 
47
25
  ```sh
48
- # inside the profile that will own the plugin:
49
26
  dsh plugin --profile web add link:<workspace-root>/packages/dsh-maestro-memory
50
- # or manually in ~/.dsh/profiles/web/package.json:
51
- # "@ddtcorex/dsh-maestro-memory": "link:<workspace-root>/packages/dsh-maestro-memory"
52
- ```
53
-
54
- **Git / registry (production after release):**
55
-
56
- ```sh
57
- dsh plugin --profile web add github:ddtcorex/dsh-maestro-memory#<tag-or-sha>
58
- # pin to an exact commit SHA; branch names reuse stale tarballs (pnpm cache pitfall)
27
+ # production: dsh plugin --profile web add github:ddtcorex/dsh-maestro-memory#<tag-or-sha>
59
28
  ```
60
29
 
61
- After install, rebuild is not needed inside the profile — the host loads `lib/index.js` and the client loads `lib/client.js` via the `dsh.client` manifest. If you edited `src/client/`, rebuild at the checkout first (`pnpm run build`).
62
-
63
- ---
64
-
65
- ## Profile Patch
66
-
67
- `cordis.patch.yml` is **owned by the package** and applied automatically by `dsh plugin add`. Do not duplicate it in the profile.
30
+ `cordis.patch.yml` is shipped with the package do not duplicate it in the profile.
68
31
 
69
32
  ```yaml
70
- # dsh-maestro-memory/cordis.patch.yml (shipped with the package)
71
33
  - insert:
72
34
  - id: maestro-memory
73
35
  name: '@ddtcorex/dsh-maestro-memory'
74
36
  config:
75
- memoryDir: null # null -> ~/.dsh/memories
76
- snapshotOrder: 500 # systemPrompt.context order
77
- ```
78
-
79
- Profile `~/.dsh/profiles/web/package.json` after a correct install:
80
-
81
- ```json
82
- {
83
- "dsh": { "profile": { "bundles": ["@ddtcorex/dsh-maestro-memory"] } },
84
- "dependencies": {
85
- "@ddtcorex/dsh-maestro-memory": "link:<workspace-root>/packages/dsh-maestro-memory"
86
- }
87
- }
37
+ memoryDir: null # -> ~/.dsh/memories
38
+ snapshotOrder: 500
39
+ autoMemory: { enabled: false, userMessage: true, desensitize: true } # opt-in
88
40
  ```
89
41
 
90
- **Rules:**
42
+ ## Tools
91
43
 
92
- - `dependencies` value **must be `link:`**, not a semver. CI and `assertSingleOwner` reject non-link owners.
93
- - `bundles` must list exactly one owner for each compat tool (see below). Do not keep `dsh-memory-evolve` and `dsh-maestro-memory` in the same profile — they compete for `memory`/`dtodo` and for file ownership. The loader crashes on duplicate `id: maestro-memory` if you copy the patch row into the profile manually.
94
- - `memoryDir: null` resolves to `~/.dsh/memories` (`resolveMemoryRoot(null)`). Override only for tests / rehearsal (`--root /tmp/...`).
44
+ | Tool | Purpose |
45
+ |------|---------|
46
+ | `memory` | Five tracks `memory`/`user`/`project`/`key`/`daily` + archive/expand. `key` is gated via `memory_suggest`. |
47
+ | `dtodo` | Four tracks `life`/`work`/`project`/`daily` with ids, smart view (max 8). |
48
+ | `memory_suggest` | Gated proposals to `SUGGESTIONS.jsonl` — requires human approve. |
95
49
 
96
- Verify the profile in a rehearsal (see `src/host/migration/fixture.ts`):
97
-
98
- ```ts
99
- import { createFixtureProfile, assertSingleOwner } from '@ddtcorex/dsh-maestro-memory/migration/fixture'
100
- await createFixtureProfile({ profileDir: '/tmp/profile', packageDir: '/path/to/dsh-maestro-memory' })
101
- const res = await assertSingleOwner('/tmp/profile')
102
- console.assert(res.ok && res.owners['memory'] === '@ddtcorex/dsh-maestro-memory')
103
- ```
104
-
105
- ---
106
-
107
- ## Supported Tools
108
-
109
- All tools are registered via `ctx.tools.register` inside `ctx.effect(..., 'label')` so they dispose cleanly on unload. No HTTP.
110
-
111
- | Tool | Purpose | When visible |
112
- |------|---------|--------------|
113
- | `memory` | CRUD + query for five tracks (`memory`/`user`/`project`/`key`/`daily`) + archive/expand. See `src/host/memory/store.ts`. | Always |
114
- | `dtodo` | Four-track todos (`life`/`work`/`project`/`daily`) with stable 8-hex ids, status/due/quadrant, smart view (max 8), historical daily lookup. | Always |
115
- | `memory_suggest` | **Gated** — model proposes `memory`/`user`/`key`/`todo-*` into `SUGGESTIONS.jsonl`; never writes directly. Requires human approve/edit/reject via Review UI or `queue.decide` RPC. | Always |
116
- | `memory_review_status` | Read-only queue depth / write-block status (used by prompt hint / UI badge). | Always |
117
- | `skill_manage` | Browse / mutate `~/.agents/skills` (optional module). Disabled by default; enable only if the optional skills module is explicitly configured. | Opt-in |
118
-
119
- ### `memory` — actions and targets
120
-
121
- ```ts
122
- memory({
123
- action: 'add'|'list'|'replace'|'remove'|'archive'|'expand',
124
- target: 'memory'|'user'|'project'|'key'|'daily', // memory=global, key=per-cwd long-term
125
- content?: string, // add: entry body; replace: new body
126
- match?: string, // replace/remove/archive: unique substring of existing entry
127
- filter?: string, // list: content substring filter
128
- since?: string, until?: string, // list: YYYY-MM-DD
129
- limit?: number, recent?: boolean, branch?: string, archived?: boolean,
130
- branches?: string, // add key: csv "main,dev" (empty=all), branch scope
131
- summary?: string, // add key: one-line summary for progressive disclosure
132
- id?: string, // expand: [mem-xxxx] id
133
- cwd?: string, // project/key track working directory (defaults to session cwd)
134
- date?: string, // daily track YYYY-MM-DD
135
- })
136
- ```
137
-
138
- - **Progressive disclosure:** `key` entries are stored with an optional `[summary]` line; `list` without `expand` returns summaries; `expand` with `id` returns full text.
139
- - **Branch scope:** `key` entries may carry `[branch:main]` tags; `list` with `branch` filters to that branch + entries with no branch tag.
140
-
141
- ### `dtodo` — actions
142
-
143
- ```ts
144
- dtodo({
145
- action: 'add'|'list'|'done'|'update'|'remove',
146
- target?: 'life'|'work'|'project'|'daily', // add/list filter; add defaults to cwd?project:work
147
- content?: string,
148
- id?: string, // done/update/remove
149
- due?: string, // YYYY-MM-DD
150
- quadrant?: 'q1'|'q2'|'q3'|'q4', // or important/urgent booleans -> quadrant
151
- cat?: string, status?: 'pending'|'doing'|'done'|'blocked'|'cancelled',
152
- all?: boolean, past?: boolean, expired?: boolean, // list: smart-view controls
153
- cwd?: string, date?: string,
154
- })
155
- ```
156
-
157
- - **Smart view (default):** when `all !== true` and no filter, `list` returns at most 8 items ordered `overdue -> due today -> current project -> q1/q2 -> rest`. Uses local date, not UTC.
158
- - **History:** `past=true` alone shows only completed history; `past=true AND expired=true` includes expired unfinished daily todos (daily todos expire same day).
159
-
160
- ### `memory_suggest` (gated)
161
-
162
- ```ts
163
- memory_suggest({ target: 'memory'|'user'|'key'|'todo-life'|'todo-work'|'todo-project'|'todo-daily', content: string, reason: string })
164
- ```
165
-
166
- Dedupes by `(target, content)` within the queue (bumps `hits`), appends to `SUGGESTIONS.jsonl`. The model must never write `key`/`user` directly — queue + human click is the only activation path.
167
-
168
- ---
50
+ `memory` sanitizes sensitive fragments (`[Filtered:API key/password/token/ID/phone]`, pure credential → `content filtered`).
169
51
 
170
52
  ## System Prompt Snapshot
171
53
 
172
- Registered as `ctx.systemPrompt.context({ name: 'memory:snapshot', order: 500, text: (ctx) => renderSnapshot(cwd, branch) })`.
173
-
174
- Injected text is **bounded** and deterministic: `USER + global MEMORY + current-project KEY` (branch-filtered if `session.header.branch` is present), plus a header with `sessionId`/`sessionName` and an end-of-turn discipline note (rendered verbatim as `---` + newline + sentence):
175
-
176
- > End of every turn you must: 1. Write daily+project via memory entries (daily+project in one call) 2. Check dtodo list (bounded, max 8)
54
+ `memory:snapshot` (order 500) injects bounded deterministic context:
177
55
 
178
- `daily` and `project log` (`projects/<hash>/MEMORY.md`) are queryable via `memory` but **not injected**, to keep prompt cost predictable. New `prompt/snapshot.ts` must reproduce this contract or agents silently stop writing logs.
56
+ `USER + MEMORY + KEY (branch-filtered) + Project Context (auto-recall top-4, 600 chars each, cap 1024) + Recent Daily (last 2 days, 512) + header + discipline note`
179
57
 
180
- Each injected section also enforces a **per-track byte cap** — defaults `SNAPSHOT_SECTION_CAPS = { memory: 2048, user: 4096, key: 6144 }`, overridable per call via `renderSnapshot(store, ctx, { caps })`. Entries are kept newest-first; the oldest overflow is dropped. The newest entry is always kept: if it alone exceeds the cap **and** carries an `[summary:…]` header tag (parsed by `ENTRY_HEAD_RE`), it renders compacted to `head + [summary:…]`; untagged oversize entries stay whole rather than vanishing.
181
-
182
- ---
58
+ Caps: `memory 2048 / user 4096 / key 6144 / recentDaily 512 / autoRecall 1024`.
183
59
 
184
60
  ## UI & RPC
185
61
 
186
- - **UI:** exactly one `conversation.view` slot `{ name:'conversation.view', id:'maestro-memory', order:40, label:()=>'Memory' }` with internal tabs **Memory / Review queue / Todos**. Uses package-private RPC, no HTTP, no DOM hacks. Client injects `['slots','locale','conversation','sessions','connection']`.
187
- - **RPC channel:** `/dsh-maestro-memory` (`ctx.connection.rpc.handle` host, `ctx.connection.rpc.call` client). Endpoints: `queue.list`, `queue.decide` (`approve`/`reject`/`archive` with optional `edits`/`targets` + `cwd`), `memory.list`, `todo.list`, `todo.mutate`, `migration.inspect`/`dryRun`/`run`/`verify`, `status` (`{ queue, blocked }`). `migration.run` via RPC requires `payload.apply === true`.
188
-
189
- ---
62
+ One `conversation.view` slot (`maestro-memory`, order 40) with tabs **Memory / Review / Todos / Skills / Health**. Health shows `coverage`, `daily last 7d`, `longest` + 5-dim score `S/R/J/C/Safety` (composite `min*0.4+mean*0.6`).
190
63
 
191
- ## Cutover
192
-
193
- **Principle:** staged single-owner replacement — never run `dsh-memory-evolve` and `dsh-maestro-memory` in the same profile. The new internals, services, RPC methods, and slot ids use a Maestro namespace; compatibility is limited to agent-facing tool names and legacy file grammar.
194
-
195
- **Operator steps (production):**
196
-
197
- 1. **Preflight** on a copy, not live home (see Migration). Keep the live profile untouched until verification passes.
198
- 2. **Backup** the live `~/.dsh/memories` via `node scripts/migrate.mjs --root ~/.dsh/memories --apply` — this is the only write; it creates `manifest.json` + byte-identical `files/` under `.maestro-memory/backups/<utc-run-id>/` + `schema.json` + `journal`.
199
- 3. **Verify** (`--verify`) — must be `ok=true`, `mismatches=[]`. If not, writes are blocked (`write-block.json`) — resolve before continuing.
200
- 4. **Profile swap:** remove `dsh-memory-evolve` from `bundles`/`dependencies`, add `@ddtcorex/dsh-maestro-memory` as `link:` (or pinned git SHA). Ensure exactly one owner per compat tool (`memory`, `dtodo`).
201
- 5. **Reload profile:** restart `dsh web` at a user-approved window (ask first — do not kill the live `dsh web` process mid-session; it holds both :3000 and :3080). After restart, live-read every track (`memory` list for each target, `dtodo` list) before first mutation.
202
- 6. **One write** against live data, then `verify` again.
203
-
204
- **Before any writes, rollback is just a profile change** (remove Maestro, restore old bundle). After writes, restore files from the manifest.
205
-
206
- For a disposable rehearsal, use `src/host/migration/fixture.ts` (`createFixtureProfile`, `createCopiedMemoryRoot`, `assertSingleOwner`) — see `tests/m4-rehearsal.spec.ts` and the `Migration rehearsal` CI job. Never touch `~/.dsh/memories` in tests.
207
-
208
- ---
209
-
210
- ## Migration
211
-
212
- CLI: `node scripts/migrate.mjs --root <path> [--inspect|--dry-run|--verify|--apply] [--run-id <id>]`
213
-
214
- Default is **read-only**. The only write is `--apply`.
215
-
216
- | Command | Effect | Side effects |
217
- |---------|--------|--------------|
218
- | `--inspect` (default) | Inventory, parse, byte count, SHA-256, warnings for malformed JSONL / locks / non-canonical files | None |
219
- | `--dry-run` | Same as inspect, explicitly read-only | None |
220
- | `--apply` | **Backup + adopt:** byte-preserving copy of every file (excluding `.maestro-memory`) into `backups/<utc-run-id>/files/` + `manifest.json` (`path, bytes, sha256, inventory`) + `schema.json` + `migration-journal.jsonl` entry. Only after all required data parses; source content is never reformatted. | Writes `manifest`, `files/`, `schema.json`, `journal` |
221
- | `--verify` | Reopen with new stores, compare digest (`bytes`, `sha256`) + inventory (`memoryEntries`, `todoIds`, `queueValid`) against manifest. On mismatch, writes `.maestro-memory/write-block.json` and blocks mutations; on success clears the block. | Writes `write-block.json` on failure; clears on success |
222
-
223
- **Disk layout:**
224
-
225
- ```
226
- ~/.dsh/memories/
227
- MEMORY.md USER.md # may be absent until first global write
228
- MEMORY-archive.md USER-archive.md
229
- SUGGESTIONS.jsonl
230
- TODOS-life.md TODOS-work.md
231
- daily/YYYY-MM-DD.md daily/YYYY-MM-DD.todo.md
232
- projects/<sha1(cwd)[:12]>/
233
- MEMORY.md KEY.md
234
- KEY-archive.md TODOS.md
235
- .maestro-memory/
236
- schema.json
237
- migration-journal.jsonl
238
- write-block.json # present only when verify failed
239
- backups/<utc-run-id>/
240
- manifest.json # { files:[{path,relative,bytes,sha256,kind,...}], inventory, runId, at }
241
- files/... # byte-identical copies
242
- ```
64
+ RPC: `/dsh-maestro-memory` + loopback `/dsh-maestro-memory-health` + `/dsh-maestro-memory-propose`.
243
65
 
244
- **Warnings (non-fatal, reported in `inspect`/`dryRun`/`verify`):**
245
-
246
- - `non-canonical` — file does not round-trip through `§` parse/serialize (drift); mutation is refused until canonicalized.
247
- - `malformed todo` — entry missing timestamp/id in a todo file.
248
- - `malformed queue` — JSONL line in `SUGGESTIONS.jsonl` that does not parse as `{target, content}`.
249
-
250
- **Write-block:** `migration/service.ts:isWriteBlocked(root)` checks `.maestro-memory/write-block.json`. When blocked, `memory`/`dtodo` mutations return an error until `verify` passes or `rollback` clears it.
251
-
252
- **Examples:**
253
-
254
- ```sh
255
- node scripts/migrate.mjs --root ~/.dsh/memories # inspect (read-only)
256
- node scripts/migrate.mjs --root /tmp/mem --dry-run # dry-run
257
- node scripts/migrate.mjs --root /tmp/mem --apply # backup + adopt
258
- node scripts/migrate.mjs --root /tmp/mem --verify # verify (latest manifest)
259
- node scripts/migrate.mjs --root /tmp/mem --verify --run-id 20260824T151230.425Z
260
- ```
261
-
262
- ---
263
-
264
- ## Verification
265
-
266
- 1. After `inspect`/`dryRun`, confirm `ok=true`, expected `memoryEntries`/`todoIdsCount`/`queueValid`, and review `warnings`.
267
- 2. After `--apply`, confirm `manifest.json` exists, each `files/<relative>` copy is byte-identical (`sha256` matches), and `~/.dsh/memories` files are unchanged (no reformatting).
268
- 3. After `--verify`, confirm `ok=true`, `mismatches=[]`. If `ok=false`, check `mismatches` (`digest mismatch`, `byte count mismatch`, `todo ID set mismatch`, `inventory mismatch`) and `.maestro-memory/write-block.json`. No mutation should proceed while blocked.
269
- 4. After profile reload, live-read via tools/RPC (`memory` list for `memory`/`user`/`key`/`daily`/`project`, `dtodo` list for `life`/`work`/`project`/`daily`) and compare to pre-cutover inventory.
270
-
271
- The rehearsal suite (`tests/m4-rehearsal.spec.ts`) exercises the full sequence against a copied schema: fixture profile (`link:`) → one-owner proof → dry-run (no `.maestro-memory`) → backup (byte-preserving) → verify → profile reload (`apply`/`ctx.effect`) → live reads → one write → second verify (fails) → rollback (byte-identical) → verify (passes) → live home untouched.
272
-
273
- ---
274
-
275
- ## Rollback
276
-
277
- Rollback restores files **byte-identical** from a backup manifest. It is exercised and tested in `tests/m4-rehearsal.spec.ts`.
278
-
279
- **When to rollback:**
280
-
281
- - Before any writes: no rollback needed — just revert the profile change (remove Maestro bundle, restore old plugin).
282
- - After a failed `verify` or a bad write: restore from the backup that `verify` reports.
283
-
284
- **How (CLI / service API):**
285
-
286
- ```ts
287
- import { rollback } from './src/host/migration/service.ts'
288
- // restore latest (schema.json runId or newest backup)
289
- await rollback('/tmp/memories')
290
- // or specific run
291
- await rollback('/tmp/memories', '20260824T151230.425Z')
292
- ```
293
-
294
- Or via the `migration` RPC (host) if exposed. The service:
295
-
296
- - Copies each `manifest.files[].relative` from `backups/<runId>/files/` to its original `path`, verifying `sha256` after copy.
297
- - If a file was absent at backup time (`exists:false` in manifest) but appeared later, it is removed.
298
- - Clears `write-block.json` on completion and appends a `rollback` entry to `migration-journal.jsonl`.
299
- - Returns `{ ok, runId, manifestPath, restored, errors }` (`restored` = count of files restored/removed).
300
-
301
- **After rollback:**
302
-
303
- - `verify` must pass (`ok=true`, no mismatches).
304
- - A new write must succeed (the write-block is cleared).
305
-
306
- **Retention:** keep `~/.dsh/memories/.maestro-memory/backups/` for at least 90 days after cutover (per plan). Do not delete the manifest for the adopted run.
307
-
308
- ---
309
-
310
- ## Removed Features (intentionally not in v1)
311
-
312
- Source-grounded inventory. Propose any as a separate plugin later.
313
-
314
- | Source subsystem | Verdict | Rationale |
315
- |------------------|---------|-----------|
316
- | **Cross-device Git memory sync** (`lib/sync/*`) | **KEEP as optional module (M5)** | Large conflict-resolution product; must not delay local-data reliability. Disabled = zero network/Git activity. |
317
- | **Skills management/browser** (`lib/skills.js`, `lib/skills-manager.js`) | **KEEP as optional module (M6)** | Mutates `~/.agents/skills`; Maestro already owns skills in `maestro-skills`. Core stays read-only. |
318
- | **COI / external CLI dispatch, scheduler, broadcast, ws coordinator, stats, attachments, session orchestration** (`lib/coi/*`, `lib/session-orch.js`) | **DROP** | Independent orchestration platform; profile already has DSH Codex/Claude subagent bundles. |
319
- | **Advisor** (`lib/advisor/*`) | **REDESIGN** (separate opt-in plugin) | Full reviewer runtime with model calls and panel — not memory. |
320
- | **Notify / channel send / session images** (`lib/notify.js`) | **DROP** | IM/web delivery via global integration registry (`de_channel_send`, `de_notify`); unrelated to durable memory. |
321
- | **Local / docs / Codex search** (`lib/search-docs.js`, `lib/search/*`) | **DROP** | Host file scans / shell-out expands authority without being needed for memory. |
322
- | **Prompt injection library** (`lib/prompts.js`) | **DROP** | Reusable guidance belongs in `maestro-skills`. |
323
- | **Model registry / settings** (`lib/models.js`) | **DROP** | Overlays DSH model config; `lib/index.js` already injects DSH settings/llm. |
324
- | **Bookmarks, Mermaid, canvas, UI settings, aliases, update checker, i18n** (`lib/{bookmarks,mermaid,canvas,ui-settings,aliases,update,i18n}.js`) | **REDESIGN** | Independent features; propose individually if needed. |
325
- | **Client/WebUI broad tabs** (`src/client/index.ts` family) | **REDESIGN** | Legacy mounts many tabs/actions + HTTP API; v1 has one slot + package-private RPC. |
326
- | **HTTP API** (`lib/api.js` `/memory-evolve` prefix, `ctx.webServer`) | **DROP** | No `webServer` registration; only `ctx.connection.rpc.handle('/dsh-maestro-memory', …)`. |
327
-
328
- **Preserved in backup but not imported:** `advisor/`, `pending-skills/`, `plugin-state.json`, `search-docs-index.json`, `coi/` (copied byte-identical into `files/` and listed in `manifest.json`, never parsed as memory state).
329
-
330
- ---
331
-
332
- ## Workflow: Superpowers skills are mandatory
333
-
334
- Every change to this repository MUST follow the Superpowers skill workflow, in order:
335
-
336
- 1. **brainstorming** — explore intent, requirements, and design before any code; record the outcome in `docs/superpowers/specs/` (`YYYY-MM-DD-<topic>-design.md`).
337
- 2. **writing-plans** — turn an approved spec into a task-by-task plan with exact test and implementation sketches in `docs/superpowers/plans/` (`YYYY-MM-DD-<topic>.md`).
338
- 3. **executing-plans** — implement task by task with strict TDD: write the failing test first, verify RED, implement, verify GREEN, then commit that task as its own commit before starting the next.
339
-
340
- Do not skip ahead to implementation, batch multiple tasks into one commit, or commit while a task's tests are red. Trivial mechanical fixes may go straight to a commit but still need tests when behavior changes.
341
-
342
- ---
343
-
344
- ## Git Workflow
345
-
346
- - Never commit to `master` directly; batch related work on a feature branch (`feat/...`, `fix/...`). One TDD task = one commit while executing a plan.
347
- - Conventional commit subjects, imperative mood.
348
- - Push the branch and open an MR when the batch is green; rebase instead of merging master into the branch when the base moves.
349
- - `origin` is `git@github.com:ddtcorex/dsh-maestro-memory.git` (private, default branch `master`).
350
-
351
- ---
352
-
353
- ## Layout
354
-
355
- - `src/host/` — Cordis host plugin (`storage/legacy-format.ts`, `storage/layout.ts`, `storage/atomic-store.ts`, `memory/store.ts`, `todo/store.ts`, `review/queue.ts`, `migration/service.ts`, `prompt/snapshot.ts`, `index.ts`).
356
- - `src/client/` — DSH client bundle (`src/client/index.tsx` → `lib/client.js` via `scripts/build-client.mjs`, `conversation.view` id `maestro-memory` order 40, internal Memory/Review/Todos tabs, RPC `/dsh-maestro-memory`).
357
- - `tests/` — Vitest specs (`legacy-format.spec.ts`, `storage.spec.ts`, `atomic-store.spec.ts`, `memory-m2.spec.ts`, `todo-m3.spec.ts`, `migration.spec.ts`, `m4-rehearsal.spec.ts`, etc.).
358
- - `scripts/migrate.mjs` — migration CLI (`--root`, `--apply`/`--dry-run`/`--verify`).
359
-
360
- Build outputs in `lib/` (`lib/client.js` via `scripts/build-client.mjs`) are generated — never edit by hand.
361
-
362
- ---
363
-
364
- ## Development
66
+ ## Maintenance
365
67
 
366
68
  ```sh
367
- pnpm install
368
- pnpm test # full suite (13 files, 199 tests)
369
- pnpm run verify # tsc --noEmit host + client (typecheck)
370
- pnpm run build # tsc host + tsc client + bundle client.js
371
- pnpm exec vitest run tests/legacy-format.spec.ts tests/storage.spec.ts tests/atomic-store.spec.ts # unit
372
- pnpm exec vitest run tests/memory-m2.spec.ts tests/todo-m3.spec.ts tests/suggestion-queue.spec.ts tests/rpc-queue.spec.ts # integration
373
- pnpm exec vitest run tests/migration.spec.ts # migration
374
- pnpm exec vitest run tests/m4-rehearsal.spec.ts # rehearsal (fixture profile + rollback)
375
- node scripts/migrate.mjs --root ~/.dsh/memories # inspect (read-only)
376
- node scripts/migrate.mjs --root /tmp/mem --apply # backup + adopt
377
- node scripts/migrate.mjs --root /tmp/mem --verify # verify
69
+ node scripts/maestro-memory-remediate.mjs --apply --threshold-days 14
70
+ node scripts/enforce-rules.mjs --check-memory --threshold 90
378
71
  ```
379
72
 
380
- Run `pnpm test` after host changes, `pnpm run verify` after TypeScript changes, `pnpm run build` after editing `src/client/`. Do not kill/restart the live `dsh web` process serving a session — ask for a convenient restart window.
381
-
382
- ### CI
383
-
384
- `.github/workflows/ci.yml` runs on `push` to `master`/`main` and on every PR:
385
-
386
- | Job step | Command | What it proves |
387
- |----------|---------|----------------|
388
- | Install | `pnpm install --frozen-lockfile` | Reproducible deps |
389
- | Build | `pnpm run build` | Host + client compile |
390
- | Typecheck | `pnpm run verify` | `tsc --noEmit` host + client |
391
- | Unit | `vitest run tests/legacy-format.spec.ts tests/storage.spec.ts tests/atomic-store.spec.ts` | Pure delimiter / path / atomic-store |
392
- | Integration | `vitest run tests/memory-m2.spec.ts tests/todo-m3.spec.ts tests/suggestion-queue.spec.ts tests/rpc-queue.spec.ts tests/smoke.spec.ts` | Stores + queue + RPC |
393
- | Migration | `vitest run tests/migration.spec.ts` | inspect/dryRun/run/verify, backup manifest, write-block |
394
- | Rehearsal | `vitest run tests/m4-rehearsal.spec.ts` | Fixture `link:` profile, one-owner proof, copied-schema cutover + rollback, live home untouched |
395
- | Client | `test -f lib/client.js && grep ModuleLoader lib/client.js` | Bundle exists + header |
396
- | Full | `pnpm test` | Entire suite (13 files, 199 tests) |
397
-
398
- ---
399
-
400
- ## Security
401
-
402
- - Never print, commit, or add fixture values for secrets. Use obviously synthetic values in tests and docs.
403
- - No cloud service, database rewrite, telemetry, or sync in v1; M5 sync (Git-backed) is opt-in and disabled means zero network activity.
404
- - Secrets must never be echoed to the client; new secret-bearing fields need masking + constant-time comparison.
405
- - File permissions: memory files inherit host umask; `SUGGESTIONS.jsonl` is append-only; `write-block.json` is local-only.
406
-
407
- ---
408
-
409
- ## Troubleshooting
410
-
411
- | Symptom | Cause | Fix |
412
- |---------|-------|-----|
413
- | `tool "memory" has multiple owners` in `assertSingleOwner` | Both `dsh-memory-evolve` and `dsh-maestro-memory` in `bundles` | Remove the old plugin from the profile; keep exactly one owner per compat tool |
414
- | `profile cordis.patch.yml must not duplicate id maestro-memory` | Profile patch duplicates the package's `cordis.patch.yml` row | Delete the row from the profile patch; the package provides it |
415
- | `verify failed: digest mismatch` / `write-block.json` exists | File changed after backup (hand-edit, concurrent writer) | Inspect `write-block.json` mismatches, then `rollback(root, runId)` or resolve drift and `verify` again |
416
- | `non-canonical` warning | File contains non-`§`-canonical content (hand-edit) | Back up manually, then normalize via a single `replace` edit through the tool (creates canonical serialization) |
417
- | `mismatched todo IDs` after verify | Todo file edited outside the store | Roll back or re-run `inspect` and compare inventories |
418
- | DSH Web UI shows no Memory tab | `lib/client.js` not built or bundle header missing | `pnpm run build` at the checkout, then restart `dsh web` (user-approved window) and hard-refresh the browser |
419
-
420
- ---
421
-
422
- ## Ghi chú vận hành (Tiếng Việt — ngắn gọn)
423
-
424
- > English là ngôn ngữ chính của tài liệu và mã nguồn. Phần này chỉ tóm tắt thao tác cho người vận hành tiếng Việt.
73
+ ## Cutover
425
74
 
426
- - **Cài đặt:** `pnpm install` `pnpm run build` → `pnpm run verify` → `pnpm test`. Thêm plugin vào profile bằng `dsh plugin --profile web add link:/path/to/dsh-maestro-memory` (không copy `cordis.patch.yml` vào profile).
427
- - **Công cụ hỗ trợ:** `memory` (5 track), `dtodo` (4 track, smart view 8), `memory_suggest` (đề xuất phải được duyệt), `memory_review_status`. `skill_manage` chỉ khi bật module tùy chọn.
428
- - **Cutover (chuyển đổi):** chỉ chạy một plugin sở hữu `memory`/`dtodo`. Không chạy đồng thời `dsh-memory-evolve` `dsh-maestro-memory`. Thực hiện trên bản copy trước, sau đó mới áp dụng lên `~/.dsh/memories`.
429
- - **Migration:** `node scripts/migrate.mjs --root <path>` (mặc định chỉ đọc). `--apply` mới ghi backup + `schema.json`. `--verify` kiểm tra `sha256` + tồn kho; nếu lệch sẽ ghi `write-block.json` chặn ghi.
430
- - **Rollback:** `await rollback(root, runId)` khôi phục byte-identical từ `backups/<runId>/files/`; xóa `write-block.json` khi xong. Trước khi có ghi mới, rollback chỉ là đổi profile.
431
- - **Tính năng đã loại bỏ:** COI/broadcast, advisor, notify/`de_channel_send`, search, prompt library, model registry, bookmarks/mermaid/canvas. Chi tiết xem bảng "Removed Features".
432
- - **CI:** `build` + `typecheck` + `unit` + `integration` + `migration` + `rehearsal` + `client` + `full` — tất cả phải xanh trước khi merge.
75
+ 1. Backup: `node scripts/migrate.mjs --root ~/.dsh/memories --apply`
76
+ 2. Verify: `node scripts/migrate.mjs --root ~/.dsh/memories --verify` (must be `ok=true`)
77
+ 3. Swap profile: remove `dsh-memory-evolve`, add `dsh-maestro-memory` as `link:` or pinned SHA.
78
+ 4. Restart `dsh web` at user-approved window, then live-read each track.
433
79
 
434
- ---
80
+ Rollback: `rollback(root, runId)` restores byte-identical files from `backups/<runId>/`.
435
81
 
436
- ## Documentation
82
+ ## Migration CLI
437
83
 
438
- Keep current behavior in `README.md`. Do not add plans, transient investigation logs, or duplicate specifications under `docs/`; capture only durable operator and architecture knowledge. The one exception is Superpowers artifacts: specs under `docs/superpowers/specs/` and plans under `docs/superpowers/plans/` are kept as the design record for each change batch.
84
+ `node scripts/migrate.mjs --root <path> [--inspect|--dry-run|--verify|--apply]`
439
85
 
440
- ---
86
+ Default read-only; only `--apply` writes `manifest.json` + `backups/<runId>/files/` + `schema.json`.
441
87
 
442
- ## References
88
+ ## Verification
443
89
 
444
- - `src/host/migration/service.ts` inspect/dryRun/run/verify/rollback implementation
445
- - `src/host/migration/fixture.ts` — fixture profile + copied-schema helpers for rehearsal
446
- - `tests/m4-rehearsal.spec.ts` — end-to-end rehearsal (profile → backup → verify → reload → write → rollback)
447
- - `scripts/migrate.mjs` — operations CLI (read-only by default, `--apply` to write)
448
- - `.github/workflows/ci.yml` — build / typecheck / unit / integration / migration / client / full
90
+ After `--apply`/`--verify`: `ok=true`, `mismatches=[]`, `manifest.json` byte-identical. Rehearsal suite `tests/m4-rehearsal.spec.ts` covers fixture `link:` profile → backup → verifyrollback.
@@ -0,0 +1,21 @@
1
+ /**
2
+ * auto-memory.ts — opt-in automatic session/event → memory persistence.
3
+ * Ported from FuRongJun-1999/dsh-memory hooks.ts (memory hooks) but
4
+ * file-native (MaestroMemoryStore) and English-only.
5
+ */
6
+ import type { MaestroMemoryStore } from './memory/store.ts';
7
+ export interface AutoMemoryOptions {
8
+ enabled: boolean;
9
+ userMessage: boolean;
10
+ assistantMessage: boolean;
11
+ toolResult: boolean;
12
+ importance: number;
13
+ desensitize: boolean;
14
+ }
15
+ export declare const DEFAULT_AUTO_MEMORY: AutoMemoryOptions;
16
+ /**
17
+ * Install session/event hooks for auto-memory.
18
+ * Returns a disposer (ctx.on returns disposer in Cordis 4).
19
+ */
20
+ export declare function installAutoMemoryHooks(ctx: any, store: MaestroMemoryStore, opts: AutoMemoryOptions): () => void;
21
+ //# sourceMappingURL=auto-memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-memory.d.ts","sourceRoot":"","sources":["../src/host/auto-memory.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAG3D,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,EAAE,OAAO,CAAA;IACpB,gBAAgB,EAAE,OAAO,CAAA;IACzB,UAAU,EAAE,OAAO,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,OAAO,CAAA;CACrB;AAED,eAAO,MAAM,mBAAmB,EAAE,iBAOjC,CAAA;AAgBD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,iBAAiB,GAAG,MAAM,IAAI,CAmD/G"}