DSH Mem
capability seam(Service Definition + Service Provider + Consumer)
이것은 DeepSeek Harness(DSH) 플러그인입니다. 이 사이트는 GitHub README, 설치 정보, 유지보수 상태, 공개 보안 시그널을 모아 보여줍니다.
업스트림에서 중국어 README를 제공하지 않아 저장소 원본 내용을 표시합니다.
dsh-mem — cross-session memory for DeepSeek Harness
English | 中文
An out-of-tree bundle plugin for DeepSeek Harness (dsh) that implements a complete capability seam — Service Definition + Service Provider + Consumer. It gives agents durable long-term memory shared across every session: the memory_save / memory_recall / memory_forget / memory_list tools persist facts, preferences, and decisions to $DSH_HOME/memory/memory.json.
Install
Install from the npm registry (published as dsh-mem):
dsh plugin --profile <name> add dsh-mem
dsh plugin addis the dsh way to install a plugin: it resolves the package from npm (via pnpm) into the profile and registers its bundle layer. Do not usenpm install dsh-mem— that installs the package as a plain dependency without activating any profile layer.
From git instead (runs the package's self-contained prepare build; the first install asks you to allow the build in the profile's pnpm-workspace.yaml):
dsh plugin --profile demo add github:Jelee0145/dsh-mem
Or from a local checkout:
dsh plugin --profile demo add ./dsh-memory
Verify the composed layer without booting, then boot:
dsh --profile demo --dump-config # expect a "# == dsh-mem" layer with memory / tool-memory rows
dsh --profile demo # new sessions can call the memory_* tools
Uninstall: dsh plugin --profile demo remove dsh-mem.
To disable without uninstalling (hot-reloaded, no restart), disable the rows in the profile's own cordis.patch.yml:
- id: memory
disabled: true
- id: tool-memory
disabled: true
Memory data is never touched by uninstall; it lives in $DSH_HOME/memory/memory.json (default ~/.dsh/memory/). Back it up or delete it separately if you want to clear it.
The capability seam
| Role | Module | Mounted row | Notes |
|---|---|---|---|
| Service Definition | src/memory.ts (dsh-mem/memory) | — | abstract MemoryService declaring the ctx.memory contract and types; loading it directly fails loud |
| Service Provider | src/provider.ts (dsh-mem/provider) | memory | MemoryFile extends MemoryService; atomic JSON-file persistence |
| Consumer | src/tool.ts (dsh-mem/tool) | tool-memory | function plugin registering the four tools; resolves the service via ctx.get('memory') per call |
This mirrors the in-repo ctx.jobs seam (Definition in packages/jobs/jobs, provider in jobs-local, consumer in tool-jobs).
Repository layout
dsh-mem/
├── package.json # declares dsh.bundle.patch → ./cordis.patch.yml
├── cordis.patch.yml # bundle layer: inserts memory + tool-memory rows
├── tsconfig.json # standalone build config (types from npm deps)
├── tsconfig.check.json # local typecheck against a dsh checkout (optional)
├── README.md # this file
├── README.zh.md
└── src/
├── memory.ts # Service Definition (default-exports the service class)
├── provider.ts # Provider (default-exports the service class + static Config)
└── tool.ts # Consumer (named exports only: name/inject/Config/apply)
Build
npm install # pulls the dependencies (runtime and compile-time types both come from npm)
npm run build # tsc emits lib/ (same as the prepare script, run automatically on git installs)
Working beside a dsh checkout, dsh-memory/node_modules/@deepseek-ai/* can be junctioned to the repo packages so local typechecking works without npm install:
pnpm exec tsc -p dsh-memory/tsconfig.check.json # typecheck only (no emit)
pnpm exec tsc -p dsh-memory/tsconfig.json # emit lib/
Smoke tests (build first):
node dsh-memory/tests/patch-smoke.mjs # patch composition over empty and web-like bases
node dsh-memory/tests/provider-smoke.mjs # provider round-trip: persistence/search/bounds/corruption
Memory entries and storage
Each note is an immutable record of 5–6 fields:
| Field | Source | Meaning |
|---|---|---|
id | provider | m-<n>; keeps counting across restarts |
content | model | the durable fact as a complete standalone sentence or short paragraph |
tags | model | keywords for filtering; empty strings are dropped |
project | model | the owning project/workspace (e.g. repository name); absent means a global fact that applies everywhere |
createdAt / updatedAt | provider, stamped automatically | epoch milliseconds; the model never supplies the timestamp, and the tool output includes a human-readable createdAtText (ISO 8601) |
Stored at $DSH_HOME/memory/memory.json (default ~/.dsh/memory/), e.g.:
{
"version": 1,
"nextId": 3,
"entries": [
{
"id": "m-1",
"content": "Project X uses pnpm workspaces and rejects yarn.",
"tags": ["project", "tooling"],
"project": "project-x",
"createdAt": 1753000000000,
"updatedAt": 1753000000000
}
]
}
Design notes
- Why the provider owns a JSON file instead of the
ctx.storageseam: dsh's storage rows (storage/storage-json/storage-domain) are mounted by thedsh-web-appbundle, not bydsh-base; an out-of-tree bundle that inserts the same row ids would duplicate them in web profiles, while omitting them leavesctx.storageabsent in headless ones. One self-managed JSON document keeps this plugin zero-dependency on every profile (web / headless / custom). To swap in actx.storage.domainbackend, subclassMemoryServiceand point thememoryrow at it — the tools and contract stay untouched. That is the point of the seam. - Durability: every mutation writes memory first, then commits via
writeFileAtomic(temp file + atomic rename,0o600/0o700); nothing is visible before it is durable. All operations (reads included) serialize on one in-process queue, so a read never observes an uncommitted write. Concurrent dsh processes sharing onerootare not supported. - Document format:
{ version, nextId, entries };nextIdis persisted so ids stay unique across restarts. A wrong version or a corrupt file fails loud at load — never a silent reset. - Model tool contract: search is a case-insensitive substring match on content plus exact tag match; an empty query returns the newest notes.
recall/listaccept aprojectfilter (case-insensitive exact; global notes never match). Result caps are clamped to the deployment'smaxRecallLimit. Oversized content, illegal limits, and malformed ids are rejected at the tool boundary. When to save and what to save is guided by the tool descriptions: project-specific facts must carryproject; global facts (e.g. user preferences) omit it; timestamps are provider-stamped and cannot be forged by the model. - Dependencies:
@deepseek-ai/cordis@^4.0.1and@deepseek-ai/dsh-*@^0.1.0-rc.6are published on npm;dsh plugin addinstalls them into the profile.
Extending
- Swap the provider: subclass
MemoryService, then point thememoryrow'snameat your class incordis.patch.yml. - Add a human command: inject
ctx.commands(present in base) and register a/memory-style slash command. - Inject memory into a turn: in an
agent/pre-steportools/post-executelistener, callagent.inject()with relevant notes for the next request. - Move the storage root: override the whole
memoryrow'sconfig.rootin the profile'scordis.patch.yml(a patch replaces the whole config, so restate the keys you keep).
Known limitations
- Single-process writer: no cross-process lock when two dsh processes share one
root. - No edit API:
updatedAtequalscreatedAttoday; to overwrite a fact,memory_forgetthenmemory_save. - No structured schema: content is free text; for fielded facts, agree on a fixed text format with the model.
보안 및 설치 증거
이 점수는 공개 저장소 메타데이터와 이 사이트에 등록된 설치 증거에만 기반하며, 코드 보안 감사와 다릅니다.
공개 플러그인 카탈로그에서 왔으며, 공개 GitHub 저장소로 연결됩니다.
GitHub 메타데이터에서 라이선스가 감지되지 않았습니다.
최근 180일 내 코드 업데이트가 있습니다.
재현 가능한 정확한 설치 메타데이터가 아직 등록되지 않았습니다. 저장소 설명에 따라 직접 확인하세요.
검사한 패키지 메타데이터에 설치 라이프사이클 스크립트가 선언되지 않았습니다.
missing-license