Back to home@runfali

dsh-mem0-plugins

dsh 持久化记忆插件 - 依托自建的 mem0‑graph 服务器实现自动回忆与回写。

Stars
0
Language
JavaScript
Created
Aug 24, 2026
Updated
Aug 24, 2026
GitHub repo

Introduction

dsh-mem0-plugins

License: MIT Node Platform

English | 简体中文

Persistent memory for the DeepSeek Harness (dsh) web profile, backed by a self-hosted Mem0 server. The plugin gives your agent long-term memory with zero manual effort: relevant memories are recalled before answering, and every finished conversation turn is distilled into facts and written back automatically.

[!IMPORTANT] Compatibility — read this first. This plugin speaks the custom HTTP API of runfali/mem0-graph (X-API-Key auth, POST /search, POST/PUT/DELETE /memories, POST /evolve/feedback) and works only against a server deployed from that project. It does not support Mem0 Cloud or the official mem0 OSS REST/SDK API, and it is not a drop-in for other Mem0 deployments.

It ships as a standard dsh bundle plugin: dsh plugin add to install, dsh plugin remove to uninstall. It changes no dsh source code.


Table of Contents

What it does automatically

CapabilityWhenHow
Tool-driven recallBefore each answerA persistent usage section steers the model to call mem0_search first; the UI tool card makes the recall visible. Long queries are distilled before searching.
Forced recall stepFirst step of every turnInjects a plugin-source reminder ("search memory before answering") via agent/pre-step. Trivial turns are skipped; disable with forceRecallStep.
Automatic write-backAfter every completed turnSends "user message + assistant reply" to the server-side LLM extraction (infer: true). Pure-JSON tool output is replaced with placeholders so key names never leak in as "facts".
Tidal coalescingAt write timeShort turns of the same session are bucketed per user and flushed as one batched write (idle 5 s / window 15 s / 5 turns / 4 000 chars — whichever hits first), amortizing server LLM extraction calls. Oversized messages (> 2 000 chars) bypass the bucket and write directly.
Evolve feedback loopAfter update/deleteBest-effort POST /evolve/feedback (correction / useless) feeds the server-side salience evolution.

Interrupted turns are never written: a half-streamed reply is not a durable conversation truth.

Model tools

Four tools are registered under the dsh agent:

ToolPurpose
mem0_searchSemantic search over the user's memories (per-call top_k / rerank overrides).
mem0_addStore a durable fact verbatim — no server-side LLM extraction.
mem0_updateFix an existing memory by ID (reports correction feedback).
mem0_deleteForget a memory by ID (reports useless feedback).

