zhang66633/dsh-memvault ↗★ 1

dsh-memvault

DeepSeek Harness plugin: inject MemVault core memory blocks into the system prompt, extract finished turns as one windowed pass, and show, edit, trace, review and replay all of it in a memory panel 适合已部署MemVault、需要深度管理和回溯AI长期记忆的专业用户。

パッケージ
dsh-memvault
互換性
未検証
Harness ピア範囲
>=0.1.0-rc.1 <0.2.0-0 || >=0.2.0-rc.1 <0.3.0-0
バージョン
0.9.1
ライセンス
MIT
最終更新
2026/09/29

インストール

$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

FieldDefaultMeaning
enabledtruefalse injects the empty string forever
dbPath$MEMVAULT_DB_PATH else D:/Claude_code/memory/data/memvault.dbPath 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
maxChars4000Budget for the whole rendered string, header included
refreshMs30000TTL for the cached render
order210Position among runtime-context contributions (e.g. relative to skill-catalog)
namememvault:coreThe name shown in the trajectory's injected-context list

Write half (config.extract)

FieldDefaultMeaning
enabledtruefalse makes the plugin read-only
everyNTurns3Turns that must accumulate before an extraction is considered; 1 = every turn (still windowed)
idleMs20000Quiet time that hands the window over. A new finished turn re-arms it
windowTurns8Hard 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 / includeToolsfalse / falseThe extractor does not separate roles; the assistant's own prose became stored "facts", so it is off by default
maxInputChars6000Transcript budget; newest lines win, oldest are dropped
minTranscriptChars40Below this a turn is not worth a process plus an LLM call — it is skipped, and the watermark still advances
timeoutMs120000Timeout 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/memoryWorking directory of the child
user / agent / runlenovo / claude-code-memory / unsetThe 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.jsonWatermarks + 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.

RouteMethodAnswers
/memvault/api/statusGETInjected 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/refreshPOSTThe same payload after dropping the render TTL — the 立即重读 button
/memvault/api/blocksPOSTOne block action through MemVault's CLI: { action: 'set', type, id, label, value, limit? } (upsert) or { action: 'delete', type, id, label }
/memvault/api/flushPOSTExtract 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/memoriesGETBrowse 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/memoryGETOne 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/flagPOSTMark 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/reviewGETThe 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/replayPOSTReplay 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

FieldDefaultMeaning
panel.writestruefalse 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-schema and 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.