Passer au contenu principal
Y

harnmem (dsh-harnmem)

ys118/harnmem/packages/dsh-harnmem

Cross-harness agent memory for DSH: one plain-file store shared by the DSH and pi adapters and the harnmem CLI, so a memory written in one harness is available in another; injected as a byte-stable hot index, with five harnmem_* tools, session-end extraction, and a read-only memory browser panel.

Installer

dsh plugin --profile web add github:ys118/harnmem

Ce plugin se trouve dans le sous-répertoire packages/dsh-harnmem du dépôt.

README

HarnMem

Cross-harness agent memory that lives in plain files.

HarnMem = harness + memory (harn is also a Scots word for "brains"). One user, one machine, one memory store — shared by every coding agent you use.

License: MIT Status: v0.3.0 CI npm Runtime deps: 0 Node

HarnMem is a portable memory system for coding agents. It stores memories as plain Markdown files under ~/.harnmem/, retrieves them with a local deterministic engine, and injects a small, byte-stable hot index into each harness session. The same store is readable by every adapter and by the CLI, so a memory written in one harness is available in another — no server, no vector database, no embedding model, and no runtime dependencies in the engine.

  • Packages: @harnmem/core (engine), @harnmem/cli (harnmem command), pi-harnmem (pi extension), dsh-harnmem (DSH plugin) — all published on npm at 0.3.0. Install the CLI with npm install -g @harnmem/cli.
  • License: MIT.
  • Status: v0.3.0 — core, CLI/migration, the pi adapter and the DSH adapter are usable and tested end to end; the Claude Code adapter is designed but not built, and its memory store can already be migrated with harnmem import claude.

Why another memory plugin?

ApproachLimitation for this use case
mem0 / Zep / Letta / OpenVikingRequires a service, vector database or Docker; heavy to install and operate
pi-memoryEffective inside pi, but not shared with other harnesses and no lifecycle governance
dsh-auto-memoryDSH-only; cross-session but not cross-harness
Built-in harness memory (e.g. ZCode)Tied to one harness; store layout not reusable
Just put everything in AGENTS.mdNo retrieval, no governance; grows noisy until models ignore it

HarnMem's bet: the source of truth is the file system, retrieval happens in the local process, and lifecycle decisions are explainable and reversible.

Features

  • Cross-harness by construction. The store and data model are harness-agnostic; adapters are thin. The pi adapter is the first one, and the CLI can read the same store without any harness.
  • File-native SSOT. One memory per Markdown file with YAML frontmatter. Indexes and digests are derived and rebuildable; a corrupt index costs a rebuild, never data.
  • Zero runtime dependencies. @harnmem/core and @harnmem/cli use only Node.js built-ins. No native modules, no downloads, no background daemon.
  • Precise recall without embeddings. CJK-aware tokenization (character bigrams), BM25F field weighting, entity/tag/time filters, two-hop link diffusion, and an explainable score blending lexical relevance with importance, confidence, recency, usage, pins, links and staleness.
  • Memory that ages well. ADD-only writes with bi-temporal supersede keep history; mechanical dedupe/merge keeps the store small; value-based retention moves entries active -> archived -> trashed; trash/ + restore and an append-only state/audit.jsonl keep it auditable.
  • KV-cache-friendly injection. The injected block is rebuilt only on session start, compaction, memory writes and day rollover, and is byte-identical between turns, so prompt caches keep hitting.
  • A vocabulary that grows with your corpus. The shipped synonym tables are generic on purpose; harnmem synonyms suggest reads your own memories, asks a model for interchangeable terms a co-occurrence pass could never find (Chinese jargon usually shares no characters), validates every group against the corpus, and writes reviewed proposals to a local -suggested table. synonyms check reports the rank gain before you keep it.
  • Measured, not asserted. A deterministic evaluation harness covers 12 scenario suites with hard functional gates and advisory performance gates; ablations show which retrieval signals matter.
  • Safe migration. Importers for DSH, pi, ZCode, Claude Code and Markdown trees (plus a relaxed JSON/JSONL/stdin contract) default to dry-run, back up the source, record a ledger, verify source integrity, and support per-run rollback. Not sure what you have? harnmem import auto probes the known stores and prints a plan.

Quick start

Requirements: Node.js 22.15+ and pnpm 10 (corepack enable).

corepack enable
pnpm install
pnpm -r build
pnpm test            # core + CLI test suites (node --test)

pi adapter

