Making your memory plugin speak dsh-memory-protocol
August 16, 2026 · View on GitHub
中文版见 adapters-guide.zh.md。The protocol itself is specified in protocol-v1.md; the adapter registry service is
ctx.memoryAdapters.
Two roads lead to the protocol. Pick the one that matches what your plugin already is:
| Your plugin is… | Road | What you implement |
|---|---|---|
| a DSH plugin with its own store | Provider conformance | the provider surface from test/protocol-conformance/README.md — you keep your store, you implement the protocol semantics (gate inside the service, budgets, audit) |
| a DSH plugin that wants to feed/read dsh-memento's store | Adapter registration | register an adapter on ctx.memoryAdapters; your external format converts to/from protocol entries |
This guide covers the adapter road. If your plugin keeps its own store and merely wants to
interoperate, register an adapter that converts your store's export format; imports/exports
then ride the same approval-gated seed path.
The adapter contract
// your-plugin/adapters/my-format.mjs
export const myFormatAdapter = {
id: 'my-format', // lowercase kebab-case, unique in the registry
name: 'My format',
description: 'Converts my-format exports to protocol entries and back.',
version: '1.0.0',
importFormats: ['my-format-v1'], // labels shown by /memory adapters
exportFormat: 'my-format-v1',
// payload -> protocol entry inputs. Pure data conversion ONLY:
// never call a model for extraction or summarization.
adapt(payload) {
// return { entries: [{ track, scope, text, source?, tags?, workspaceKey?, agentKey? }] }
},
// protocol entries -> your format (JSON-safe value)
export(entries) {
// return your format document
},
}
Registering (reversibly)
import { myFormatAdapter } from './adapters/my-format.mjs'
export function apply(ctx) {
const adapters = ctx.get('memoryAdapters') // optional dependency: dsh-memento may be absent
if (adapters !== undefined) {
// register() returns a disposer — hand it to ctx.effect and Cordis
// undoes the registration when your plugin stops/updates.
ctx.effect(() => adapters.register(myFormatAdapter))
}
}
Registration errors fail loudly (INVALID_INPUT for a malformed adapter, duplicate id
included). Never catch and swallow them.
Conversion rules that keep you conformant
- Convert data, never infer. If a payload has no fact-level entries (e.g. raw chat
transcripts), fail loudly with
ADAPTER_PAYLOADand tell the caller to extract first. The protocol is a storage interop surface, not a reasoning surface. - Validate every field you emit.
track/scopemust be protocol vocabulary;textnon-empty;tags≤16 entries × ≤32 chars. The provider re-validates onseed, but a good adapter never emits garbage. - Fail loud on unrepresentable structure. A line you cannot map to an entry should throw with a line number, not become a mangled entry.
- Be honest about lossiness in
description.exportmay omit fields your format has no concept for (e.g. tags) — say so. - Idempotent and side-effect-free.
adapt/exportmust not write files, hit the network, or touch the store. Import persistence happens inseed— one approval, one transaction, per-entry audit rows.
What users get
Once your adapter is registered (yours ships with your plugin, or anyone registers it at runtime), these surfaces light up for free:
/memory adapters # list registered adapters + formats
/memory import --adapter=my-format ./export.json # convert + seed: ONE approval, audited
/memory export --adapter=my-format # read-only conversion to stdout
Reference adapters (shipped with dsh-memento)
| Adapter id | External format | Notes |
|---|---|---|
mem0 | mem0 fact collections ({facts: [{memory, metadata?}]} or bare array) | metadata.category/metadata.tags become tags; raw messages arrays are rejected — extraction is the caller's job |
hermes-memory-md | Hermes memory.md (## section + bullets) | section names become tags; non-bullet prose lines fail loudly |
claude-code-memory-md | CLAUDE.md-style markdown (headings, bullets, paragraphs) | bullets and blank-line-separated paragraphs become entries; section names become tags |
These are production examples of the contract — read lib/adapters.mjs alongside this guide.