This document explains the behavior and design of the maintained mem0.ts
OpenCode plugin template. See the README for copy installation.
The plugin injects a bounded set of durable memories only when a turn is likely
to benefit from them. It also exposes mem0 modes for add, search, list,
forget, and help, and can add project memory to session compaction.
Automatic recall, explicit search, and compaction call POST /retrieve.
Supersession detection during add alone calls POST /search. Requests are
retried and guarded by a circuit breaker, while retrieval failures leave chat
available and may reuse the last good chat context.
The plugin returns seven OpenCode surfaces:
chat.messagedetects remember/recall signals, first-turn and periodic refreshes, and topic shifts./retrieveowns result ranking and order; the plugin filters and deduplicates those results before bounded injection.tool.mem0exposes the five explicit modes.addmay includeanchororanchorContext, and safe Git context may be inferred when available.chat.paramsrecords provider parameter diagnostics only when prompt debug logging is enabled; it does not change the parameters.experimental.chat.messages.transformrecords summarized message diagnostics when prompt debug logging is enabled; it does not transform the messages.experimental.chat.system.transformrecords system-prompt diagnostics when prompt debug logging is enabled; it does not transform the system prompt.experimental.session.compactingretrieves up to eight project memories and either appends a context block or replaces the compaction prompt.eventarchives a completed compaction summary as cold memory when enabled and removes session-local state aftersession.deleted.
In practice, chat.message decides when automatic retrieval is worthwhile,
and tool.mem0 handles explicit operations. Compaction and event handling keep
durable context available across long sessions without injecting raw history.
The plugin uses POST /memories, POST /retrieve, GET /memories, and
DELETE /memories/{memory_id} for its main memory operations. Automatic recall,
explicit search, and compaction use POST /retrieve. During add, supersession
detection uses POST /search to find similar older memories; this is the only
plugin flow that calls /search. The Hippocampus server also offers get-by-ID,
update, history, delete-all, reset, query, and configure operations, but this
plugin does not expose or call those extra operations.
OpenCode sessions are excellent at short-term reasoning, but they need help with durable continuity across sessions.
This plugin adds a practical memory model:
- Tier 1 (Core): static guidance from
AGENTS.md(outside this plugin) - Tier 2 (Working): session-local, temporary memory control in plugin state
- Tier 3 (Long-term): searchable mem0 persistence
The core principle is: inject less, retrieve smarter, persist only high-signal facts the implementation of the mem0 plugin is inspired by the opencode-supermemory plugin.
The plugin keeps session-local state (SessionState) to avoid repeated noisy injections and to adapt retrieval timing.
Why:
- Without working state, memory injection repeats and token cost grows quickly.
- Session-local state lets us detect topic shifts and refresh only when useful.
- It keeps temporary coordination data out of long-term memory.
Key fields and why they matter:
turn: lets refresh policy be deterministiclastInjectionTurn: prevents over-injectiontopicSignature: enables topic-shift detectioninjectedMemoryIdsLRU: suppresses repeated memoriesworkingSet: tracks the current selected contextlastGoodContextSnippet: fallback when mem0 is temporarily unavailable
Retrieval is triggered on:
- first turn (optional)
- explicit recall intent
- periodic refresh (every N turns)
- topic shift
Why:
- Always retrieving on every turn is expensive and noisy.
- Never refreshing causes stale context in longer sessions.
- Triggered policy balances relevance and cost.
POST /retrieve ranks and orders the returned cross-scope candidates.
Normalization, filtering, deduplication, recent-injection suppression, and
budget selection preserve the candidates' relative order. Final context
presentation groups selected memories in fixed user, project, agent, and
environment scope order while preserving server order within each scope.
Why:
- Normalization rejects candidates that cannot or should not be injected.
- Dedupe protects context quality and reduces prompt bloat.
- Recent-injection suppression avoids repeating context within a session.
- Hard item and character budgets keep the final context bounded.
The plugin enforces char/token-like bounds and item count limits.
Why:
- Unlimited context growth degrades model quality and cost.
- Hard budgets make behavior predictable and tunable.
Writes include metadata such as created_at, last_used_at, access_count, fingerprint, decay_half_life_days, and tier.
Why:
- Long-term memory without lifecycle becomes stale and noisy.
- Metadata enables better ranking and future cleanup.
- Fingerprints support dedupe and supersession workflows.
New memories can mark similar old entries as superseded.
Why:
- Decisions evolve; old decisions should not be equally ranked forever.
- Supersession keeps history while reducing retrieval confusion.
The plugin retries mem0 calls, then opens a circuit breaker after repeated failures, and can inject last-known-good context.
Why:
- Memory backend instability should never break chat flow.
- Retries handle transient network errors.
- Breaker avoids repeated expensive failures.
- Fallback maintains continuity during outages.
Compaction summaries can be stored as cold context (inject: false).
Why:
- Full summaries are useful for audit/recovery.
- They are usually too broad for default prompt injection.
- Keeping them cold preserves recall without polluting active context.
The plugin intentionally stores only:
- decisions
- problems + fixes
- stable facts
- reusable procedures
Why:
- Raw transcripts degrade retrieval precision.
- High-signal filtering keeps memory useful over time.
- It reduces storage and retrieval cost.
This plugin is almost entirely tuned through environment variables. The list below reflects what the code actually reads today.
MEM0_SERVER_URL- Source fallback:
http://100.75.83.103:18000 - What it does: points the plugin at the mem0-compatible backend and strips any trailing
/characters. - Important: the fallback is an environment-specific address, not a portable localhost default. Set this variable explicitly to the running server's base URL.
- Source fallback:
Automatic recall sends one POST /retrieve request with the user, project,
agent, and environment scopes, the derived identifiers, limit: 10, and
filters.include_cold_context: false. Explicit search calls the same endpoint
for its selected scope. Retrieve responses preserve normalized
backend_capabilities, degraded, degradation_reasons, and request_id
diagnostics when the server supplies them.
Supersession detection is deliberately different. When add checks for
similar older memories, detectSupersedes uses the server's scope-specific
POST /search API. The current server API and plugin implementation require
that exception; no recall, explicit-search, or compaction flow uses /search.
-
MEM0_READ_TIMEOUT_MS- Default:
8000 - What it does: timeout for the supersession-detection
POST /searchrequest and allGETrequests. - Tradeoff: lower values fail faster; higher values tolerate slower backends.
- Default:
-
MEM0_WRITE_TIMEOUT_MS- Default:
45000 - What it does: timeout for all other requests, including
POST /memories,POST /retrieve, and deletes. Despite being a retrieval endpoint,/retrievecurrently uses this timeout. - Tradeoff: higher values are safer for slow persistence paths, but failures take longer to surface.
- Default:
-
MEM0_REFRESH_EVERY_TURNS- Default:
10 - What it does: if memory was previously injected, forces a periodic refresh after this many turns.
- Tradeoff: lower means fresher context but more retrieval overhead.
- Default:
-
MEM0_AUTO_RETRIEVE_FIRST_TURN- Default: enabled
- Disable with:
0 - What it does: controls whether the first user turn is allowed to trigger automatic retrieval.
- Tradeoff: disabling it reduces first-turn noise, but can delay useful context injection.
-
MEM0_MAX_INJECT_CHARS- Default:
2200 - What it does: hard cap for the total injected
[MEM0 CONTEXT]block. - Tradeoff: lower values reduce prompt cost; higher values preserve more retrieved memory.
- Default:
-
MEM0_MAX_RECENT_IDS- Default:
40 - What it does: size of the in-session LRU used to avoid reinjecting the same memories too often.
- Tradeoff: higher values reduce repetition across longer sessions but may suppress useful repeats longer.
- Default:
-
MEM0_SIMILARITY_DEDUPE_THRESHOLD- Default:
0.92 - What it does: threshold for near-duplicate suppression during injection selection.
- Tradeoff: lower values dedupe more aggressively; higher values allow more similar memories through.
- Default:
-
MEM0_SUPERSEDES_THRESHOLD- Default:
0.88 - What it does: threshold used when deciding whether a newly saved memory supersedes older similar ones.
- Tradeoff: lower values create supersession links more often; higher values are more conservative.
- Default:
-
MEM0_AUTO_ANCHOR_CONTEXT- Default:
safe - Disable with:
offor0 - What it does: enables best-effort automatic Git-derived anchor context for writes when enough repository metadata is available.
- Notes: this uses repo/commit/ref information and only activates when the plugin can safely infer it.
- Default:
-
MEM0_USER_ID- Default: unset
- What it does: explicit stable user identifier for memory scoping.
- Priority: preferred over
OPENCODE_USER_ID.
-
OPENCODE_USER_ID- Default: unset
- What it does: fallback explicit user identifier when
MEM0_USER_IDis not set.
-
USER/USERNAME- Default: inherited from the shell/OS if present
- What they do: last-resort inputs for deriving a stable fallback user identity when neither
MEM0_USER_IDnorOPENCODE_USER_IDis set. - Important: these are not plugin-specific knobs, but they do affect identity fallback behavior.
MEM0_COMPACTION_MODE- Default:
append - Accepted values:
replaceor anything else, which behaves asappend - What it does: retrieves project memory with the fixed query
recent decisions fixes constraints proceduresand controls whether that memory is appended to context or placed in a replacement prompt. - Use
replacewhen: you want deterministic compaction output that always includes the Mem0 memory section.
- Default:
Compaction uses identifiers derived from the active session directory and
sends one POST /retrieve request for at most eight results. The request body
uses scopes: ["project"], limit: 8, and
filters.include_cold_context: false.
On success, append mode adds a memory block only when normalized memories exist and otherwise changes nothing. Replace mode always sets the Mem0-aware prompt, including a no-verified-memory statement when the result is empty. After an exhausted retrieval failure, append mode preserves both existing context and prompt; replace mode sets the same no-verified-memory fallback prompt.
-
MEM0_SAVE_COLD_COMPACTION- Default: disabled
- Enable with:
1 - What it does: after compaction, archives the generated summary back into mem0 as cold context with
inject: false. - Why this matters: it preserves long-session summaries for later retrieval without automatically polluting prompt injection.
-
MEM0_COLD_MAX_CHARS- Default:
6000 - What it does: max size of the stored compaction summary before it is truncated for cold archival.
- Tradeoff: higher values preserve more of the summary but store more broad context.
- Default:
-
MEM0_LOG_INJECTION- Default: disabled
- Enable with:
1 - What it does: emits injection-related lifecycle/debug events, including app log writes and NDJSON debug entries.
- Use this when: you need operational visibility into when and why memory was injected.
-
MEM0_LOG_INJECTION_CONTENT- Default: disabled
- Enable with:
1 - What it does: includes a preview of injected content in logs.
- Tradeoff: useful for debugging, but increases verbosity and may expose more prompt content than you want.
-
MEM0_DEBUG_PROMPTS- Default: disabled
- Enable with:
1 - What it does: turns on prompt/debug-file lifecycle logging even if normal injection logging is off.
-
MEM0_DEBUG_PROMPT_CONTENT- Default: disabled
- Enable with:
1 - What it does: includes sanitized prompt text in debug output.
- Notes: content is passed through the plugin's private-content redaction before logging and then truncated by
MEM0_DEBUG_MAX_CHARS.
-
MEM0_DEBUG_LOG_PATH- Default:
/tmp/opencode-mem0.ndjson - What it does: file path for NDJSON debug events written by the plugin.
- Change this when: you want logs somewhere persistent or project-local.
- Default:
-
MEM0_DEBUG_MAX_CHARS- Default:
4000 - What it does: upper bound for debug-text payloads written to the NDJSON log.
- Tradeoff: lower values reduce leakage and file size; higher values preserve more evidence for debugging.
- Default:
-
MEM0_BREAKER_THRESHOLD- Default:
3 - What it does: number of failed mem0 request cycles before the circuit breaker opens.
- Tradeoff: lower values stop repeated failures sooner; higher values retry longer before backing off.
- Default:
-
MEM0_BREAKER_COOLDOWN_MS- Default:
20000 - What it does: how long the breaker stays open before requests are allowed again.
- Tradeoff: higher values protect the backend more; lower values retry recovery sooner.
- Default:
- If context feels stale, reduce
MEM0_REFRESH_EVERY_TURNSor keep first-turn retrieval enabled. - If prompt cost or noise is too high, lower
MEM0_MAX_INJECT_CHARSand/or lowerMEM0_SIMILARITY_DEDUPE_THRESHOLDto dedupe harder. - If your backend is slow or flaky, increase timeouts and/or tune
MEM0_BREAKER_THRESHOLDplusMEM0_BREAKER_COOLDOWN_MS. - If you are validating behavior, turn on
MEM0_LOG_INJECTIONfirst; only enable content logging when you actually need payload-level evidence.
When mem0 is unavailable:
- the plugin attempts each request up to three times
- the circuit breaker may open temporarily after failed request cycles
- chat continues without a memory-backend exception
- automatic chat retrieval may inject the session's last good context snippet
- explicit tool modes return a structured failure instead of throwing to chat
- compaction follows the append/replace failure behavior described above
Why:
- Availability of conversation flow is prioritized over memory freshness.
Near-term improvements should prioritize:
- better confidence-based retrieval gating
- stronger supersession semantics (active/inactive memory views)
- periodic lifecycle maintenance (demotion/expiry)
- evaluation of memory precision and recall quality
Why:
- These improve memory quality without increasing prompt size.