{"slug": "show-hn-seahorse-an-agent-s-memory-that-lives-in-your-own-notes", "title": "Show HN: Seahorse – an agent's memory that lives in your own notes", "summary": "Seahorse, a new open-source memory system for LLM agents, stores persistent, bi-temporal memory in local markdown files readable in Obsidian, addressing the problems of opaque, costly, and unreliable existing memory tools. The system, installable via `pip install seahorse-memory`, provides an MCP-native interface and CLI, allowing agents like Claude Code to recall and maintain context across sessions without vendor lock-in. Seahorse claims to overcome issues such as the LOCOMO benchmark's 6.4% wrong gold answers and Mem0's broken reproduction (issue #2800), offering a portable standard that humans can read and edit.", "body_md": "Persistent, bi-temporal memory for LLM agents — local-first, MCP-native, Obsidian-readable.\n\n```\npip install seahorse-memory\nseahorse init myvault && seahorse remember \"Sergio lives in Madrid\"\nseahorse recall \"where does Sergio live?\"\n```\n\nLLM agents start every session from zero. The context window is not memory: it is a scratchpad that resets, and it is too small to hold what an agent has learned across weeks of work. The tools that try to fix this have their own problems:\n\n**They forget badly.** Most memory systems accumulate facts forever and never resolve contradictions — an agent \"remembers\" that a user lives in Madrid and Barcelona at the same time, with no way to know which is current.**They are opaque.** Memory lives in a proprietary database the human cannot read, edit, or audit. If the agent is wrong, there is no way to correct it.**They are expensive to feed.** Every episode goes through an LLM, so writing thousands of small facts costs real money.**They lock you in.** Adopting a memory system often means adopting its runtime, its provider, or its ecosystem.**Their benchmarks are not trustworthy.** The field's own numbers are hard to reproduce: the LOCOMO benchmark has 6.4% wrong gold answers, Mem0's reproduction is broken (issue[#2800](https://github.com/mem0ai/mem0/issues/2800)), and MTEB embedding scores do not predict memory-retrieval performance (LMEB, arXiv[2603.12572](https://arxiv.org/abs/2603.12572)).\n\nSeahorse is a different approach: an **open, portable, bi-temporal memory\nstandard** that an agent writes to and reads from, that a human can read and\ncorrect, and that does not lock you into any runtime or provider.\n\n**Developers building agents**(Claude Code, Cursor, Codex, or your own) who want the agent to remember decisions and context across sessions.** Obsidian power users**who want their notes to be more than a static archive — a knowledge base an agent can query and maintain.** Teams that want portable memory**— a format they can migrate between vendors without replaying history.\n\nThe fastest way to see Seahorse is to give Claude Code a memory that survives between sessions. Three steps:\n\n**1. Capture sessions.** `seahorse setup`\n\ninstalls the observer hooks into\n`~/.claude/settings.json`\n\n; `seahorse observe start`\n\nruns the capture worker.\nEvery session is recorded as episodes — skip-first (near-zero cost),\nredacted, with a deterministic summary.\n\n```\nseahorse setup\nseahorse observe start\n```\n\n**2. Recall across sessions.** The SessionStart hook injects `seahorse context`\n\ninto the next session, so the agent starts with what it learned before. Ask\ndirectly with `seahorse recall`\n\n:\n\n```\nseahorse context\nseahorse recall \"what did we decide about the API design?\"\n```\n\n**3. Bring your existing memory.** If you already use claude-mem, `seahorse import`\n\nmigrates its observations into canonical episodes — no replay, no\nlock-in:\n\n```\nseahorse import --mode commit\n```\n\nThe key difference: the agent writes into **the same vault you edit in\nObsidian**. Every episode is a markdown file with YAML frontmatter — readable,\neditable, diffable in git, and auditable by a human. The agent's memory is not a\nblack box; it is your notes.\n\nSeahorse is built for agents: the memory surface is a stdio MCP server\n(`io.seahorse.memory/v1`\n\n) that any agent that speaks MCP can connect to. The\nCLI is for humans and scripts; agents talk to `seahorse-mcp`\n\n.\n\n**Register the server in Claude Code** (local scope, default):\n\n```\nclaude mcp add seahorse-mcp -- uvx --from seahorse-memory seahorse-mcp --vault \"${HOME}/myvault\"\n```\n\nThe `--`\n\nis required — it separates Claude's own flags from the server command.\nUse `--scope project`\n\nto share the server with a team via `.mcp.json`\n\n(checked\ninto git). Verify with `claude mcp list`\n\n(should show `✔ Connected`\n\n) and\n`claude mcp get seahorse-mcp`\n\n.\n\n**Or configure it in .mcp.json** at the project root (works with any MCP\nclient):\n\n```\n{\n  \"mcpServers\": {\n    \"seahorse-mcp\": {\n      \"type\": \"stdio\",\n      \"command\": \"uvx\",\n      \"args\": [\"--from\", \"seahorse-memory\", \"seahorse-mcp\", \"--vault\", \"${HOME}/myvault\"]\n    }\n  }\n}\n```\n\nNote: `~`\n\nis not expanded in `.mcp.json`\n\n— use `${HOME}`\n\nor an absolute path.\n(`mcpServers`\n\nin `settings.json`\n\nis silently ignored; MCP servers live in\n`~/.claude.json`\n\nfor user/local scope and in `.mcp.json`\n\nfor project scope.)\n\nOnce connected, the agent sees the 14 memory tools — `remember`\n\n, `recall`\n\n,\n`recall_timeline`\n\n, `recall_full`\n\n, `improve`\n\n, `forget`\n\n, `build_pit`\n\n,\n`skill_add`\n\n, `skill_show`\n\n, `skill_list`\n\n, `skill_search`\n\n, `freshness_view`\n\n,\n`audit_log`\n\n, `follow_supersedes_chain`\n\n(see [The agent\nsurface](#the-agent-surface--7-memory-native-primitives--7-proceduralread-only-tools)).\n\nThe observer (`seahorse setup`\n\n) is a separate piece: it captures Claude Code\nsessions into episodes. The MCP server is how the agent *reads and writes*\nmemory. Both work together — capture sessions, then recall across them.\n\n```\ngraph LR\n    A[Claude Code / any MCP agent] -- stdio MCP io.seahorse.memory/v1 --> S[seahorse-mcp]\n    S --> E[Bi-temporal engine]\n    E --> DB[(sqlite3 + sqlite-vec + FTS5)]\n    E --> V[Obsidian vault: markdown + F3.1 frontmatter]\n    H[Human in Obsidian] --> V\n```\n\nAn agent talks to `seahorse-mcp`\n\nover stdio MCP. The engine stores every\nepisode twice: once in a single-file SQLite database (sqlite-vec for vector\nsearch, FTS5 for full-text), and once as a markdown file with F3.1 frontmatter\nin the vault. The human edits the same markdown. The format is versioned and\ndocumented in [docs/f3.1-format.md](/ssanvi-builds/seahorse/blob/main/docs/f3.1-format.md).\n\n```\n# Install (PyPI):\npip install seahorse-memory\n# …or with uv:\nuv tool install seahorse-memory\n# For hybrid semantic retrieval (FastEmbed ONNX, downloads mE5-small on first\n# embed): pip install \"seahorse-memory[embeddings]\"\n# For the multi-LLM extraction path (LiteLLM): pip install \"seahorse-memory[llm]\"\n\n# Create a vault and write your first episode:\nseahorse init myvault\nseahorse remember \"Sergio lives in Madrid\" --title home\nseahorse recall \"madrid\"\n\n# Improve and forget (append-only; history is preserved):\nseahorse improve <ep_id> \"Sergio lives in Barcelona\" --reason correction\nseahorse forget <ep_id> --reason done\n\n# Session capture, context, and consolidation:\n# Install the observer (writes [observe] + merges the Claude Code hooks into\n# ~/.claude/settings.json):\nseahorse setup\n# Start the observer (unix socket + worker), then the next session is captured\n# automatically (skip-first, redacted, deterministic summary):\nseahorse observe start\nseahorse observe status\n# Bootstrap context by recency (the SessionStart hook injects this):\nseahorse context\n# Distill recurrent episodes into semantic knowledge notes (N≥3, idempotent):\nseahorse consolidate\n# Remove the observer:\nseahorse setup --uninstall\n\n# Serve an agent over stdio MCP (io.seahorse.memory/v1):\nseahorse-mcp --vault myvault\n# …equivalently:\nseahorse mcp --vault myvault\n```\n\nThe `seahorse`\n\nconsole script is for humans and shell scripts; `seahorse-mcp`\n\nis\nfor agents. The `seahorse mcp`\n\nsubcommand invokes the same stdio server as\n`seahorse-mcp`\n\n, so both agent entry points are equivalent. To connect an agent,\nsee [Use it from an agent](#use-it-from-an-agent-mcp).\n\n**Python ≥ 3.11**(any recent 3.11/3.12/3.13 works). The interpreter's`sqlite3`\n\nmust support`enable_load_extension`\n\n(sqlite-vec needs it); most standard builds do —`seahorse doctor`\n\nreports it as a FAIL if not.**Obsidian is optional.** Seahorse runs on any directory of markdown —`seahorse init`\n\ncreates a`.seahorse/`\n\nsidecar in a plain folder. Obsidian is a human-facing editor for the same folder; its`.obsidian/`\n\ndirectory is ignored by Seahorse, never required.\n\nA vault of pre-existing Obsidian notes (no frontmatter, or legacy `tags`\n\n/\n`created`\n\nfrontmatter) is not yet in the canonical format — `seahorse index rebuild`\n\nfails honestly on those notes. `seahorse frontmatter migrate`\n\nconverts\nthem:\n\n```\n# Preview: classify every note, write nothing (always exit 0):\nseahorse frontmatter migrate --vault myvault --dry-run\n# Apply: convert legacy notes, leave canonical notes untouched, refuse\n# incompatible notes:\nseahorse frontmatter migrate --vault myvault\n# Rebuild the sidecar index from the converted notes:\nseahorse index rebuild --vault myvault\n```\n\nApply exits `97`\n\nwhen incompatible notes block a full migration — the manifest\nsummary is printed first so the operator sees which notes need manual\nresolution. `--resume`\n\nskips notes unchanged since the last manifest;\n`--batch-size`\n\nsets the manifest checkpoint cadence. Migration works before\n`seahorse init`\n\n(it only touches `.md`\n\nfiles + the manifest).\n\nFirst run: the semantic-embedding model (mE5-small, ~235MB) downloads lazily on the first`remember`\n\n/`recall`\n\n— the CLI announces it so the first call doesn't look hung.`seahorse status`\n\nshows the active retrieval regime (`hybrid RRF (model cached)`\n\nvs`current-state listing — install seahorse-memory[embeddings] for semantic recall`\n\n).\n\nA comparison of verified facts, not a ranking. Sources: the project's\nstate-of-the-art analysis (see the [research\nnotes](https://github.com/ssanvi-builds/seahorse) and the claims cited below).\n\n| Seahorse | mem0 | Letta / MemGPT | Zep / Graphiti | claude-mem | LangMem | |\n|---|---|---|---|---|---|---|\nPortable open format |\n✓ F3.1 spec | ✗ proprietary | ✗ runtime-bound | ✗ | ✗ own schema | ✗ |\nHuman-readable layer |\n✓ Obsidian vault | ✗ | ✗ | ✗ | ✗ | ✗ |\nBi-temporal (point-in-time) |\n✓ | ~ | ~ | ✓ Graphiti | ✗ | ✗ |\nLocal-first, zero-infra |\n✓ | ~ | ~ | ✗ cloud-only | ✓ | ~ |\nReproducible benchmark |\n✓ harness in-repo | ✗\n|\n\n**License** Legend: ✓ yes · ~ partial · ✗ no · — not verified.\n\nThe two facts that matter most: **mem0 paywalls the features that produce its\nbenchmark numbers**, and **Zep abandoned self-host for cloud-only**. Seahorse\nis local-first by default, publishes its benchmark harness in the repo, and\nkeeps the memory format portable so you are never locked in.\n\nSeahorse ships a reproducible benchmark harness (LMEB-S, a subsample of the LongMemEval benchmark) and publishes its own numbers — with caveats. The point is not a leaderboard; it is an honest, reproducible measurement.\n\n| Metric | Value | Note |\n|---|---|---|\n| recall@10 | 0.13 | knowledge-update slice: 0.44 |\n| ndcg@10 | 0.11 | |\n| mrr | 0.13 | knowledge-update slice: 0.47 |\n| precision@10 | 0.02 | |\n| token efficiency | 0.998 | 51.5M tokens full-context → 121K measured |\n| latency p95 (INDEX) | 42 ms | retrieval-only, no rerank |\n\nCaveats: the run uses a **subsample** (n≈470–500 questions, not the full\ndataset); relevance is judged by a **small LLM without human validation**; and\nit measures **retrieval only**, not the agent's final answer. A cross-encoder\nrerank was tested and **rejected** — it degraded recall@10 to 0.11 with 1.2s\nlatency. Full methodology and reproduction commands in\n[docs/benchmark.md](/ssanvi-builds/seahorse/blob/main/docs/benchmark.md).\n\nThese numbers measure\n\nretrieval ranking onlyon a subsample with a small judge — they arenot comparableto the end-to-end accuracy scores other memory systems publish (e.g. Graphiti 63.8%, Mem0 94.8, Hindsight 91.4%). See[docs/benchmark.md]for how not to compare.\n\n**What is an episode?** A single memory record: a markdown file with YAML\nfrontmatter carrying two time axes (`valid_at`\n\n— when it became true, and\n`created_at`\n\n— when it was recorded), provenance, and a cognitive type. The\nformat is versioned and documented in [docs/f3.1-format.md](/ssanvi-builds/seahorse/blob/main/docs/f3.1-format.md).\n\n**Why Obsidian?** Because the human is part of the memory system. The agent\nwrites into the same vault you edit — markdown is readable, diffable in git,\nand auditable. If the agent is wrong, you correct the note, not a database.\n\n**How is this different from claude-mem?** claude-mem stores session\nobservations in its own schema. Seahorse is an open, bi-temporal standard with\na portable format and a human-readable layer — and `seahorse import`\n\nmigrates\nclaude-mem observations into canonical episodes, so it is a bridge, not a\ncompetitor.\n\n**Do I need an LLM?** No. The deterministic skip-path is the default for the\nbulk of writes (near-zero cost). LLM extraction is optional (`seahorse-memory[llm]`\n\n)\nand reserved for the few episodes that justify it.\n\n**Is it free?** Yes. Apache-2.0, local-first, zero-infra. A managed SaaS and\nenterprise tier are planned for the future (see the project's strategy notes).\n\n**How do I contribute?** See [CONTRIBUTING.md](/ssanvi-builds/seahorse/blob/main/CONTRIBUTING.md) for the\ndevelopment setup, test/lint commands, and pull request workflow.\n\nSee [ROADMAP.md](/ssanvi-builds/seahorse/blob/main/ROADMAP.md) for what is built, what is next, and the direction\nof the project. Release history lives in [CHANGELOG.md](/ssanvi-builds/seahorse/blob/main/CHANGELOG.md).\n\nExposed over stdio MCP (`io.seahorse.memory/v1`\n\n, protocol pinned `2025-11-25`\n\n) and\nmirrored on the CLI. These are memory primitives, not generic CRUD: an agent calls\n`remember`\n\n/ `recall`\n\n/ `improve`\n\n/ `forget`\n\nthe way a human would talk about memory.\n\nThe 7 primitives (write + retrieve):\n\n| Primitive | What it does |\n|---|---|\n`remember` |\nRecord an episode (body, source, optional title/subject). |\n`recall` |\nINDEX level — the current-state listing, clamped to `top_k` . |\n`recall_timeline` |\nTIMELINE level — the supersedes chain around an anchor episode. |\n`recall_full` |\nFULL level — the hydrated episode with all provenance. |\n`improve` |\nSupersede an episode with a corrected one (append-only). |\n`forget` |\nSoft-delete an episode (append-only; history preserved). |\n`build_pit` |\nBuild a point-in-time projection (all-None → current state). |\n\nPlus 7 procedural / read-only tools (skills + facade introspection):\n\n| Tool | What it does |\n|---|---|\n`skill_add` |\nCreate a procedural skill (deterministic, near-zero cost). |\n`skill_show` |\nShow a skill's gated body (trust gate). |\n`skill_list` |\nList procedural skills (Discovery level). |\n`skill_search` |\nSearch procedural skills (hybrid recall, procedural filter). |\n`freshness_view` |\nFreshness snapshot of an episode (age, stale, pending_ingest). |\n`audit_log` |\nAudit events for an episode (write-path history). |\n`follow_supersedes_chain` |\nThe supersedes closure for an episode (version history). |\n\nThree retrieval levels give **progressive disclosure**: a cheap listing first\n(INDEX), the chain on demand (TIMELINE), and the full record only when needed\n(FULL). This keeps the common path cheap.\n\n- Bi-temporal, append-only episode store on stdlib\n`sqlite3`\n\n+ sqlite-vec (FTS5- vec0). Auto-migrating schema.\n\n- The 7 memory-native primitives plus 7 procedural / read-only tools, on both the CLI and stdio MCP (14 tools total).\n- Progressive disclosure (INDEX / TIMELINE / FULL) and point-in-time projection.\n**Hybrid semantic retrieval**:`recall`\n\nranks by relevance — sqlite-vec kNN + FTS5 BM25 fused with Reciprocal Rank Fusion, with point-in-time routing (`state_at`\n\n/`known_at`\n\n) when a real embedder is wired. The write path and`seahorse index rebuild`\n\npopulate vec0/FTS (best-effort — an embedder failure never fails the episode write).**Honest degrade**: without the`embeddings`\n\nextra (or with no vectors populated),`recall`\n\nfalls back to the current-state listing (score 0.0, no ranking) and point-in-time recall is refused — the engine keeps working without ranking.**LLM extraction**: a real multi-LLM path (ollama / gemini / groq / openrouter / openai / anthropic / deepseek / vllm, local-first) with a strict schema validator + repair loop, retry/fallback chain, and an operative cost cap (local and free-tier models price at $0).`seahorse init --llm`\n\nbootstraps it; the skip-path stays the near-zero-cost default for the bulk of writes.**Local-first CI gate**: the real extraction path runs in CI against the weakest model of the family (`ollama/qwen3:0.6b`\n\n) so the validator + repair must carry the load — the path does not silently depend on native structured outputs or a strong model.- Supersession (\n`improve`\n\n) and soft-delete (`forget`\n\n) with full history preserved. **Batch distillation**(`seahorse consolidate`\n\n): distill many episodes into a single consolidated note — deterministic by default, with opt-in LLM synthesis (`--synthesis llm`\n\n) and supersession (`--supersede`\n\n) so the consolidated note supersedes its sources.- Frontmatter import/export for the Obsidian vault layer (markdown as the human-readable, portable on-disk contract).\n**Legacy-vault migration**:`seahorse frontmatter migrate`\n\nconverts legacy Obsidian notes with a`--dry-run`\n\npreview,`--resume`\n\n, and honest exit`97`\n\nwhen incompatible notes block a full migration.- Honest exit codes and a structured\n`{\"error\": {...}}`\n\nenvelope on stderr, so agents and scripts can branch on`seahorse_code`\n\n/`cli_code`\n\ndeterministically.\n\nA few CLI commands are wired but intentionally return exit `75`\n\nwith a reason\n(`expire`\n\n, `revalidate`\n\n, `index verify`\n\n), so the surface is honest about what is\nnot implemented yet rather than silently no-op'ing. `llm_partial`\n\nstays fully\nreserved.\n\n- Python ≥ 3.11. stdlib\n`sqlite3`\n\n+ sqlite-vec for storage (zero-infra single file; the`vec0`\n\nvirtual table + FTS5). - numpy for the embedding blob shape.\n- Pydantic v2 for the canonical\n`Episode`\n\ncontract (core type system). - Typer for the CLI surface (humans and scripts). Confined to\n`seahorse.cli`\n\n. - stdio JSON-RPC 2.0 for the MCP agent surface (hand-rolled framing, stdlib-only\n`seahorse.mcp`\n\npackage —`import seahorse.mcp`\n\ndoes not load Typer). `ruamel.yaml`\n\n+`python-frontmatter`\n\n, confined to the frontmatter adapter.**FastEmbed ONNX + onnxruntime**(`embeddings`\n\nextra, NOT in the default install): the mE5-small bundle defaults to`model_O4.onnx`\n\n(fp32, ~235MB) — no int8/fp16 artifact is portable to Apple Silicon, and an open standard must run on Windows/Linux/macOS. A portable int8 bundle is a measured follow-up.**LiteLLM**(`llm`\n\nextra, NOT in the default install): unifies the 100+ provider surface for the LLM extraction path. Without the extra,`seahorse.llm`\n\nstill imports (contract +`StubLLMClient`\n\n) and the real path degrades llm→skip with a setup hint.\n\nThe FastAPI / SQLAlchemy / Postgres stack is planned for a later multi-agent tier (Postgres + pgvector). The README states what ships now, not the target architecture.\n\n**Unit + integration**:`uv run pytest`\n\n(coverage ≥ 80% gate).**Fresh-user e2e**:`scripts/e2e-fresh-user.sh`\n\n— the full install → init → core CLI → embeddings → LLM → import → MCP flow from a clean, isolated HOME (never touches the real`~/.claude`\n\n/`~/.claude-mem`\n\n).**Environment matrix**:`scripts/e2e-matrix.sh`\n\n— the fresh-user flow across environment combinations (install method × extras × Obsidian × Ollama × online/offline × vault state × concurrency).`--ci-subset`\n\nruns the CI-safe combos (`core_min`\n\n+`uv_sync_dev`\n\n);`--list`\n\nshows all combos.**Core stress**:`scripts/stress-core.sh`\n\n— ingest 1000+ episodes, recall`--top-k 100`\n\np95 ≤ 250ms (in-process INDEX budget), concurrent single-writer, reindex, idempotent import, improve/forget chain.\n\nContributions are welcome. See [CONTRIBUTING.md](/ssanvi-builds/seahorse/blob/main/CONTRIBUTING.md) for the\ndevelopment setup, test/lint commands, and pull request workflow. Release history\nlives in [CHANGELOG.md](/ssanvi-builds/seahorse/blob/main/CHANGELOG.md).\n\nApache-2.0. See [LICENSE](/ssanvi-builds/seahorse/blob/main/LICENSE).\n\n**v0.8.0.** The memory engine works end-to-end from a clean install: write\nepisodes, recall them with hybrid semantic retrieval, extract with a real\nmulti-LLM path (local-first, CI-gated), improve and forget them, and serve an\nagent over stdio MCP. Recall ranks by relevance when vectors are populated and\nthe embedder is wired, and honestly degrades to a current-state listing\notherwise. `seahorse import`\n\nmigrates claude-mem observations to episodes, and\nan opt-in recency ranking signal is available. Batch distillation\n(`seahorse consolidate`\n\n) turns many episodes into one consolidated note, with\nopt-in LLM synthesis and supersession. The MCP server and the CLI now expose the\nsame skill and read-only surfaces (14 MCP tools), timelines can be ranged by\n`created_at`\n\n/`valid_at`\n\n, and `--verbose`\n\nreports per-operation timing. See\n[What works](#what-works) and [ROADMAP.md](/ssanvi-builds/seahorse/blob/main/ROADMAP.md) for what is next.", "url": "https://wpnews.pro/news/show-hn-seahorse-an-agent-s-memory-that-lives-in-your-own-notes", "canonical_source": "https://github.com/ssanvi-builds/seahorse", "published_at": "2026-08-19 06:11:41+00:00", "updated_at": "2026-08-19 06:42:17.304405+00:00", "lang": "en", "topics": ["artificial-intelligence", "ai-agents", "ai-tools", "ai-infrastructure"], "entities": ["Seahorse", "Obsidian", "Claude Code", "Mem0", "LOCOMO", "MTEB", "LMEB"], "alternates": {"html": "https://wpnews.pro/news/show-hn-seahorse-an-agent-s-memory-that-lives-in-your-own-notes", "markdown": "https://wpnews.pro/news/show-hn-seahorse-an-agent-s-memory-that-lives-in-your-own-notes.md", "text": "https://wpnews.pro/news/show-hn-seahorse-an-agent-s-memory-that-lives-in-your-own-notes.txt", "jsonld": "https://wpnews.pro/news/show-hn-seahorse-an-agent-s-memory-that-lives-in-your-own-notes.jsonld"}}