# Try it without installing: load the built extension in a one-shot pi session.
pi -e ./packages/pi-harnmem/dist/index.js -p "Remember: I prefer pnpm over npm."

# Install it as a persistent pi extension.
pi install ./packages/pi-harnmem

The extension injects the hot index before each turn, registers the memory tools (memory_write, memory_search, memory_read, memory_forget, memory_status), adds /memory, and runs a bounded extraction at session end. Configuration lives in ~/.harnmem/config.json (or $HARNMEM_HOME/config.json).

DSH adapter

pnpm --filter dsh-harnmem build
dsh plugin --profile desktop add ./packages/dsh-harnmem

The plugin mounts as a cordis plugin: it injects the same hot index through systemPrompt.section (byte-stable, with a synchronous cold-start fallback), registers five tools prefixed harnmem_ (harnmem_write, harnmem_search, harnmem_read, harnmem_forget, harnmem_status; the bare memory_* names belong to dsh-auto-memory, which DSH keeps when both plugins are installed), keeps subagent sessions read-only, and runs a bounded session-end extraction through the host LLM service. Lifecycle and extraction decisions are written to ~/.harnmem/logs/dsh.jsonl (log: "on" is the trial default), and every extraction outcome is emitted as a kind:"extract" event in ~/.harnmem/state/audit.jsonl. It requires DSH 0.2.x and never opens a network connection of its own.

CLI

node packages/cli/dist/main.js --help
node packages/cli/dist/main.js stats
node packages/cli/dist/main.js search "deployment rule" --limit 5
node packages/cli/dist/main.js write --type feedback \
  --title "Never force-push main" \
  --content "Why: shared repo. How to apply: open a PR." --importance 7

# Migrate legacy memories: dry-run by default, then --apply
node packages/cli/dist/main.js import auto          # probe known stores (DSH/pi/ZCode/Claude Code)
node packages/cli/dist/main.js import dsh
node packages/cli/dist/main.js import md --path ./notes --apply   # any Markdown tree
node packages/cli/dist/main.js import json --path export.json      # any JSON/JSONL export

# Grow the local synonym table from your own memories (nothing is auto-applied)
node packages/cli/dist/main.js synonyms list
node packages/cli/dist/main.js synonyms suggest --llm-url http://127.0.0.1:11434/v1 --llm-model qwen3:8b
node packages/cli/dist/main.js synonyms check

Evaluation

node evals/gen/generate.mjs           # generate deterministic corpora
node evals/gen/generate.mjs --check   # prove a second generation is byte-identical
pnpm eval                             # all suites
pnpm eval:gate                        # hard gates (perf gates are advisory)

Architecture

flowchart LR
  subgraph Harnesses
    PI[pi extension]
    DSH[DSH plugin]
    CC[Claude Code adapter - design]
  end
  CLI[harnmem CLI + migration]
  CORE["@harnmem/core<br/>store · retrieval · writing · lifecycle · extraction"]
  PI --> CORE
  DSH --> CORE
  CC -.-> CORE
  CLI --> CORE
  CORE --> STD[(~/.harnmem plain files)]
  CORE --> IDX[(rebuildable BM25F index)]

A session looks like this:

  1. Session start / resume: the adapter calls ensureContext(); core returns a hot index (pinned entries, high-value entries, recent entries, digest lines) within a token budget. The adapter injects it into the system prompt once and keeps it byte-stable until it is invalidated.
  2. During the session: the model uses the memory tools (or the CLI) to write, search, read, forget and inspect memories. Writes are deduplicated and scoped; conflicting facts supersede old ones instead of overwriting history.
  3. Session end: the adapter sends a transcript digest to one bounded LLM call, then writes the extracted memories (deduped against the existing store, with a confidence threshold and an idempotent cursor).
  4. Between sessions: consolidation merges near-duplicates, retires stale entries by value/age, and (optionally) writes topic digests.

What a memory looks like

---
schema: 1
id: mem_01J8Z6Q0K4T9B2M7R5X3C8V1ND
type: feedback
category: workflow
scope: project
project: my-app-4f2a91c3d0
title: Never force-push main
description: Shared repository requires PRs; force-push rewrites others' history.
importance: 7
confidence: 0.9
pinned: false
status: active
created: 1760918400000
updated: 1760918400000
tags: [git, workflow]
links: []
supersedes: []
---

**Why:** `main` is shared; rewriting history disrupts other contributors.
**How to apply:** push feature branches and open a pull request.

Types are user, feedback, project and reference. Records are append-only: a corrected fact creates a new record and marks the old one status: superseded with valid_to set, so "what did I believe last month?" is still answerable.

