{"slug": "giving-an-opencode-coding-agent-persistent-editable-memory", "title": "Giving an OpenCode Coding Agent Persistent, Editable Memory", "summary": "A developer maintains @ghilteras/opencode-agent-memory, an experimental OpenCode plugin that gives AI coding agents persistent, editable memory stored as scoped Markdown files on disk rather than an opaque remote service. The plugin exposes memory_list, memory_set, and memory_replace tools for global and per-project blocks, plus an opt-in append-only journal with local embedding-based search using paraphrase-multilingual-MiniLM-L12-v2 via Transformers.js. The developer notes that memory systems fail quietly, with stale memory being worse than none.", "body_md": "AI coding agents are good at using the context currently in front of them. They are much less reliable at carrying useful context from one session to the next.\n\nProject conventions, decisions made during a debugging session, and preferences that were obvious yesterday often need to be re-explained after a restart or after context compaction.\n\nI maintain `@ghilteras/opencode-agent-memory`, an experimental plugin for OpenCode that explores an alternative: treating agent memory as editable, scoped Markdown state on disk rather than as an opaque remote service.\n\n`AGENTS.md` and custom instruction files are a good start. I rely on them heavily. But they are a single flat document with no notion of scope, no size enforcement, and no dedicated operations for the agent to maintain it.\n\nWhat I actually wanted was structured state:\n\nThe plugin gives the agent three tools: `memory_list`, `memory_set`, and `memory_replace`. Blocks are plain Markdown files with YAML frontmatter.\n\nGlobal blocks live in `~/.config/opencode/memory/*.md` and are shared across projects. Project blocks live in `.opencode/memory/*.md` and are shared across sessions in that codebase, and are gitignored automatically.\n\nEach block has:\n\n| Field | Type | Default | Purpose | \n|---|---|---|---|\n| `label` | string | filename | unique identifier | \n| `description` | string | generic fallback | tells the agent how to use the block | \n| `limit` | integer | 5000 | maximum characters | \n| `read_only` | boolean | false | prevents agent edits | \n\nThe `description` field carries more weight than it looks like it should. Without it, the agent gets a generic fallback and does not know when the block is relevant. This mirrors the emphasis Letta puts on describing memory blocks well.\n\nThree blocks are seeded on first run: `persona` and `human` globally, `project` for the current codebase.\n\nMemory blocks are curated state. Some things are not: observations, dead ends, discoveries, decisions, and the reasoning behind them.\n\nFor that the plugin adds an optional append-only journal with `journal_write`, `journal_search`, and `journal_read`. Entries are Markdown files with YAML frontmatter stored under `~/.config/opencode/journal/`, and each entry records which project, model, provider, agent, and session it came from.\n\nThe journal is deliberately opt-in. Enabling it is a line in `~/.config/opencode/agent-memory.json`:\n\n```\n{\n  \"journal\": {\n    \"enabled\": true\n  }\n}\n```\n\nAppend-only plus retrieval is what makes it useful. I do not want the agent rewriting history; I want it to be able to find what happened last month.\n\n`journal_search` uses local embeddings rather than a hosted API. Entries are embedded with `paraphrase-multilingual-MiniLM-L12-v2` (384 dimensions, multilingual) through Transformers.js, and the model is cached locally. Journal content does not leave the machine for search.\n\nTwo implementation details were worth the effort:\n\nEmbedding files are versioned. Each `.embedding` sidecar stores `{ v, model, dimension, vector }`. If the stored dimension does not match the current model, the entry degrades to text matching instead of failing the search. Legacy bare-array embeddings from earlier versions remain readable.\n\nThe in-memory index is fingerprinted. `journal_search` keeps an index per store instance and re-reads an entry only when the entry `.md` or its `.embedding` sidecar changed (mtime plus size). Regenerating or deleting a sidecar is picked up on the next search without a restart, and the embedding model is warmed up in the background at plugin init so the first search after a restart does not pay the cold model-load cost.\n\nThere is also a deliberate retrieval floor: since v0.4.2, a query whose text matches an entry title (in either direction, case-insensitive) is guaranteed a high score, so title-based pointers stay retrievable even for entries with long bodies.\n\nI want to be clear about the failure modes, because memory systems fail quietly.\n\nStale memory is worse than no memory. A block that says something true six weeks ago will be confidently applied today. The `description` field and size limits help the agent judge relevance, but they do not make the content true. Having the state as editable Markdown on disk means you can inspect and fix it directly; that is a feature, not a workaround.\n\nAutomatic persistence is not the same as truth. The journal records what the agent observed, including wrong conclusions. Treat it as a log, not a knowledge base.\n\nThe journal is opt-in because silent collection is worse than explicit collection.\n\nRequires OpenCode v1.0.115 or later.\n\n```\n{\n  \"plugin\": [\"@ghilteras/opencode-agent-memory@0.4.3\"]\n}\n```\n\nOpenCode fetches unpinned plugins from npm on each startup; pinned versions are cached and need a manual bump. Restart OpenCode after changing plugin configuration; editing the config file alone is not enough to load a new plugin version.\n\nA `cacheDir` key at the top level of `agent-memory.json` relocates the Transformers.js model cache if you prefer not to use the default Hugging Face cache location.\n\nThis is experimental software, MIT licensed. It is maintained at [https://github.com/Ghilteras/opencode-agent-memory](https://github.com/Ghilteras/opencode-agent-memory) and published as `@ghilteras/opencode-agent-memory`. It is not built by or affiliated with the OpenCode team.\n\nIf you try it, issues and concrete workflow reports are welcome.", "url": "https://wpnews.pro/news/giving-an-opencode-coding-agent-persistent-editable-memory", "canonical_source": "https://dev.to/ghilteras/giving-an-opencode-coding-agent-persistent-editable-memory-458n", "published_at": "2026-09-17 22:24:01+00:00", "updated_at": "2026-09-17 22:52:57.641355+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "mlops"], "entities": ["OpenCode", "@ghilteras/opencode-agent-memory", "Transformers.js", "paraphrase-multilingual-MiniLM-L12-v2", "Letta"], "alternates": {"html": "https://wpnews.pro/news/giving-an-opencode-coding-agent-persistent-editable-memory", "markdown": "https://wpnews.pro/news/giving-an-opencode-coding-agent-persistent-editable-memory.md", "text": "https://wpnews.pro/news/giving-an-opencode-coding-agent-persistent-editable-memory.txt", "jsonld": "https://wpnews.pro/news/giving-an-opencode-coding-agent-persistent-editable-memory.jsonld"}}