Requirements

  • Node.js ≥ 22 and a working DeepSeek Harness install (web profile).
  • A running runfali/mem0-graph server reachable over HTTP (e.g. http://127.0.0.1:8888).
  • If the server runs with auth enabled, an API key created from its dashboard. With AUTH_DISABLED=true, leave the key empty.

Installation

# Install into the web profile (restart dsh afterwards)
dsh plugin --profile web add /path/to/dsh-mem0-plugins

# Uninstall
dsh plugin --profile web remove dsh-mem0-plugins

The plugin is enabled by default and needs zero configuration when pointed at a local AUTH_DISABLED server. Changes made in the settings page take effect immediately — no restart needed. To turn memory off entirely, flip Enable plugin off in the settings card; the card header always shows the current enabled state and host at a glance.

Configuration

All settings live in the dsh settings page under the mem0 namespace. Values saved there override profile-layer defaults.

Connection & identity

KeyDefaultDescription
enabledtrueMaster switch. When off: no recall, no writes, tools report "plugin disabled".
hosthttp://127.0.0.1:8888Base URL of the self-hosted mem0-graph server.
apiKey(empty)Sent as the X-API-Key header. Leave empty for AUTH_DISABLED deployments.
userIddsh-userOwner of the memories; shared across sessions.
agentIddshAttached as agent_id on writes.

Connection and identity settings

Automatic recall & query distillation

KeyDefaultDescription
forceRecallSteptrueForce-recall step (Plan B): inject a "must call mem0_search first" notice every turn (trivial turns skipped). Off = rely on usage guidance only.
topK10Max results per search (1–50).
rerankfalseRequest full-depth reranking (server needs a reranker configured).
distillEnabledtrueMaster switch for query distillation (see below).
distillMinChars500Queries up to this length go straight to /search unchanged — zero loss, zero extra calls.
distillInputMaxChars8000Truncation cap for text sent to the distillation model.
distillBaseUrlauthor's private endpointOpenAI-compatible endpoint used to distill long queries. Empty = skip distillation. The shipped default points at the author's internal deployment — override it with your own endpoint.
distillApiKeyauthor's private keyBearer token for the distillation endpoint.
distillModelQwen3.5-9BDistillation model id (a small local model is plenty).
distillTimeoutMs90000Per-request distillation timeout.
distillRetryAfterMs20000Hedged-request threshold: if the first request is still silent after this delay, fire a second concurrent one; first response wins.

Recall settings: top-k, rerank, distillation

Distillation model, timeout and hedging

Automatic write-back (tidal coalescing)

KeyDefaultDescription
syncEnabledtrueEnd-of-turn write-back master switch.
coalesceEnabledtrueBucket short turns and flush merged writes; off = one request per turn.
coalesceIdleMs5000Flush a bucket after this much inactivity.
coalesceWindowMs15000Flush a bucket after this much wall time.
coalesceMaxTurns5Max turns per bucket.
coalesceMaxChars4000Max characters per bucket.
fastpathChars2000Turns longer than this skip the bucket and write immediately.
feedbackEnabledtrueReport evolve feedback after successful update/delete.

Write-back settings: coalescing thresholds, fast path, evolve feedback

Reliability & timeouts

KeyDefaultDescription
queueMaxLen50Pending-write queue cap; oldest entry dropped when full.
breakerThreshold5Consecutive failures that open the circuit breaker.
breakerCooldownMs120000Breaker cooldown before half-open retry.
requestTimeoutMs300000Hard per-request cap shared by search/add (mirrors hermes httpx timeout=300.0; worst-case server-side LLM fallback is ~180 s). There is deliberately no second tool-level timeout.

Reliability settings: queue, breaker, request timeout

To change profile-layer defaults (applies to all users of the machine), append to ~/.dsh/profiles/web/cordis.patch.yml:

- id: mem0
  config:
    enabled: true
    host: http://mem0.internal:8888
    apiKey: your-admin-api-key

Recall design

Forced recall-step notice plus multi-angle Chinese-keyword mem0_search

  • Explicit tool pipeline. No silent background prefetch — the dsh platform has no content-injection hook after message echo (see docs/COMPARISON.md for the platform timing analysis). The model calls mem0_search following usage guidance; the tool card shows the recall happening, and distillation / hedging / the breaker all run inside the tool.
  • Forced recall step (default on). Every turn's first step gets a plugin-source notice ("answer only after calling mem0_search") rendered as a collapsed context-injection line in the UI. It never writes memory, skips trivial turns, and can be turned off with forceRecallStep.
  • Trivial-input guard (src/guards.js). Pure greetings, confirmations, and slash commands are classified by exact whole-string match against word lists — a real sentence is never misclassified.
  • Query distillation. Ported from hermes agent/memory_manager.py::_distill_query, applied to the recall query only (never the write path):
    1. Query ≤ distillMinChars: search as-is;
    2. Long queries (pasted logs/code): truncate to distillInputMaxChars, ask a small model for a 2–4 keyword retrieval intent, then search with that;
    3. Language-drift guard: distilled output of Chinese input containing Vietnamese diacritics or other non-Latin/non-CJK characters (an observed small-model routing failure) is treated as pollution and rejected;
    4. Hedged requests: a silent first request triggers one concurrent retry;
    5. Total failure: fall back to the raw query — retrieval never silently dies.

Reliability design

  • Circuit breaker: breakerThreshold consecutive failures pause all mem0 traffic; auto half-open after breakerCooldownMs. HTTP 404 / not-found style client errors don't count toward the breaker.
  • Connection-level retry: connection-refused/DNS failures retry once — the request most likely never reached the server, so no duplicate writes.
  • Bounded queue: pending writes capped at queueMaxLen (oldest dropped) so a long server outage can't grow memory unbounded.
  • Flush-on-dispose: all open coalescing buckets are flushed when the plugin stops — queued memories are never lost.

Observability

Coalescing and write-path hygiene counters go to the dsh host process logs (not the browser): the plugin logs through both ctx.logger (internal) and console.log/warn (host stdout). With systemd: journalctl -u dsh.service -f; otherwise watch the dsh process stdout.

Every merged flush logs one info line with cumulative totals:

[dsh-mem0] mem0 coalesced 3 turn(s) into 1 write (session=<id>, saved 2 call(s), chars=512, trigger=idle; totals: batches=12 savedCalls=34 dropped=0 jsonSanitized=3)
CounterMeaning
savedCallsServer LLM extraction calls saved by coalescing (merging N turns saves N−1).
droppedOldest-entry drops due to a full queue (each also logs a warn).
jsonSanitizedPure-JSON messages stripped before write-back.
batches / directMerged batch writes / fast-path direct writes.

Queue drops log a warn; JSON stripping and fast-path writes are debug-level; breaker transitions and failed direct writes always warn.

Development & testing

git clone <this-repo> && cd dsh-mem0-plugins
npm install                # or symlink your dsh node_modules for offline dev
node test/smoke.mjs        # host half: apply pipeline + tools + write path + guards
node test/client-smoke.mjs # client half: bundle load + locale/slots + settings form save

Troubleshooting

SymptomFix
Tools report "plugin disabled"Turn enabled on in the settings page and check host.
"circuit breaker open"The server failed repeatedly. Restore the server and wait out the cooldown, or lower breakerThreshold.
HTTP 401apiKey missing/wrong — required unless the server runs AUTH_DISABLED=true.
"server unreachable"Confirm reachability: curl http://<host>/openapi.json.
Memories never recalledNothing relevant under that userId (check GET /memories), or the model skipped mem0_search — verify the forced-recall notice isn't being skipped along with its tool card.

Documentation

  • docs/COMPARISON.md — design notes vs. the hermes original, including the platform timing constraints that shaped the tool-driven recall (Chinese).

License

MIT © 2026 dsh-mem0 contributors