Long-term memory for AI agents — a seven-layer cognitive memory database in a single embedded file. Pure Go, zero infrastructure.
中文 · Website · MeowAgent (coming soon)
Current: v1.6.0 · Latest stable tag: v1.6.0
MemHop is an embedded long-term memory database for AI agents and LLM applications, written in pure Go. It is not a vector database — it is a memory system modeled after how the human brain organizes knowledge, with identity, episodic recall, semantic compression, a knowledge graph, archival storage, and crystallized skills. One agent, one .meh file, zero infrastructure.
MemHop is an agent-dedicated memory database: each agent binds to exactly one .meh file, and a file-level exclusive lock guarantees a single instance per file (a second Open fails fast). It runs on Linux, macOS, and Windows with no cgo and no external service beyond your LLM endpoint.
Built as the brain memory of MeowAgent (coming soon), MemHop works as an embedded organ rather than a standalone service. No server to run, no configuration to manage — just open a file and your agent has memory.
Our stance on agent memory. Memory should not be an afterthought bolted on with a vector database plugin or a plain-text log dumped into a context window. An agent without internalised memory is just a stateless function pretending to be intelligent. MemHop exists because we believe memory must be cognitive — structured, compressed, consolidated, and forgotten the way a human brain does — and embedded — living inside the agent process itself, not behind a network call. One file, zero infrastructure, a mind that grows with every conversation.
- Seven-Layer Architecture — L0 Profile → L1 Engram → L2 Context → L3 Knowledge → L4 Archive → L5 Crystal → L6 Trajectory, with Dream consolidation
- Scene-is-the-session memory loop — one L2 scene = one host session.
Searchreads that scene's depth-1 topic set straight from the in-memory cache (zero LLM, zero embedding, no scoring) and opens the turn: it hands back the topic id the turn will live in.Updatesettles the whole finished turn into that id (user text + agent text + both timestamps → one distillation into the topic's keywords), and L6 trajectory events bind to the same id. The scene'sFusedKeywordsset is the context a host injects - V2 Storage —
.mehformat (FormatVersion=0x0009) with A/B dual headers, per-record CRC32 + torn-write truncation recovery, mmap zero-copy, snapshot/checkpoint. Record frames carry an 8-byteagent_id(26-byte header) and the engine indexes every record by(agent, idHash)domain. Not compatible with0x0008(or older).mehdata files — they are rejected at Open with no migration path - Multi-Agent Domains —
OpenMulti+CreateAgent(name)/Session(agentID)/ListAgents/DeleteAgent: many agents share one.mehfile with fully isolated per-agent domains (caches, Dream pipelines, domain locks); same-agent operations serialize, different agents run in parallel; idle domains reclaim memory on access cadence (Defaults.AgentIdleTTLMs) while their records stay on disk. Multi-agent is the only mode — every operation runs through a per-domain session - L1 Scene Hypergraph — Dream creates co-occurrence hyperedges between scenes whose keyword sets overlap (Jaccard ≥
L1EdgeMinSimilarity) and decays/prunes them over time; query-time spreading activation retired with the scoring subsystem, so L1 is maintained by Dream for explicit graph queries and future association - Dream Pipeline — consolidation over L0–L2 plus L6 retention pruning: L2 compress → index rebuild → L1 nodes/hyperedges rebuild → L1 decay → L0 distill (emotion/MBTI) → L6 prune (drops trajectory events older than 7 days); returns a per-stage
DreamReport - L3 Knowledge Graph — multiple independent hypergraphs with node import carrying positional source refs and relation edges (an edge is its members plus its kind, so one node pair can hold several relations), graph and node-level deletion, keyword/type/id lookup that ANDs together, and BFS subgraph queries
- Single Instance by Design — one
.mehfile has exactly one owner: a cross-platform exclusive lock (linux/darwin/windows) makes a second open fail fast, and the embedded path runs with no server process and no background daemon - Minimal & Embeddable — 4 direct Go deps (xxhash, go-openai, go-sdk, golang.org/x/sys) — the engine contacts no embedding / vector service at all, and there is no dimension to declare in the config;
sync.RWMutex+atomic.Pointer, zero infrastructure - MCP Server —
cmd/memhop-mcpexposes 27 of the 34 public session methods as MCP tools over multi-tenant HTTP (SSE + streamable-http, officialmodelcontextprotocol/go-sdk): one process serves many hosts through one shared.mehfile, each tenant isolated by URL path/mcp/<tenant-id>into its own agent domain (stable agentID per tenant name,os.Root-anchored db-dir, andmemhop_capability_importpaths anchored to--capability-dir, defaulting to the db-dir). Go-only by design: the L6 plan write/read surface (PlanCommit/PlanState/PlanReplace/SyncPlanTree), memory correction (DeleteTopic/DeleteScene/DeleteL3Nodes) and file maintenance (CompactTo, whose argument is an output path) — those need a host that owns session state or chooses where a file is written
Full integration guide (config, all layer APIs, N:N turns, pitfalls): INTEGRATION_GUIDE.md · 中文: INTEGRATION_GUIDE.zh.md
import (
"context"
"log"
"os"
"time"
memhop "github.com/qyiun666/MemHop/api"
)
dbm, err := memhop.OpenMulti(&memhop.MemHopConfig{
DBPath: "agent.meh", // the whole database; no server, no dimension to declare
LLM: memhop.LlmConfig{ // required, validated at Open (powers Update's distillation)
APIURL: "https://api.openai.com/v1",
APIKey: os.Getenv("OPENAI_API_KEY"),
Model: "gpt-4o-mini",
},
Defaults: *memhop.DefaultMemHopDefaults,
})
if err != nil {
log.Fatal(err)
}
defer dbm.Close()
// One .meh file carries isolated domains. CreateAgent returns a stable
// 16-char hex ID; Session binds every call to that domain.
agentID, err := dbm.CreateAgent("my-agent")
if err != nil {
log.Fatal(err)
}
sess, err := dbm.Session(agentID)
if err != nil {
log.Fatal(err)
}
// Read memory = read one scene (a scene IS a host session), which also
// opens the turn about to run. Empty SceneID → the library creates a scene
// and returns its id (L3ID optionally anchors it to a project domain); a
// non-empty SceneID must already exist, otherwise ErrNotFound. The read costs
// no LLM, no embedding and no scoring; NewTopicID is the topic this turn
// lives in.
res, err := sess.Search(memhop.SearchQuery{})
if err != nil {
log.Fatal(err)
}
sceneID := res.Scene.SceneID
for _, topic := range res.Topics { // this session's depth-1 set = the context
_ = topic.FusedKeywords
}
// End of turn: settle the whole exchange into the topic Search opened. Both
// originals become L4 archives and one distillation produces the turn
// topic's keywords. Replaying the same TopicID rewrites that turn instead of
// duplicating it, so a timed-out Update is safe to retry.
topicID, err := sess.Update(memhop.TurnUpdate{
SceneID: sceneID,
TopicID: res.NewTopicID,
UserText: "What did we discuss yesterday?",
UserTS: time.Now().UnixMilli(),
AgentText: "Agent: ...",
AgentTS: time.Now().UnixMilli(),
})
if err != nil {
log.Fatal(err)
}
// While the turn runs, its trajectory events bind to that same topic id.
_ = sess.AppendTrajectory(topicID, "", memhop.TrajectorySlot{
EventType: "tool_call",
Payload: `{"tool":"grep"}`,
Timestamp: time.Now().UnixMilli(),
})
// Dream consolidation (L0-L2); an empty sceneID sweeps every scene of the
// domain. Update already schedules it in the background once a scene's
// topic count passes the threshold, so hosts rarely call it.
report, err := sess.Dream(context.Background(), "")Concurrency contract. Same-agent operations (Search / Update / Dream / write APIs) are serialized by the library's per-agent domain lock; different agents run in parallel on a
*MultiAgentDB, so the host needs no external queue.*memhop.Sessioncarries no cross-domain state beyond its bound id. The file's exclusive lock still allows only one process per.mehfile;MultiAgentDBexposes no locking API (removed in v1.5.0): the domain lock is the library's, and a host critical section needs its own.
Prerequisites: Go 1.27+ and an OpenAI-compatible LLM endpoint (Config.LLM is required) — no embedding / vector service needed
| Group | Methods |
|---|---|
| Core loop | Search(q) · Update(TurnUpdate) → topicID · Dream(ctx, sceneID) |
| L0 Profile | GetL0 · UpdateL0 |
| L2 Context | ListScenes([l3ID]) · UpdateScene(id, {Name, L3ID, Force}) · SceneContext · MergeScenes · DeleteTopic · DeleteScene |
| L3 Knowledge | GetL3 · ListL3 · ImportL3 (returns the graph ids it wrote) · UpdateL3 · DeleteL3 · DeleteL3Nodes (Go only) · QueryL3Nodes · QueryL3Subgraph |
| L4 Archive | SearchL4(q) — one read surface; keyword (case-insensitive), time range, ids, topic and content type are conditions, not modes; Limit keeps the newest matches |
| L5 Capability | ImportCapability · UpdateCapability · DeleteCapability · ListCapabilities([IDs]) · ActivateCapability · RecordCapabilityUsage |
| L6 Trajectory | AppendTrajectory(key, [nodePath]) · ReadTrajectory(key) · ListTrajectorySessions · Crystallize(key) — a turn's events key on its topic id (7-day auto-retention, no delete API) |
| L6 Plan tree | PlanCommit · PlanState · PlanReplace · SyncPlanTree, ids from api.NewPlanID(name) (Go API only, not in the MCP tool set) |
| DB handle | OpenMulti · CreateAgent · ListAgents · DeleteAgent · Session(id) · Checkpoint · CompactTo(newPath) (defragmented copy; Go only) · Close · IsClosed · api.DefaultAgentID |
The root capabilities/ directory ships six built-in capability cards (memhop-capability/v3, embedded into the library at build time, English): memhop-guide (the loop split — Search/Update/Dream and trajectory recording run host-side and must never be manual LLM calls — plus an index of the other five) and five LLM-callable manuals (knowledge, scene, archive, profile, capability). Cards document the Go API (type: "api", ref: "api:MethodName") — the host calls the methods directly, no MCP layer involved. Resources are tool declarations: name/desc/input/output mirror the host tool spec (e.g. meowire ToolSpec) field-for-field, so a host projects them with a pure field copy and zero format conversion. Tiered injection: ListCapabilities serves the toolbox read-only (same filters as stored records, never persisted to the .meh file, deduped by ID against stored same-name records — stored wins, NOT attached to Search responses); inject only a one-line-per-card index (id + name + summary + trigger) plus the guide, and fetch full parameter schemas on demand via ListCapabilities(CapabilityListQuery{IDs: []string{id}}).
Layer Name Human Parallel Mechanism
───── ────────────── ─────────────────── ─────────────────────────────────────────────
L6 Trajectory Procedural log Host-appended operation events; crystallized into L5 capability drafts
L5 Crystal Muscle memory Reusable capability packages (skills · MCP · tools · prompts · services)
L4 Archive Long-term memory Raw dialogue logs & historical records
L3 Knowledge Semantic memory Multi-source hypergraph knowledge base
L2 Context Working memory Compressed topic structures (4 depth levels)
L1 Engram Scene hypergraph Scene nodes + keyword-overlap hyperedges; maintained by Dream for explicit graph queries
L0 Profile Identity Agent personality, preferences & language habits
The Dream cycle is an automatic consolidation pass inspired by how sleep processes the day's experiences. It acts on L0–L2 only (L3 distillation and L5 crystallization are out of scope) plus L6 retention pruning:
- L2 compression — the LLM groups related topics per scene; each target scene runs in its own goroutine, sinking merged topics under a new depth-1 fused node
- L1 rebuild — scene nodes are synced from L2, the L2Meta topic cache is rebuilt in the same scan, and keyword-overlap hyperedges are created or refreshed
- L1 decay — scene importance and edge weights decay over time, weak nodes are pruned
- L0 profile — the agent profile is rebuilt from consolidated memory
- L0 distill — emotion/MBTI signals are distilled (always runs; skipped when the L1 sample set is empty)
Trigger: once a scene's depth-1 topic count passes Defaults.SceneDreamTopicThreshold (default 24), Update schedules that scene's Dream in the background; hosts may also call it. Dream(ctx, sceneID) (*DreamReport, error) holds the domain lock for the whole cycle, sweeps every scene of the domain when sceneID is empty (scenes below DreamCompressMinTopics are skipped) and honours ctx cancellation between stages.
There is no scored retrieval. A scene is a host session, so the engine never guesses which scene a message belongs to:
| Path | What it does | Cost |
|---|---|---|
Search(SearchQuery{SceneID, L3ID}) |
empty SceneID → create a scene (named by the library) and return its id; otherwise → the scene's depth-1 topics (user-timestamp order) plus the L0 profile — and NewTopicID, the topic this read opens for the coming turn |
in-memory read (L2Meta), zero LLM / embedding / scoring; the only write is the scene record (hit counters + turn counter) |
Update(TurnUpdate{SceneID, TopicID, ...}) |
settles one finished turn into the topic Search opened: two L4 archives plus a topic whose keywords come from a single distillation | exactly one LLM call per turn; distillation runs before any write, so a failure leaves no trace. Same TopicID = rewrite, never duplicate |
What a host injects as context is the keyword set of that scene's depth-1 topics; to read a turn's original text, follow the topic's L4Refs through SearchL4, or use SceneContext. Dream keeps the injected size bounded (Consolidate requires at most 20 topics per scene after compression).
Removed along with retrieval: three-channel RRF scoring, L1 spreading activation (AssociatedContexts), topic centroids and the embedding dependency, the AutoCreate / DirectedL2ID / DirectedL3ID routes, and topic-level L3Refs (L2↔L3 now lives solely on the scene anchor SceneSlot.L3ID).
MemHop's test suite exercises only the public api surface — exactly the calls a host (e.g. MeowAgent) makes — and asserts the engine's own memory structures, not external answerability judges.
- Memory loop (
TestCoreCycleUpdateDream): N turns settled into one scene the way a real host does, with periodic L0/L2/L4 consistency checks every few turns — L0 profile readable, the scene read non-empty, L4 holding the raw utterance verbatim. After Dream consolidation the scene surface must shrink while every fact stays recoverable from L4. - Keyword fidelity & persistence (
TestKeywordFidelity/TestKeywordPersistence/TestDreamCompressionFidelity): the keywords distilled from a turn faithfully carry its meaning, survive noise turns, and stay faithful across Dream compression. - API contracts (
TestInterface*: reads make zero LLM calls, writes cost exactly one distillation per turn, unknown scenes are rejected, checkpoints survive a restart), e2e flows (TestE2E*), long-input robustness (TestExtractKeywordsLongInputRealLLM/TestUpdateLongTurnNeverFails).
All benchmarks drive the real api loop (real LLM, no external judge):
| Benchmark | Measures |
|---|---|
BenchmarkMemoryLoop |
steady-state Search+Update loop including the engine's auto-scheduled Dream (a scene's depth-1 topic count passing the threshold) and periodic L0/L2 verification |
BenchmarkUpdateTurn |
one-turn settle latency (one distillation + topic + two L4 writes) |
BenchmarkSceneRead / BenchmarkSceneReadLatency |
scene-read throughput and latency distribution (min/p50/p95/max) |
BenchmarkAppendL4 |
pure storage append latency (no LLM) |
BenchmarkDreamConsolidation |
full Dream pipeline latency |
Public memory benchmarks (LoCoMo, LongMemEval) evaluate "retrieval → LLM-judged answerability" — a different question than what MemHop's layered design asserts (L0 profile distillation, L1 scene-graph coherence, L2 compression semantics, L4 verbatim archival). LongMemEval, the closest fit (multi-session user-assistant chats, ~500 QA), needs 115K–1.5M tokens per question and is not a practical continuous-integration target. MemHop therefore verifies its memory structures directly through the api loop instead of chasing a generic QA score.
api/ ← Public facade: openmulti (entry + tenant management) / session (the only
business handle, hex-id surface) / types / mapping / ids / errors / exports
internal/ ← Business assembly: config / db / session / defaults / tuning /
l0 / l2 / l3 / l3query / l4 / l5 / l6 / l6_plan / agents / agentctx /
search / update / dream / plancache / llm_client / llm_ops / models / exports
internal/repo/ ← Data layer: l0layer–l6layer + agentlayer (record read/write)
internal/repo/index/ ← Index layer: l2meta / rebuild (single-pass scan) /
traj (the L6 turn shape)
internal/repo/core/ ← .meh engine: engine / frame / header / snapshot / reclaim /
record / model / mmap / filelock
internal/common/ ← Bottom-layer utilities: enum / errors / hash /
sliceutil / strutil / timeutil
test/ ← Integration tests (build tag: integration)
benches/fixtures/ ← Benchmark datasets (locomo10, locomo_smoke, longmemeval_smoke)
Dependency direction is strictly one-way: api → internal → repo → core, with common at the bottom (no references to any other internal package).
Note:
docs/andAGENTS.mdare intentionally kept local-only (see.gitignore), so links underdocs/may not resolve in a public clone.
- Read path (
Search): zero LLM, zero embedding — served from the L2Meta cache alone. - Write path (
Update): exactly one keyword distillation per turn (both originals fed together), 512-token output cap escalating on truncation, heuristic tokenisation on parse failure. - Dream: one consolidation call per scene reaching the topic floor (
DreamCompressMinTopics, default 20), plus one distill call with at most 200 ranked L1 samples (up to 20 keywords each). Output caps: 8192 / 2048 tokens. - Crystallize: one explicit, host-triggered call per turn trajectory; turns sharing an L2 topic fold into one prompt (capped at 128KB of payload, oldest dropped).
- Use a small/fast chat model (a cheap API model or a local OpenAI-compatible endpoint) for the configured LLM when latency and cost matter; keyword distillation does not need a frontier model.
go build ./... # Build
go vet ./... # Static analysis
go test ./internal/... # Unit tests (no external services)
go test -tags integration ./test/... # Integration tests (requires an LLM key)Integration tests run against a real LLM (the engine needs no embedding service). Configure the LLM via environment variables MEMHOP_TEST_LLM_KEY / MEMHOP_TEST_LLM_URL / MEMHOP_TEST_LLM_MODEL (defaults to the DeepSeek endpoint when only the key is set), or via test/testsupport/key_config.json.
| Version | Date | Highlight | Core Changes |
|---|---|---|---|
| v1.6.0 | 2026-09-04 | No-fallback interfaces; the finished v1.5.0 surface | The v1.5.0 tag was cut before its API surface was complete — hosts requiring it hit undefined: api.DefaultAgentID / api.NewPlanID / api.ScenePatch. This release ships the finished line. 1. Library-minted ids: api.DefaultAgentID names the implicit domain and api.NewPlanID(name) mints plan ids; the four api/ids.go Format/Parse bridges are gone2. Merged surface: SetSceneName+SetSceneL3ID → UpdateScene(id, ScenePatch), PlanAppend → AppendTrajectory(key, nodePath, ev), ListScenesByL3 → ListScenes(l3ID); Lock/Unlock/Session.Checkpoint/IsClosed/AgentID/DistillL0/ListPlans/GetArchive/GetCapability deleted (34 + 8 methods)3. Interfaces refuse instead of falling back: unparseable LLM keyword output is ErrLLM and the turn writes nothing (gse/tokenizer fallback deleted, direct dependencies 5 → 4); a rejected PlanCommit leaves the tree and events untouched; over-budget trajectory payloads are rejected, not truncated; DeleteCapability/DeleteAgent/MergeScenes report unknown ids instead of silent no-ops; reads surface transient errors instead of skipping records4. Graph import closed loop: skipped re-imports rebuild their edges, existing graph slots are reused so renames survive re-import, batches validate before writing, L3Relation.Titles declares true N-ary hyperedges, UpdateL3 name collisions are rejected with deterministic re-export routing, deleting a graph detaches scene anchors, SyncPlanTree branch deletion mirrors into the trajectory index5. Format stays 0x0009, MCP tool set unchanged (31); every fix re-verified by importing this repo into its own L3 hypergraph — full record in CHANGELOG |
| v1.5.0 | 2026-09-01 | L2 re-shape: a scene IS a host session, and the library owns the turn id | 1. Search reads the scene AND opens the turn: input {scene_id, l3_id}, both optional — an empty scene_id mints a scene (library-named session:<id>; the scene_name input is gone — the host titles a scene with UpdateScene), a non-empty unknown one is ErrNotFound, and l3_id only anchors a newly created scene. The result is {profile, profile_brief, scene, topics, new_topic_id}: the scene record, its depth-1 topics in turn order (the context to inject) and the topic id this read minted for the turn about to run, hash("turn:" + scene:turn_seq) from a new per-scene turn_seq counter. contexts/associated_contexts/auto_create/directed_l2_id/directed_l3_id, the ctx parameter and every read-path LLM/embedding/scoring call are gone. That counter write is the read's only write and is now load-bearing: a failed write fails the read instead of reissuing a possibly duplicate id2. Update settles the whole turn into that id: TurnUpdate{scene_id, topic_id, user_text, user_ts, user_type, agent_text, agent_ts, agent_type} returns the same id; one distillation runs before any write, so a failed LLM call leaves nothing behind; a missing / zero / non-hex topic_id is ErrInvalidQuery; replaying one id overwrites the turn and tombstones the two archives it supersedes. Both timestamps are now ordering fields only, not identity inputs3. N:N append surface deleted: AppendL4Message (many messages into one topic) and RefineTopicKeywords (re-distill one topic from all originals) are gone — a turn's L4 originals are exactly its two texts, and what happens between them belongs to the turn's L6 trajectory. L4 content types are declared on the way in — user_type/agent_type (zero value = text, a non-text slot carries its media path or URL as the text) — and SceneMessage.type reports them back4. L6 trajectory keyed by the turn's topic id: AppendTrajectory / ReadTrajectory / Crystallize take that id and the event's topic_id is stamped from the key, so host-minted turn keys disappear; plan-bound events keep using the plan id, and the cross-turn fold (with TrajIndex.TopicEvents) is deleted — a plan id is now the cross-turn aggregation unit5. Single keyword track: user_keywords/agent_keywords/centroid_page_ref/l3_refs removed, only fused_keywords remains (same on-disk field name); Dream compression, L1 hyperedges and host injection all read that one track, no summary field — originals stay in L46. Scene ids are minted by the library: NewSceneSlot(sceneID, name) no longer hashes the name; CreateSceneL2WithID reuses an existing scene idempotently; the timestamp:text auto-naming path disappears7. Retrieval subsystem deleted: internal/cap/scenefind (three channels, RRF, scene bonuses, L1 spreading activation), topic centroids, RecVecCentroid, Encoder/HttpEncoder/OpenMultiWithEncoder and the encoder config — the engine contacts no embedding service8. VectorDim deleted from the config surface (CheckVectorDim/ErrVectorDimMismatch/MCP --vector-dim with it); the two header bytes at offset 6 are reserved, format stays 0x00099. Dead index island deleted: the orphaned BM25 / entity / BK-tree / L3-index code (L3 node search always scanned records), two Levenshtein implementations and zero-caller common.FormatIDs10. Consolidation trigger re-axled: activeScenes/Capacity plus ActiveSceneIDs/HasActiveScenes removed; Update schedules a scene's Dream once its depth-1 count passes SceneDreamTopicThreshold (default 24); Dream(ctx, "") sweeps every scene of the domain11. No format bump: TopicSlot.UnmarshalJSON folds an old file's two tracks into fused_keywords at decode time, and turn_seq is additive (scenes written before this release decode to 0 and open turn 1 on the first read). Turn topics derive their id under a "turn:" namespace disjoint from Dream's fused-node ids; the unused ComputeTopicIDForText and the dead SceneNode.VectorPageRef are removed12. Scene merging no longer happens inside Dream (it would delete a sceneID the host still holds); MergeScenes stays an explicit host API. Delivery: MCP memhop_search loses scene_name and returns new_topic_id, memhop_update requires topic_id, the trajectory tools key on that id, memhop_status reports scene_count, memhop_scene_active_list is deleted while memhop_scene_rename is added (30 → 31 tools); L0 profile field ownership is enforced by the library — UpdateL0 writes only the host's four fields, inherits the Dream-evolved emotion state and MBTI from the stored profile and stamps updated_at_ms itself, so memhop_profile_update is a plain forward and Go hosts get the same guarantee; SyncPlanTree inherits a blank Title/PlanType/Status/Summary instead of rewinding the node, a failed Dream merge group rolls back what it wrote, and a transient read failure is never reported as "record not found"; the DSH plugin surface (dsh/, dsh-adapter/) is retired in this same release, so the delivery is the library plus the MCP server13. Public surface consolidated to what a host actually reaches (audited against api/ alone — the MCP tool set is not a reason to keep a method): Session 43 → 33 methods, MultiAgentDB 9 → 7; the post-release interface audit below added DeleteL3Nodes and CompactTo, so the surface is 34 + 8. Deleted with their implementation chains: Lock/Unlock (the default domain's mutex exposed to hosts), Session.Checkpoint/IsClosed/AgentID (duplicates of the DB handle), DistillL0 (a Dream stage, not an entry point), ListPlans + plan.Summarize + PlanCache.All (recovery is PlanState on a known id), GetArchive and GetCapability (both are one condition of the list query), MergeScenes' dead repo overwrite twin OverwriteSceneL3ID, and the un-consumed api.CapabilityImport alias. Merged: ListScenesByL3 → ListScenes(l3ID), SetSceneName+SetSceneL3ID → UpdateScene(id, ScenePatch{Name, L3ID, Force}) (one read-modify-write), PlanAppend → AppendTrajectory(key, nodePath, ev) (empty nodePath = the bare turn event). The library issues every id: the four Format*/Parse* bridges are gone — api.DefaultAgentID names the implicit domain and api.NewPlanID(name) mints a plan id under a plan: namespace, so a host echoes ids and never builds one; internal.FormatAgentID/ParseAgentID collapsed into the one FormatID/ParseID pair. A fourth was caught by a new reflection guard: UpdateScene had been merged without a facade override, so embedding leaked core.SceneSlot to hosts with uint64 ids — the facade now maps it and hands back the written scene (confirming an anchor no longer costs a ListScenes sweep), and api/surface_public_test.go rejects any host-visible uint64 id field. api.NewPlanID borrows away from the reserved 0, so every id the library issues is one it accepts. Three silent failures fixed: SearchL4 with only TopicID or only Type returned nothing (the L4 selector is now a set of AND-ed conditions, so one call pulls a turn's originals); re-anchoring an anchored scene answered nil while changing nothing (now ErrInvalidQuery unless Force, and the target domain must exist — also enforced when Search anchors a new scene); Dream on an unknown scene id returned a zero report with no error (now ErrNotFound)14. Post-release per-layer interface audit (every finding re-checked against source, plus the public surface imported into this repo's own L3 hypergraph and queried): two core-layer defects that turned a wrong id into silent data loss are fixed — typed record reads now check the frame's record type ( GetL3(nodeID) and friends answer ErrNotFound instead of decoding a foreign slot, which is what let UpdateL3 rename a node record into a graph slot), and a hyperedge's identity includes its kind (an edge pair carrying both related and part_of used to collapse to whichever kind was written last; re-imports dedupe on sorted members + kind, so edges already stored under the pair-only hash are not duplicated). The public surface got more honest, in the host's favour: the twelve contract-heavy methods that reached hosts by embedding alone are now declared with doc comments on the facade (go doc api.Session showed 22 of 34 before), ImportL3 reports graph_ids for the graphs it wrote (a graph id is hash(Domain) and no other public call renders that derivation), and two new entries exist: DeleteL3Nodes (node-level deletion, cascading its hyperedges) and MultiAgentDB.CompactTo (the way back from tombstone-only deletes). Semantics tightened or made consistent: Update may only settle a turn this scene opened (writing a Dream-fused topic, another scene's turn, or a host-invented id is refused, while replays and out-of-order settles stay valid); QueryL3Nodes conditions AND together instead of a priority switch that silently ignored two of them; SearchL4's keyword is case-insensitive like the L3 filter and gains Limit (newest matches), with the MCP tool capped at 50 by default; SceneContext's depth ≤ 2 flattening is documented as deliberate — it is the only read that brings back the originals Dream sank. Dead public fields removed: HypergraphNode.importance, HypergraphEdge.weight/label, ArchiveSlot.metadata and the RoleSystem constant (no write path anywhere in the engine; the record fields stay so older files decode), and plan_type is now cleared on event writes since the record contract makes it node-only. Security and delivery: memhop_capability_import paths are anchored through os.Root on --capability-dir (default --db-dir) and escapes are refused; memhop_dream's scene_id is optional so MCP hosts can reach domain-wide consolidation at all; the import tool description and the ListL3 card each claimed something the code does not return and now tell the truth; each card's summary maps its Go method names to the memhop_* tools an MCP client actually calls; and the ~70-line idsToHex rewrite in the MCP server, a no-op since the api DTOs render every id as hex (four of its fifteen keys named fields no public DTO has), is deleted. New drift guards tie hand-written vocabularies to the engine enums they enumerate. Format version stays 0x0009: this round changes derivation and validation, not the record layout. |
| v1.4.2 | 2026-08-31 | L6 plan tree + L2 directory anchor | 1. L6 carries a task tree: TrajectorySlot.NodeType splits turn events from plan nodes, node ids derived stably by HashPlanNode(planID, nodePath) under a plan: namespace, events bound via PlanNodeRef2. three-form surface PlanAppend / PlanCommit / PlanState plus PlanReplace (re-plan, keeps planID), SyncPlanTree (whole-tree snapshot diff, emits no plan_step), ListPlans (restart recovery)3. Model A fold: a parent turns done only on an explicit host commit; after each commit the done children's summaries roll up bottom-up in numeric NodePath order without clobbering a host-written parent summary4. PlanTree.Roots is a forest (one root per top-level step; orphaned nodes surface as roots instead of vanishing)5. L2 scene → L3 directory anchor (N:1): SceneSlot.L3ID, optional SearchQuery.L3ID pre-filter with backfill on hit, ListScenesByL3, SetSceneL3ID(sceneID, l3ID, force) write-once unless correcting or clearing6. per-domain planCache so PlanState/ListPlans/rollup stop scanning the engine per call7. api constants exported: Role*, NodeType*, numeric Status* (read-side), string PlanStatus* + PlanStatus type (write/query side); fifth status running added8. write surface forced authoritative: all plan-node fields and Seq are overwritten on every append path, EventType restricted for plan events9. hardening: 0000000000000000 is the reserved bare-event PlanID and rejected by all five plan entry points (PlanReplace on it used to delete every turn event of the domain); Dream's plan exemption narrowed to plans active inside the 7-day window, so abandoned plans no longer accumulate forever10. no format change (stays 0x0009, additive JSON fields, v1.4.1 files open as-is), MCP tool set unchanged (31) — the plan surface is Go module only this release |
| v1.4.1 | 2026-08-28 | Type-contract cleanup: hex-ID DTOs, L0 profile v2, L3 hypergraph activation | 1. api response DTOs are real structs — every ID field leaves as a 16-char hex string (SearchResult.NewTopicID, AppendL4Message, AgentID() included) with new api.FormatID / api.ParseID helpers2. L0 profile v2 ( FormatVersion 0x0009): field ownership (Name/Role/Preferences host-exclusive, Personality host-seeded + Dream-distilled), typed EmotionState/MBTI distillation signals, dead lexicon/style_traits removed3. zero in-library hex round-trips (repo-layer ID params are uint64, centroid hash via HashBytes)4. L3 import gains source_ref (positional reference) and related (same-graph hyperedges resolved by title, two-phase forward references, idempotent re-import; edges_created result field, L3Relation type exported)5. AppendL4Message gains contentType (Content* constants exported; text/document/code carry the original text, image/audio/video carry a path/URI with mime/size/sha256 in Metadata), L4Query.Type filter and MCP archive_search content_type param6. L6 one-trajectory-per-turn: SessionID is a turn key (search opens, update closes), events carry TopicID for cross-turn crystallization, external surface trimmed to append/query (TrajectoryStats / DeleteTrajectory / PruneTrajectory removed, 33 → 31 tools), Dream l6_prune auto-drops events older than 7 days7. distill/consolidate LLM parsing gains a format-constrained retry 8. breaking: .meh files with FormatVersion != 0x0009 (i.e. ≤ 0x0008) are rejected at Open, no migration |
| v1.4.0 | 2026-08-26 | Multi-agent memory database | 1. one .meh file carries many isolated agent domains: record frames gain agent_id (26-byte header), engine indexes and snapshots (0x02) are per-agent, tenant registry records map names to stable crypto/rand agentIDs2. api.OpenMulti / AgentSession / CreateAgent / ListAgents / DeleteAgent; Open stays zero-change for single-agent hosts (default domain)3. business layer rebuilt around per-agent agentContext with domain locks (same-agent serial, cross-agent parallel), idle-domain memory reclamation and scoped Dream pipelines4. L7 trajectory layer renumbered to L6 (cognitive layers converge to L0–L6) 5. MCP registry shares one MultiAgentDB (one <db-dir>/memhop.meh), os.Root-anchored db-dir6. duplicate structs/conversion layers removed ( topicSlotJSON, topicToL2Meta, single-value slice wrappers)7. Go 1.23–1.26 stdlib modernization ( iter.Seq2, unique.Make, os.Root)8. zero new dependencies 9. breaking: .meh files with FormatVersion <= 0x0007 are rejected at Open, no migration; promoted internal.DB methods on api.DB now carry an agentID parameter (facade methods unchanged), Lock() panics on a closed DB |
| v1.3.4 | 2026-08-26 | L5 tool-declaration isomorphism | 1. memhop-capability format v3: ResourceRef renamed description → desc and gained input (JSON Schema string) / output — the tool-declaration fields now mirror the host tool spec shape (meowire ToolSpec) exactly, so hosts project capabilities with a pure field copy and zero format conversion2. WorkflowStep gained args — action chains carry step parameters officially (no private config formats)3. crystallize prompt emits the v3 shape ( type/resources instead of kind/manifest)4. validateCapabilityImport now requires resource names and validates input as JSON5. breaking: v2 cards are rejected at import (format must be memhop-capability/v3); stored capability records written by earlier versions lose desc/input/output on read6. built-in capability toolbox ( capabilities/*.json) fully rewritten to v3 with real JSON Schemas |
| v1.3.3 | 2026-08-26 | Retrieval scoring normalization + defaults slimdown | 1. vector floor fixed from overriding every other signal to lifting only below-threshold scenes (floor = threshold + cosine×0.5): real-signal ordering (RRF + keyword overlap + bonuses) wins, semantic fallback preserved 2. MemHopDefaults slimmed from 24 fields to 3 business knobs (Capacity / DreamCompressMinTopics / SearchDreamContextThreshold); 4 dead fields (MaxResults / DefaultTimeoutSecs / DefaultMaxOutputTokens / MaxDepth) removed and 16 tuning constants moved to package-private internal/tuning.go3. TopScene / SpreadingActivation / applySceneBonuses / rrfFuse signatures dropped the defaults parameter4. breaking: hosts referencing removed fields must clean up 5. no format change (stays 0x0007)6. MCP tool set unchanged (32) |
| v1.3.2 | 2026-08-26 | API fixes: async Dream + deletion + Update simplification | 1. Search/Update no longer block on an internally triggered Dream (background goroutine, per-scene in-flight dedup, Close cancels a pending Dream) 2. new DeleteTopic (subtree closure + L4 + indexes + parent ChildrenIDs pruning) and DeleteScene (scene + all topics + archives + L1 node + active set) for memory correction3. Update returns error instead of (bool, error)4. SearchResult.ProfileBrief — compact profile digest (name/role/top preferences/style/emotions, bounded)5. no format change (stays 0x0007)6. MCP tool set unchanged (32) |
| v1.3.0 | 2026-08-26 | L1 scene hypergraph + spreading-activation association | 1. Dream creates real RecL1Hyperedge co-occurrence edges between scenes (keyword-overlap Jaccard ≥ L1EdgeMinSimilarity); Search AssociatedContexts replaced the no-op same-scene listing with a graph walk (activation × edge weight × dampening per hop, ≤ L1EdgeMaxHops, top L1AssocMaxScenes other scenes)2. L6 scene-usage record removed — hit counters folded into the L2 SceneSlot (HitCount/LastHitAt)3. L1ReverseIndex (incl. snapshot field) and 4 dead L1 functions removed; association is now a pure storage-level graph read4. .meh format bumped to 0x0007 — 0x0006 files are rejected at Open, no migration5. new defaults: L1EdgeMinSimilarity (0.15), L1EdgeMaxHops (2), L1ActivationDampening (0.5), L1ActivationThreshold (0.05), L1AssocMaxScenes (3) |
| v1.2.7 | 2026-08-25 | Host alignment + bilingual integration guides | 1. Search(ctx, q) and RefineTopicKeywords(ctx, id) accept a context (cancels LLM extraction, encoder calls, internally triggered Dream)2. api exports LlmConfig / MemHopDefaults / TopicSlot / ResourceRef / CrystallizeDetail / TrajectoryStats3. new TrajectoryStats (per-session L7 stats) + memhop_trajectory_stats MCP tool (31 → 32 tools)4. CrystallizeResult.Details — per-candidate create/reuse/merge/skip disposition5. AppendL4Message (pure L4 append, no LLM)6. active-scene capacity: Update triggers a Dream on the oldest scene at Capacity with a compressibility pre-check; SearchDreamContextThreshold zero-value guard7. bilingual integration guides added at repo root ( INTEGRATION_GUIDE.md / INTEGRATION_GUIDE.zh.md) |
| v1.2.5 | 2026-08-20 | MCP server rewritten | 1. cmd/memhop-mcp fully rewritten against the api facade (v1.2.4 removed it): all 31 MCP tools map 1:1 to api.DB methods2. multi-tenant HTTP — SSE + streamable-http (2025-03-26 spec, stateless), each tenant isolated by URL path /mcp/<tenant-id> into its own .meh file, lazy-open registry with a first-open mutex3. all tool outputs serialize record IDs as 16-char hex strings (uint64 JSON numbers lose precision in JS/TS hosts) 4. tenant-ID whitelist + path-traversal rejection (defense in depth) 5. LLM credentials via env vars only (no CLI flag) 6. go-sdk v1.7.0 back as a direct dep (3 → 4) 7. offline tests for config/registry/tools/streamable + multi-tenant SSE smoke 8. codebase cleanup: dropped redundant enum JSON helpers (default ~uint8 JSON behavior is identical), CodeOf migrated to Go 1.26 errors.AsType, scalar cosine loop (2.7× faster at 1024 dims), deleted the internal/repo/open.go forwarding layer (17 funcs + 8 aliases; internal calls core/index directly), removed the ParseID→FormatHash round-trip in Update |
| v1.2.4 | 2026-08-19 | api/ facade + internal/ flattening | 1. Public Go API moved from the root package to github.com/qyiun666/MemHop/api (root memhop.go/types.go removed)2. internal/sub/ flattened into internal/ (package sub → package internal), internal/sub/repo → internal/repo, internal/sub/common → internal/common3. cmd/memhop-mcp removed (rewritten in v1.2.5)4. build config (Makefile fmt, pre-commit hook, CI gofmt) updated 5. breaking change: hosts importing the root package must switch to /api |
| v1.2.3 | 2026-08-18 | MCP compatibility fixes + DSH integration + retrieval quality | 1. MCP tool schemas fixed (no-arg tools no longer emit properties: null, breaking strict clients)2. all tool outputs render record IDs as 16-char hex strings (uint64 JSON numbers lose precision in JS/TS hosts, breaking new_topic_id round-trips)3. new --transport streamable-http (2025-03-26 spec, stateless multi-tenant; supported by DSH's dsh-mcp-client)4. DeepSeek Harness integration guide + agent instructions ( docs/dsh/)5. streamable-http smoke test 6. keyword-extraction prompt overhauled (semantic completeness + colloquial variants + phrases) + Search returns all relevance-ordered topics (scene-context truncation removed), LoCoMo recall 0.392 → 0.668, entity_hit 0.284 → 0.877 |
| v1.2.1 | 2026-08-16 | MCP server + L5 capability layer | 1. New cmd/memhop-mcp binary: multi-tenant SSE MCP server (official go-sdk v1.7.0) mapping the full public API to 28 tools (search/update/dream/checkpoint/status, profile, scenes, knowledge, archive, capabilities, trajectory/crystallize)2. tenant path isolation /mcp/<tenant-id>3. graceful shutdown persists via snapshot 4. offline SSE smoke tests ( make test-mcp)5. usage docs under docs/mcp/ (local)6. L5 plugin layer refactored into the capability layer ( memhop-capability/v1: manual/atomic/composite kinds, draft→active lifecycle via ActivateCapability, fingerprint dedup, Crystallize emits create/reuse/merge candidates)7. built-in capability toolbox ( capabilities/, embedded, read-only, attached at Open)8. Update returns (bool, error)9. .meh format bumped to 0x0005 — 0x0004 files (v1.2.0 plugin records) are rejected at Open, no migration10. encoder health check requires a 2xx HEAD on the endpoint root (no fallback) 11. active scenes bounded by Capacity (default 7, oldest evicted from Dream targets)12. RecordEnd header field + A/B header damage recovery |
| v1.2.0 | 2026-08-14 | L5 plugin layer | 1. L5 action chains → plugin slots (PluginSlot + structured five-section manifest: skills / MCPs / tools / prompts / services) 2. path-only import via ImportPlugin, hand-written create/update removed3. Crystallize dispatches plugins by type from L7 trajectories 4. SearchResult.Crystals → Plugins5. eight-layer architecture (L0–L7) docs |
| v1.1.0 | 2026-07-27 ~ 08.11 | Architecture refactor | 1. Layered internal rewrite (assembly → sub → repo → core/index/common)2. f16 → f32 single-precision vectors 3. topic centroid vector retrieval 4. BatchStore removed5. Dream(ctx) narrowed to (bool, error)6. .meh format 0x0004, incompatible with v1 data7. integration tests rebuilt against the new internal API |
| v1.0.0 | 2026-07-26 | First stable release | Go rewrite with six-layer cognitive architecture, V2 .meh storage, BM25+vector+entity RRF search, Dream consolidation pipeline, L3 hypergraph with community detection. |
| v0.54–v0.58 | 2026-07-16 ~ 07-23 | Go Rewrite | 1. v0.58: Unified RRF — additive scene bonuses, three-channel fusion, L6 removed, atomic.Pointer 2. v0.57: Dream narrowed to L0+L1+L2, LLM hardening, L5 Write API, SkipDistill 3. v0.55: Stability — IVF removed, panic→error, crash recovery, L5 write pipeline 4. v0.54: Go foundation — 4-layer arch, V2 .meh storage, 2 deps, log/slog |
| v0.18–v0.63 | 2026-05-31 ~ 07-10 | Rust | 1. V2 append-only .meh with snapshot/checkpoint2. BM25 + IVF hybrid retrieval 3. L3 hypergraph DSL, community detection (clique + Louvain), BFS/caching 4. Full Dream pipeline: L3 distill → L2 compress → L1 decay → L0 rebuild → L5 crystallize 5. FFI (cdylib), MCP Server, gRPC/Unix Socket encoder |
| v0.6–v0.17 | 2026-05-20 ~ 05-25 | Rust Early | 1. Pure Rust single crate (dropped Python bindings) 2. LMDB to custom .meh storage migration3. 4-layer to 6-layer cognitive architecture evolution 4. MCP Server integration 5. HNSW vector index (replaced brute-force) |
| v0.1–v0.5 | 2026-05-19 ~ 05-24 | Python | 1. Hopfield associative memory network 2. LMDB embedded storage, pip install one-click3. O(1) associative recall with confidence scoring 4. BrainLoop self-circulating agent loop 5. Proved "living memory" concept |
| MeowAgent | github.com/meowagent/meowagent — coming soon |
| MemHop | github.com/qyiun666/MemHop |
| Meowire | github.com/qyiun666/meowire |
| MeowDesk | github.com/qyiun666/MeowDesk — coming soon |
| Website | qyiun666.github.io/meowagent.github.io |
| qyiun666@163.com |
⭐️ Star MemHop on GitHub — your support keeps us building!
MIT OR Apache-2.0