Data layout

Default ~/.harnmem/ (Windows: C:\Users\<you>\.harnmem\):

~/.harnmem/
├── config.json          # optional user config (budgets, weights, governance)
├── VERSION              # store layout version
├── memories/            # SSOT: one .md file per memory
│   ├── user/            # global, cross-project scope
│   └── projects/<projectId>/
├── digests/             # optional topic digests produced by consolidation
├── prompts/             # prompt assets deployed on first run (editable)
├── data/                # synonym tables, one file per language, deployed on first run (editable)
├── projects.json        # project identity registry (git remote/path -> id)
├── index/               # derived indexes; safe to delete, rebuilt on demand
├── sessions/            # raw session journals used as extraction input
├── state/               # sequence numbers, audit log, cursors, usage overlay
├── trash/               # soft-deleted entries (30-day grace, restorable)
└── logs/                # adapter logs, e.g. logs/pi.jsonl

The schema and migration mapping are specified in docs/design/02-data-model.md.

Retrieval and ranking

Retrieval is deliberately local and explainable:

  • tokenizer: English words + Chinese character bigrams (queries avoid boundary-unigram noise);
  • BM25F over title/description/tags/entities/body with per-field weights;
  • a scored blend: lexical relevance, importance, confidence, recency (0.995^hours), usage, pin, two-hop link diffusion, and a staleness penalty;
  • MMR diversification and a token budget for injection;
  • abstention when the best lexical evidence is too weak (prevents injecting irrelevant context).

The formula and its calibration are documented in docs/design/03-retrieval.md.

Evaluation results

The harness is deterministic (no LLM, no network) and runs in about a minute; the numbers below come from the tracked eval-report.md:

MetricResult
Suites / queries / documents12 (S1-S12) / 565 / 27,510
Macro Recall@101.000
MRR0.891
nDCG@100.911
Cross-language (zh <-> en) recall gap0.000
Functional gatesall passing
Advisory performance items10k import ~12 s; warm search p95 ~16 ms; index ~2.9x canonical store

The advisory items are recorded rather than hidden: import is a one-time migration cost, and the index-size/latency targets for very large stores are on the roadmap. Details, per-suite numbers, ablations and gate definitions are in docs/design/05-evaluation.md and evals/README.md.

Harness support

HarnessStatusNotes
pi 1.3.0SupportedExtension with injection, tools, /memory, session-end extraction; verified end to end
DeepSeek Harness 0.2.1Supported (0.2.x)Cordis plugin (dsh-harnmem); verified against the desktop runtime with a disposable profile and a local mock endpoint
CLI (any harness)Supportedharnmem commands read/write the same store
Claude CodeDesign (migration available)docs/design/09-claude-adapter.md (MCP stdio server + hooks); harnmem import claude migrates its existing memory store today
Other harnessesWelcomeSee the adapter contract and CONTRIBUTING.md

Documentation

Start with the documentation index. Highlights:

Roadmap

See docs/ROADMAP.md for the current status (verified numbers, the running trial, known issues) and the ordered next steps.

  • Implement the Claude Code adapter (MCP stdio + hooks).
  • Persist and incrementally update indexes so large stores warm up faster; reduce index size.
  • Validate npm installs on Linux/macOS/Windows and publish provenance for every release.
  • Harden concurrent writes across harnesses sharing one home.
  • Add an optional, opt-in reranker or LLM selector for ambiguous recalls (never required for basic use).

Security and privacy

HarnMem is local-first: no telemetry, no background service, and the engine never opens a network connection. The CLI is offline by default too; the only outbound call is the opt-in harnmem synonyms suggest --llm-url bridge, which runs solely when you pass an endpoint. Memory content is sensitive by nature — back up ~/.harnmem/, keep it out of version control, and read SECURITY.md before reporting a vulnerability. The store uses symlink/path containment checks, size caps, sensitive-value redaction, soft delete and an audit log; see the design docs for details.

Contributing

Contributions are welcome. Start with CONTRIBUTING.md, follow the Code of Conduct, and run pnpm test and pnpm eval:gate before opening a pull request. Note the contribution gate: issues and pull requests from new contributors are auto-closed by default and reopened once a maintainer approves them with lgtmi (issues) or lgtm (issues and pull requests). Merging master is PR-only and requires the owner's approval. Questions and ideas are welcome in Discussions. Security issues go through the private process in SECURITY.md.

License

MIT © HarnMem contributors

Plugins associés