- Add image-baked fallback skills (opencode-devbox-environment, mempalace) + harness instruction (instructions/opencode-devbox.md) under /usr/local/share/opencode-devbox/, symlinked in by entrypoint-user.sh (skills only-when-absent; instruction symlink to image, never copied into the devbox-opencode-config volume). Ported from pi-devbox v1.2.0/v1.2.1, adapted to opencode's ~/.config/opencode/instructions/ auto-load model. No pi-extensions skill (opencode has no fork/recall). - Bump opencode 1.17.8 -> 1.17.10. - Bump mempalace 3.4.0 -> 3.5.0 (lockstep with pi-devbox v1.2.2); remove the obsolete diary_write anyOf perl workaround (fixed upstream, issue #1728). - Fix stale ssh-lan.conf ProxyJump guidance comment in setup-lan-access.sh (mirrors pi-devbox 8de0fad); comment-only. - smoke-test.sh + recreate-sanity-check.sh assert baked source + resolved links. - Docs: README Custom skills, AGENTS.md duties + MINOR example, CHANGELOG.
14 KiB
name, description
| name | description |
|---|---|
| mempalace | MemPalace agent memory protocol. Use on every session to maintain continuity across conversations — search before answering about past work, write diary entries before session ends, and mine new projects into the palace. Load this skill at session start. |
MemPalace Agent Memory Protocol
Overview
MemPalace gives you persistent memory across sessions via an MCP server. It stores project knowledge (mined from files), conversation summaries (diary entries), and entity relationships (knowledge graph). Without this protocol, you have tools but no habits — and memory without habits is just storage.
Core principle: Storage is not memory. Storage + protocol = memory.
When to Load This Skill
- At the start of every session (proactively, before the user asks)
- When the user mentions past conversations, decisions, or work
- When working on a new project or repository for the first time
- When the user asks about people, projects, or relationships
Session Lifecycle
Phase 1: Wake Up (session start)
Run these immediately when a session begins, before responding to the user:
-
Load palace overview:
mempalace_statusThis returns wing/room counts, the AAAK spec, and the memory protocol reminder.
-
Read your recent diary:
mempalace_diary_read(agent_name="<your_agent_name>", last_n=5)Scan for context about recent sessions — what was worked on, what matters, what's pending.
-
Check the knowledge graph for the user or active project if relevant:
mempalace_kg_query(entity="<project_or_person>")
Do NOT announce this to the user. Just do it silently to orient yourself.
Phase 2: Active Session (during work)
Search Before You Speak
Before answering questions about past work, decisions, people, or projects:
mempalace_search(query="<keywords>", wing="<project>")
Never guess about facts that might be in the palace. Wrong is worse than slow. Say "let me check" and query.
Mine New Projects
When working on a new codebase for the first time:
-
Check if it's already mined:
mempalace_list_wings -
Decide what to mine — docs first, code never (by default).
The palace is for context and intent, not code recall. Code is better read from the working tree via
Read/Grep/glob— always authoritative, never stale. Embedding source code produces thousands of low-signal drawers (e.g.def __init__(self, ...)across every class) that pollute search for years.Mine by default:
*.md,*.rst,*.txt— docs, READMEs, CHANGELOGs, architecture notesAGENTS.md,CLAUDE.md,CONTRIBUTING.md, design/decision docs — highest signal per byte*.sh,Dockerfile,Makefile, entrypoints — small, intent-bearing*.yml,*.yaml,*.toml, selective*.json(docker-compose,pyproject,mkdocs.yml, CI workflows) — skip lockfiles
Do NOT mine by default:
*.py,*.ts,*.tsx,*.js,*.go,*.rs,*.java,*.cpp,*.c,*.rb— raw source code- Test files, fixtures, generated code
node_modules/,.venv/,__pycache__/,.mypy_cache/,.pytest_cache/,.ruff_cache/(the miner respects.gitignorebut double-check)
Exception: if a code file is the documentation (e.g. a heavily-commented reference script, or a protocol definition), file it manually via
mempalace_add_drawer. -
Before mining, inspect the repo to estimate drawer count:
# Quick audit — what will actually get mined? find <dir> -type f \ -not -path '*/.git/*' -not -path '*/node_modules/*' \ -not -path '*/.venv/*' -not -path '*/__pycache__/*' \ \( -name '*.md' -o -name '*.sh' -o -name '*.yml' -o -name '*.yaml' \ -o -name '*.toml' -o -name 'Dockerfile*' -o -name 'Makefile' \) | wc -lA docs-heavy repo should produce ~5–10 drawers per file. If a mine produces >15 drawers/file on average, code leaked in — investigate.
-
Run the mine:
mempalace init --yes <directory> mempalace mine <directory> --agent <your_agent_name>The miner currently lacks a
--docs-onlyor--exclude-extflag (as of v3.3.3). Until it does, either:- (a) Add a
mempalace.yamlat the repo root with explicit include globs, OR - (b) Mine everything, then surgically remove code-sourced drawers via SQL on
~/.mempalace/palace/chroma.sqlite3(delete byembedding_metadata.source_file LIKE '%.py'), followed bymempalace repair --yes.
- (a) Add a
-
If the CLI miner misses a file you do want (e.g.,
.zsh, an undocumented extension), file it manually:mempalace_add_drawer(wing="<project>", room="<aspect>", content="<verbatim content>", source_file="<path>") -
After mining, reconnect to pick up the new embeddings:
mempalace_reconnectIf search errors occur after mining ("Error finding id"), repair the index:
mempalace repair --yes
Track Facts in the Knowledge Graph
When you learn new facts about people, projects, or relationships:
mempalace_kg_add(subject="ProjectX", predicate="uses", object="PostgreSQL")
mempalace_kg_add(subject="Alice", predicate="owns", object="ProjectX", valid_from="2026-01-15")
When facts change (ended, no longer true):
mempalace_kg_invalidate(subject="Alice", predicate="works_at", object="OldCorp", ended="2026-03-01")
Cross-Reference with Tunnels
When content in one project relates to another, create a tunnel:
mempalace_create_tunnel(
source_wing="project_api", source_room="endpoints",
target_wing="project_db", target_room="schema",
label="API endpoints map to these DB tables"
)
Feeding opencode session history (opencode + mempalace-toolkit only)
MemPalace has no upstream integration with opencode as of v3.3.3 — hooks_cli.py only supports claude-code and codex harnesses. Opencode persists every turn in a local SQLite DB at ~/.local/share/opencode/opencode.db, but nothing moves that data into the palace automatically.
On a machine with opencode + the mempalace-toolkit installed, session history is fed into wing_conversations via mempalace-session — either manually, or on a weekly systemd user timer / cron schedule shipped in mempalace-toolkit/contrib/. If this is missing, opencode conversations exist only in the local SQLite DB and are invisible to mempalace_search.
How to tell if it's set up:
mempalace_list_wings
If wing_conversations exists and has a drawer count comparable to the user's opencode session count, session feeding is working. If it's empty or suspiciously small, suggest:
- Check if the toolkit is installed:
which mempalace-session. - If installed, suggest running
mempalace-session --dry-runto preview andmempalace-sessionto file. - If not installed, point the user at
gitea.jordbo.se/joakimp/mempalace-toolkitfor setup.
Don't try to paper over the gap by dumping turn-level content into the palace manually via mempalace_add_drawer — that reinvents what mempalace-session does with normalization and dedup. Use the tool.
Full routine (triggers, cadence, automation) is in the opencode-mempalace-bridge skill and the toolkit's ARCHITECTURE.md §5. The two skills pair: this one (mempalace) covers using the palace; that one (opencode-mempalace-bridge) covers feeding it from opencode.
Phase 3: Wind Down (session end)
Always write a diary entry before the session ends. This is the most important habit.
mempalace_diary_write(
agent_name="<your_agent_name>",
entry="<AAAK compressed summary>",
topic="session-summary"
)
Why still write diaries when sessions may be mined automatically?
On machines running opencode + mempalace-toolkit, every session is mined into wing_conversations on a weekly (or user-defined) schedule. A common and incorrect conclusion: "since every turn is captured automatically, writing a diary entry is redundant." It isn't.
Session mining captures what was said (every turn, verbatim). A diary captures what the session meant — editorial judgment by the agent who lived it:
- Lessons learned, patterns noticed, pending items rolled forward
- Meta-observations that were never said aloud during the session
- Aggregate counts (commits shipped, bugs fixed, hours spent)
- A compressed, recency-scannable summary for the next agent's wake-up
Mining raw turns cannot surface these because the words don't exist verbatim — they're the agent's reflection at wind-down. Think of the split as release notes (diary) vs. git log with diffs (session mine): a repo keeps both because they answer different questions. So does the palace.
Practical rule: automated mining does not replace Phase 3. Both systems cover each other's failure modes — a skipped diary is recovered from the raw turns; a missed mine is recovered from the diary summary. For the full treatment (comparison table, retrieval patterns, token economics), see mempalace-toolkit/ARCHITECTURE.md §5 → "Diary vs session mine: why keep both?".
AAAK Diary Format
Write diary entries in compressed AAAK format for efficiency. Structure:
SESSION:<date>|<what.you.worked.on>|
TASKS:
1.<task.description>→<outcome>|
2.<task.description>→<outcome>|
DISCOVERED:<unexpected.findings>|
ENTITIES:<people.or.projects.encountered>|
<importance: one to five stars>
Example:
SESSION:2026-04-28|api.refactor+db.migration|
TASKS:
1.refactored.auth.endpoints→split.into.3.modules|
2.added.user.roles.migration→postgres.enum.type|
DISCOVERED:legacy.session.table.unused.since.v2|
ENTITIES:ProjectX;Alice(reviewer)|
***
Rules:
- Use dots instead of spaces within phrases
- Use pipes as field separators
- Use arrows for cause/effect or transitions
- Stars indicate session importance (one to five)
- Keep it tight — a future agent should get the gist in seconds
What to Capture
Prioritize recording:
- Decisions made and their rationale
- Discoveries — things that surprised you or that a future session needs to know
- Unfinished work — what's pending, what was deferred
- User preferences observed during the session
- Entities encountered — people, projects, tools, services
Phase 4: Fact Updates
If facts changed during the session, update the knowledge graph before writing the diary:
mempalace_kg_invalidate(subject="...", predicate="...", object="...", ended="<today>")
mempalace_kg_add(subject="...", predicate="...", object="...", valid_from="<today>")
Palace Structure
Wings
Wings are top-level categories, typically one per project or domain:
- Named after the project directory (e.g.,
cli_utils,opencode_devbox) - Agent diaries live in
wing_<agent_name>(e.g.,wing_orchestrator,wing_pi)
Multi-harness palace
A single palace can be fed by multiple coding-agent harnesses. On this machine the palace is shared between opencode and pi (Mario Zechner's pi-coding-agent). Implications:
wing_conversationsmixes sources. Both harnesses' session feeders write into the same wing. To tell them apart, look at thesource_filemetadata on each drawer:pi_<uuid>.jsonl→ pi session<slug>_ses_<id>.jsonl→ opencode session- The first chunk of each session also carries a
| source: opencodeor| source: pimarker in the synthetic header line.
- Other wings may belong to other harnesses. For example
wing_piis pi's diary, not opencode's. Don't assume every diary entry was written by you — checkagent_nameon the entry. - Session feeders run on different schedules. Pi sessions are fed Tue 03:00, opencode sessions Mon 03:00. Recent sessions from either harness can lag the palace by up to a week, so absence-of-evidence in
wing_conversationsis not evidence-of-absence for recent work. - Reading another harness's diary is useful. When orienting after a gap,
mempalace_diary_read agent_name=pi(or whichever sibling agent has been active) often gives a fresher picture than waiting for the conversations feeder to catch up.
Rooms
Rooms are aspects within a wing:
fzf,scripts,configuration,general— whatever the miner detects- Diary entries go into rooms by topic tag
Drawers
Drawers hold verbatim content — never summarized, always searchable.
Tunnels
Cross-wing connections linking related content across projects.
Knowledge Graph
Entity-relationship triples with temporal validity. Query with mempalace_kg_query, browse with mempalace_kg_timeline.
Troubleshooting
| Problem | Fix |
|---|---|
| "No palace found" | Run mempalace init <dir> then mempalace mine <dir> |
| "Error finding id" after mining | Run mempalace repair --yes then mempalace_reconnect |
| Search returns irrelevant results | Use max_distance=1.0 for stricter matching; add wing filter |
| Miner skips file types | File manually with mempalace_add_drawer or use --no-gitignore |
| Stale results after external changes | Call mempalace_reconnect |
Anti-Patterns
- Don't guess when you can search. If a question touches past work, search first.
- Don't skip the diary. A session without a diary entry is a session forgotten.
- Don't summarize drawer content. File verbatim — the embedding model needs the original words.
- Don't mine .git directories or node_modules. The CLI miner respects .gitignore by default.
- Don't create duplicate drawers. Use
mempalace_check_duplicatebefore adding manually. - Don't treat the palace as a task list. It's for knowledge and context, not todos.