dsh-memvault
将MemVault核心记忆块注入系统提示词并提供管理面板 适合已部署MemVault、需要深度管理和回溯AI长期记忆的专业用户。
安裝
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:zhang66633/dsh-memvault說明文件
閱讀完整 README ↗⚙️ Configuration
Every knob is described once, in lib/config.js: type, bounds, default and a
description. From that one description come the code defaults (DEFAULT_EXTRACT
and friends), the Config schema DSH validates the entry against and projects for
the Plugins page, and the panel's view of what is configured. The tables below are
that description in prose.
Setting values is unchanged — a config block in cordis.patch.yml. What changed
is what happens when one is wrong: a value that does not type-check is reported
(and the default is used), an undeclared key is reported, and both surface as a
warning banner in the panel rather than only in the host log.
Read half
| Field | Default | Meaning |
|---|---|---|
enabled | true | false injects the empty string forever |
dbPath | $MEMVAULT_DB_PATH else D:/Claude_code/memory/data/memvault.db | Path to memvault.db |
scopes | [{user,lenovo},{agent,claude-code-memory}] | Blocks are keyed by (scope_type, scope_id); both halves must be given explicitly — a foreign process has no "current scope" |
labels | [] | Only these labels; empty means every block in the scopes |
maxChars | 4000 | Budget for the whole rendered string, header included |
refreshMs | 30000 | TTL for the cached render |
order | 210 | Position among runtime-context contributions (e.g. relative to skill-catalog) |
name | memvault:core | The name shown in the trajectory's injected-context list |
Write half (config.extract)
| Field | Default | Meaning |
|---|---|---|
enabled | true | false makes the plugin read-only |
everyNTurns | 3 | Turns that must accumulate before an extraction is considered; 1 = every turn (still windowed) |
idleMs | 20000 | Quiet time that hands the window over. A new finished turn re-arms it |
windowTurns | 8 | Hard cap: reaching this many turns extracts immediately, so a session that never pauses still gets extracted |
endReasons | ['completed','max-tokens'] | Which turn/end reasons count. aborted / error / interrupted / blocked are skipped — but max-tokens is not, because a truncated turn still holds the user's message |
includeAssistant / includeTools | false / false | The extractor does not separate roles; the assistant's own prose became stored "facts", so it is off by default |
maxInputChars | 6000 | Transcript budget; newest lines win, oldest are dropped |
minTranscriptChars | 40 | Below this a turn is not worth a process plus an LLM call — it is skipped, and the watermark still advances |
timeoutMs | 120000 | Timeout kills the child and records one warning |
pythonPath | $MEMVAULT_PYTHON else /.venv/{Scripts/python.exe,bin/python} | Interpreter that can import memvault |
projectDir | $MEMVAULT_DIR else D:/Claude_code/memory | Working directory of the child |
user / agent / run | lenovo / claude-code-memory / unset | The three axes are orthogonal; any combination is allowed |
env | {PYTHONUTF8:'1', PYTHONIOENCODING:'utf-8'} | Child environment. Removing these reintroduces the cp936/emoji bug below |
statePath | ~/.dsh/storages/dsh-memvault-state.json | Watermarks + the last five diagnostics; atomic write |
Panel routes
The browser half has no knobs of its own; it reads the two routes the host half registers. They exist only when the composition provides webServer.
| Route | Method | Answers |
|---|---|---|
/memvault/api/status | GET | Injected blocks (label, scope, characters, stored limit, value clamped to 2000), read config, cache age, extraction config, watermark session count, last five diagnostics, and whether writes are enabled |
/memvault/api/refresh | POST | The same payload after dropping the render TTL — the 立即重读 button |
/memvault/api/blocks | POST | One block action through MemVault's CLI: { action: 'set', type, id, label, value, limit? } (upsert) or { action: 'delete', type, id, label } |
/memvault/api/flush | POST | Extract every pending window now (the 立即抽取 button). The answer says how many sessions were handed over; the work stays queued. Answers 405 when extraction is disabled |
/memvault/api/memories | GET | Browse stored memories: q (substring), type, user, agent, run, ids (an explicit id list, how a diagnostic's output is looked up), flagged=1 (only marked rows), limit (default 20, capped at 200), offset. Answers { rows, total, limit, offset, order, applied, mode, flags, flaggedCount } — applied echoes what was actually used, and mode: 'substring' says out loud that this is not ranked retrieval |
/memvault/api/memory | GET | One memory's provenance (?id=): the row, history (every audited decision with old/new text) and relations (the contradictions, with the other side's text and weight). 404 for an unknown id, missing: true in the body |
/memvault/api/flag | POST | Mark or clear one memory for review: { id, flagged: true | false, note? }. Writes the plugin's own state file — MemVault is untouched — and answers with the whole bounded flag map. 403 when panel.writes: false |
/memvault/api/review | GET | The review queue: every flagged memory with its provenance, the window that produced it, whether that window's input is still retained (replayable), the retained inputText when it is, and request — a ready-to-paste instruction listing ids, texts and provenance for a model to propose fixes |
/memvault/api/replay | POST | Replay one retained input: { key, text?, extractor?: 'inherit' | 'rule' | 'llm' }. Answers 202 because the work is queued; the outcome appears as a diagnostic with replayOf. Writes the store through MemVault's own add(); 403 when panel.writes: false |
The handlers refuse anything that is not a loopback Host with a matching Origin (when the browser sends one) and a same-site Sec-Fetch-Site, answering 403 otherwise. They are exact routes, so they match before the shell's index//api handlers. A bad action, an unknown scope type, an empty value or an over-long value is a 400 before anything is spawned; a CLI failure is a 502.
Panel
| Field | Default | Meaning |
|---|---|---|
panel.writes | true | false makes the panel read-only: /memvault/api/blocks answers 403 instead of running the CLI |
Config schema
lib/schema.js builds a native Schemastery schema from the same spec and exports
it as Config, which is what DSH reads off a plugin module. Two consequences:
- validation. A config that fails the schema keeps the entry from activating (DSH's documented behaviour), so the schema is stricter than the resolver on purpose: bounds and types are enforced before the plugin runs.
- a settings page.
dsh --dump-config-schemaand the Plugins page project the same schema into JSON Schema, which is what renders the fields.
@deepseek-ai/schemastery comes from the DSH runtime and is declared as a peer.
The import is attempted once and tolerated when absent, because the bundled smoke
tests run on plain Node: outside DSH the plugin exports no Config, still loads,
and warns that it has no settings page. In DSH it always resolves.