Files
Rohit Ghumare 9c82d2aa7d chore(release): v0.9.29 with project-scope parity across capture surfaces (#1141)
* chore(release): v0.9.29 with project-scope parity across surfaces

Version trio + plugin manifests + supportedVersions + ExportData union
bumped to 0.9.29; CHANGELOG entry covering everything since v0.9.28 with
upgrade notes for the four visible behavior changes.

Fixes the endpoint-count drift on main (130 registered routes vs docs
saying 129 after #1132 landed in parallel with #1136).

Project-scope parity: OpenCode plugin, Hermes plugin, Pi extension, and
JSONL replay now resolve project the same way the hooks do (env
override, git toplevel basename, cwd basename) instead of sending raw
filesystem paths, closing #903 and #1135 and pre-empting the same bug
in pi. The filesystem watcher accepts AGENTMEMORY_PROJECT_NAME with the
old AGENTMEMORY_PROJECT kept as a deprecated alias, replay handles
Windows-recorded paths, and OpenCode file enrichment matches the
agent's lowercase tool names (the capitalized set never matched).

Tests: opencode fallback expectations updated to basenames per the
canonicalization, git-toplevel resolution covered with a fixture repo,
new project-scope-parity suite for replay and fs-watcher.

* fix(release): review findings, git-toplevel parity, doc counts

- skills generator dedupes routes on method plus path, so the REST
  reference lists all 130 registered routes instead of hiding the second
  method on ten dual-method paths (header said 119)
- fs-watcher trims AGENTMEMORY_PROJECT_NAME and the deprecated alias,
  treating whitespace as unset, and derives the git toplevel basename
  when watching a subdirectory
- replay resolves the git toplevel basename when the recorded cwd still
  exists locally (memoized per cwd), keeping the basename fallback for
  historical or cross-platform paths; no env override here since a bulk
  import spans many projects
- parity tests for replay git-root resolution, watcher git-root and
  trim behavior
- stat-tests badge updated from 1428+ to 1550+ passing

* fix(cli): refuse second-instance boot over a live daemon

Closes the class behind issue 1140: agentmemory consolidate (or any
unrecognized word) fell through the command table into the full server
boot, registering a duplicate worker on the running engine; on iii
0.11.2 the second instance's shutdown tears down the daemon's HTTP
trigger routing until a full engine restart. Unknown subcommands now
error with the supported list, and main() probes livez on the resolved
port and refuses to boot over a live daemon, so multi-instance setups
on other ports are unaffected. Verified behaviorally against the built
CLI: both paths refuse with exit 1.

Also from review: the watcher stamps each event with its own root's
project via a per-root map (an explicit config.project still overrides
for every root), and replay only accepts a non-empty string cwd from
parsed JSONL so malformed entries cannot reach the filesystem probe.

* test(watcher): two-repository flush events scope to their own project

* chore(release): bump packages/mcp, guard it, refresh CONTRIBUTING

packages/mcp was still 0.9.28 after the release bump because nothing
guarded it; a consistency test now pins it to package.json. CONTRIBUTING
release list corrected to the files a bump actually touches (no tracked
lockfile, the two extra plugin manifests, the export test derives from
VERSION now), and the subsystems table gains src/cli, integrations/pi,
and the generated-manifest note.

* fix(export): refuse over-frame export instead of dropping the worker

Closes the availability bug in issue 1142: GET /agentmemory/export
assembles the full store and returns it through sdk.trigger, so a store
whose serialized export passes the engine's 16 MiB WebSocket frame
(tungstenite max_frame_size, not raisable under the 0.11.2 pin) dies on
the worker->engine hop, drops the worker, and 404s every endpoint for
~1s. The session collections page on maxSessions/offset but ~18 others
do not, so a large store hits this at any parameter combination.

A shared frame-guard measures the serialized size before returning:
mem::export returns a small oversized error instead of the giant
object, and api::mesh-export returns 413 (same dead-end as #890). Either
way the over-frame payload never crosses the boundary, so the daemon
stays up and the failure is one clean request with a hint to narrow the
range. Full pagination of the non-session collections is a follow-up.

Layer 1 of the fix; verified with a synthetic oversized export returning
the error object (tiny) rather than the payload.

* ci: collapse to a single npm install to fix Node 24/26 CI

The two-step install (npm install --package-lock-only then npm ci) failed
only on the Node 24/26 matrix rows: their stricter npm rejects rolldown's
optional platform bindings (@rolldown/binding-android-arm64) that a
--package-lock-only pass does not fully enumerate. Lockfiles are gitignored,
so npm ci re-validation buys no reproducibility here. A single lenient
npm install resolves and installs in one pass.

* fix(mesh): scope exported memories by project like actions

api::mesh-export filtered actions by ?project but returned every project's
memories. On a mesh instance federating one project to a peer, the peer
pulled other projects' memories (cross-project leak), and those extras could
push the payload past the 16 MiB transport frame into a 413 even when the
requested project's own slice fit. Memories carry the same optional project
field as actions, so filter both before the frame-size guard runs.

Adds a regression test asserting a project-scoped export excludes other
projects' memories and that an oversized memory in another project no longer
413s the scoped request.

* chore(release): credit the Antigravity native hooks adapter in 0.9.29 notes

* chore(release): sweep stale 0.9.28 refs for 0.9.29

Deploy Dockerfiles/compose/render pins, AGENTS.md stats header, opencode
plugin manifest, website meta snapshot, test-count claims (1,428 -> 1,596)
in README/AGENTS/stat SVGs, and the missing 0.9.29 CHANGELOG compare link.

* chore(release): sync stat-tests badge to 1596+ and commit bridge exec bit

* refactor: trim frame-guard comments and drop issue refs from code
2026-08-09 13:22:25 +01:00
..

OpenCode  agentmemory for OpenCode

Your OpenCode agents remember everything. No more re-explaining.
Persistent cross-session memory via agentmemory — 95.2% retrieval accuracy on LongMemEval-S.

54 MCP tools 22 hooks 2 slash commands 95.2% R@5


Quick start

1. Start the agentmemory server

npx @agentmemory/agentmemory

The server starts on http://localhost:3111.

2. Configure the MCP server

Add to ~/.config/opencode/opencode.json or your project's .opencode/opencode.json:

{
  "mcp": {
    "agentmemory": {
      "type": "local",
      "command": ["npx", "-y", "@agentmemory/mcp"],
      "enabled": true
    }
  }
}

3. Install the plugin

Add to ~/.config/opencode/opencode.json:

{
  "plugin": ["./plugins/agentmemory-capture.ts"]
}

Copy the plugin file from this repo:

mkdir -p ~/.config/opencode/plugins
cp plugin/opencode/agentmemory-capture.ts ~/.config/opencode/plugins/

4. Add the slash commands

Copy the commands into your project or global .opencode/commands/ directory:

mkdir -p ~/.config/opencode/commands
cp plugin/opencode/commands/recall.md ~/.config/opencode/commands/
cp plugin/opencode/commands/remember.md ~/.config/opencode/commands/

Restart OpenCode or open a new session. The plugin auto-captures everything.

What gets captured

Session lifecycle

Event Hook agentmemory API
Session start session.created POST /session/start
Idle → summarize session.idle + session.status (idle) POST /summarize
Status transitions session.status (idle/busy/retry) POST /observe
Compaction session.compacted POST /summarize + POST /observe
Metadata updates session.updated POST /observe
Code change tracking session.diff POST /observe
Session delete session.deleted POST /session/end
Session error session.error POST /observe

Messages & prompts

Event Hook agentmemory API
User prompt (rich) chat.message POST /observe
User prompt metadata message.updated (user) POST /observe
Assistant response message.updated (assistant) POST /observe
Message removed (undo) message.removed POST /observe

Parts & steps

Event Hook agentmemory API
Subagent start message.part.updated (subtask) POST /observe
Tool completed message.part.updated (tool completed) POST /observe
Tool error message.part.updated (tool error) POST /observe
Step finish (cost/tokens) message.part.updated (step-finish) POST /observe
Reasoning trace message.part.updated (reasoning) POST /observe
Patch applied message.part.updated (patch) POST /observe
Auto/manual compaction message.part.updated (compaction) POST /observe
Agent selection message.part.updated (agent) POST /observe
API retry message.part.updated (retry) POST /observe

File enrichment pipeline

Event Hook agentmemory API
File tool params tool.execute.before → stash paths
File edited file.edited → stash paths
File part attached message.part.updated (file) → stash paths
Enrichment inject experimental.chat.system.transform POST /enrich → output.system[]
Memory context inject experimental.chat.system.transform POST /context → output.system[]

Permissions

Event Hook agentmemory API
Permission prompt permission.updated POST /observe
Permission reply permission.replied POST /observe

Tasks & commands

Event Hook agentmemory API
Task tracking (w/ priority) todo.updated POST /observe
Command executed command.executed POST /observe

Model & config

Event Hook agentmemory API
LLM parameters chat.params POST /observe
Config loaded config POST /observe
Compaction (WIP) experimental.session.compacting POST /context → output.context[]

File enrichment + memory injection (two-layer pipeline)

experimental.chat.system.transform fires before every LLM call and injects two layers of context:

  1. Memory context (once per session): calls /agentmemory/context and injects project profile, recent session summaries, and important past observations into the system prompt. This is the OpenCode equivalent of Claude's MEMORY.md bridge — instead of syncing to a markdown file, context is injected directly into the system prompt.

  2. File enrichment (every turn with stashed files): calls /agentmemory/enrich with files stashed by tool.execute.before, file.edited, and message.part.updated (file parts). File-specific context (past observations, related bugs, semantic search) is injected into the system prompt.

System prompt = [OpenCode instructions] + [memory context] + [file enrichment] + [user message]
                                        ^                 ^
                               first turn only         every file-touching turn

Differences from Claude's PreToolUse:

Dimension Claude (PreToolUse) OpenCode (two-hop pipeline)
Injection mechanism stdout → context window output.system[] → system prompt
Timing Same turn (parallel with tool) Next turn (before next LLM call)
File set Per-tool (immediate) Batched (all files since last enrichment)
Coverage Edit/Write/Read/Glob/Grep only Edit/Write/Read/Glob/Grep only
What gets injected <agentmemory-file-context> + bug memories Identical /enrich response

MEMORY.md vs AGENTS.md: how context flows

Claude Code and OpenCode take fundamentally different approaches to injecting memory context into the agent's system prompt.

Claude Code: file-backed bridge (two-hop)

agentmemory  ──write──▶  MEMORY.md  ──read──▶  Claude system prompt
  • The claude-bridge/sync endpoint serializes agentmemory observations into a MEMORY.md file in the project root
  • Claude Code reads MEMORY.md on session start and prepends it to the system prompt
  • Sync is periodic — sessions only get fresh context when the bridge last ran (session end, pre-compact)
  • Coupling: memory data lives in a git-trackable file, visible to CI, team members, and other tools

OpenCode: direct injection (one-hop)

agentmemory  ──push──▶  OpenCode system prompt
  • experimental.chat.system.transform calls /context at runtime and pushes the response directly into output.system[]
  • Always current — context is fetched at session start (once) and before file-touching turns (per-batch)
  • No file intermediary — no stale copies, no merge conflicts, no disk I/O
  • AGENTS.md is a static instruction file for project conventions, coding standards, and tool guidance — agentmemory does not read or write it

Tradeoffs

Dimension Claude (MEMORY.md bridge) OpenCode (direct injection)
Freshness Stale between syncs Always current (fetched at call time)
Visibility Human-readable file in repo In-memory injection only
Simplicity Two moving parts (bridge + file) One step (API → system prompt)
Team sharing File is git-trackable, CI-friendly Memory shared via agentmemory server API
Integration Any tool can read MEMORY.md Requires OpenCode plugin SDK

Why OpenCode goes direct

agentmemory already persists everything in SQLite (data/state_store.db). Adding an intermediate MEMORY.md file would duplicate data, introduce sync lag, and require the model to re-parse structured context from markdown. Direct injection delivers the same data with lower latency and zero staleness — the agent always sees what agentmemory knows right now.

Slash commands

  • /recall <query> — Search past observations and lessons
  • /remember <text> — Save an insight to long-term memory

Session instruction injection

Agentmemory usage instructions are injected into the system prompt on the first turn of every session via experimental.chat.system.transform (alongside memory context from /context). This is functionally equivalent to Claude Code's skills mechanism — the agent learns which agentmemory_memory_* tools to use and when, without needing separate skill invocations.

What's not covered (vs Claude Code plugin)

Claude feature Reason
SubagentStop OpenCode's SubtaskPart type has no completion/result fields; subtask lifecycle ends are not exposed as distinct events in the OpenCode SDK
TaskCompleted No team/teammate concept in OpenCode; todo.updated captures task state changes as a partial equivalent
Stop session.compacted event handler exists; experimental.session.compacting injection hook defined in SDK but Go binary (v1.14.41) doesn't wire it — will auto-activate when upstream implements it
Skills (remember/recall/forget/session-history) Covered by injected system instructions via experimental.chat.system.transform — agent receives usage guidance on first turn
Consolidation pipeline (crystals/auto + consolidate-pipeline) Now called on session.deleted — mirrors Claude's CONSOLIDATION_ENABLED=true behavior
Claude MEMORY.md bridge OpenCode-specific; OpenCode uses its own AGENTS.md mechanism, not Claude's MEMORY.md

All other Claude Code hooks have direct or pipeline equivalents in this plugin. 12 of 12 Claude hook types covered.