メインコンテンツへスキップ
Y

dsh-kymemory

ysqp6-lang/dsh-kymemory

Layered local memory for DeepSeek Harness: a Markdown index auto-injected into the system prompt, a route layer read on demand, and evidence files for detail. Pure files, zero runtime dependencies.

インストール

dsh plugin --profile web add github:ysqp6-lang/dsh-kymemory

README

dsh-kymemory

Layered local memory for DeepSeek Harness: an index that rides along in the system prompt, a route layer read on demand, and evidence files for the details. Plain Markdown, zero runtime dependencies, fully readable and version-controllable.

English | 中文

Why

DSH sessions have no memory by default: every new session starts from zero. Content written into AGENTS.md is injected on every request, but it is static, costs context on every turn, and never accumulates on its own.

This plugin splits memory into three layers and loads them cheapest-first — the same way you read a book by checking the table of contents, then the chapter, then the footnotes:

┌─ memory_summary.md ──────┐  index: what topics exist  ← injected into the system prompt (≤4KB)
│  ## What's in Memory     │
└──────────────────────────┘
              ↓ memory_recall
┌─ MEMORY.md ──────────────┐  route: one fact per bullet ← read per task group
│  ## infra                │
│  - 2026-10-18 Tailscale… │
└──────────────────────────┘
              ↓ memory_search
┌─ summaries/*.md ─────────┐  evidence: commands, output, repro ← full-text search
└──────────────────────────┘

The index is injected by the plugin, so the agent never has to remember to read a file. It is re-read on every prompt assembly, so an edit made in any editor shows up on the next turn without a restart.

Install

dsh plugin --profile web add dsh-kymemory

Restart dsh web. The package ships its own cordis.patch.yml and mounts itself.

Quick start

Install, restart DSH, then open a new session and say:

Remember: this project uses pnpm, not npm.

The agent calls memory_save and the fact is stored. Next time you open a session, just ask:

Which package manager does this project use?

It reads the index the plugin injected into the system prompt, resolves #env, then calls memory_recall env for the details — without you having to remind it to look.

To check whether the memory itself has rotted:

Run a health check on my memory

That calls memory_doctor, reporting duplicate task groups, orphaned evidence files, an over-budget index, and bullets missing their date prefix.

Configure

Add a config block to your profile's cordis.patch.yml (everything is optional; defaults match the ~/.dsh/memory layout):

- id: dsh-kymemory
  name: dsh-kymemory
  config:
    root: '~/.dsh/memory'      # memory root, supports ~
    injectIndex: true          # inject the index into the system prompt
    order: 150                 # section ordering weight
    enqueueOnDispose: true     # record closed sessions into pending.jsonl
    maxIndexBytes: 4096        # hard cap for the injected index (bytes)
    summaryFile: 'memory_summary.md'
    routeFile: 'MEMORY.md'
    evidenceDir: 'summaries'

    # auto-capture (0.2.0)
    autoCapture: 'inbox'       # 'inbox' collects candidates; 'off' disables
    captureFrames: 300         # frames decoded from the tail
    captureMaxChars: 4000      # body cap per candidate
    captureMinMessages: 2      # skip sessions shorter than this
    captureThrottleMs: 60000   # throttle for turn/end incremental capture
    sessionsRoot: '~/.dsh/sessions'

Tools

Five native tools become available to the agent:

ToolPurpose
memory_searchKeyword search across route and evidence layers; returns file, line and matching line
memory_recallFull contents of one task group
memory_saveAppend one dated fact to a task group
memory_groupsList task groups with entry counts
memory_statsSize report: index lines/bytes (with limit check), route stats, evidence count
memory_doctorHealth check: duplicate groups, suspected contradictions, over-budget index, orphaned evidence, missing dates, dead routes, usage report
memory_forgetDelete one memory (by substring or line number, scoped to that group)
memory_archiveMove one memory into archive/, keeping it recoverable
memory_pendingInspect the queue of closed sessions awaiting curation
memory_curateArchive and clear the queue, making room for the next batch
memory_inboxInspect auto-captured candidates; pass file to read one
memory_promoteArchive a promoted candidate into inbox/archive/

Layout

~/.dsh/memory/
├── memory_summary.md    index layer (keep under ~80 lines / 4KB)
├── MEMORY.md            route layer, `## <group>` blocks, dated bullets
├── summaries/           evidence layer, one file per detailed topic
├── archive/             stale content, archived rather than deleted
├── pending.jsonl        sessions queued on close, shared with your curation script
└── usage.json           recall counts and last-used timestamps per task group

Bullet convention: - YYYY-MM-DD content, one line, readable on its own.

Compatibility with the hand-rolled approach

The layout is identical to the "build a directory, then write read rules into AGENTS.md" approach — same data format. Installing the plugin only takes over three jobs:

  1. the index no longer needs a protocol to remind the agent to read it — it is injected;
  2. reads and writes no longer shell out to a script — they are native tools;
  3. session-close archiving no longer needs an external hook config — it is built in.

An existing memory directory can be pointed at directly; no migration required.

How it differs from other memory plugins

  • No vector search: Markdown structure plus keywords. Results are explainable and git diff-able.
  • No background extraction: writes are initiated by the agent in-session, or driven by your own periodic task.
  • Not model-bound: memory is files; any other agent can read them.

Development

node --test test/store.test.mjs test/plugin.test.mjs

Tests cover the store layer and plugin assembly (mock ctx verifies the tool contract, the injected section, and event handling).

License

MIT

関連プラグイン