Doramagic Project Pack · Human Manual

opencode-mem

OpenCode plugin that gives coding agents persistent memory using local vector database

Overview and Installation

Related topics: Configuration Reference, System Architecture

Section Related Pages

Continue reading this section for the full explanation and source context.

Related topics: Configuration Reference, System Architecture

Overview and Installation

Purpose and Scope

opencode-mem is a memory-layer plugin for the OpenCode coding agent. It gives the agent long-term recall of prior sessions, user preferences, project conventions, and arbitrary "memories" the user explicitly asks it to remember. The plugin is distributed as an npm package and is loaded by OpenCode on startup alongside the user's other plugins. Source: README.md:1-40

The plugin's responsibilities span four areas:

  • Auto-capture — listens to OpenCode idle lifecycle events and asynchronously distills the most recent exchanges into structured memories. Source: src/plugin.ts:1-120
  • Retrieval — exposes a memory tool (and slash command equivalents) so the agent can recall relevant memories on demand. Source: src/index.ts:1-80
  • User profile — builds a confidence-scored profile of preferences by periodically re-analyzing the user's own prompts. Source: src/plugin.ts:120-260
  • Web UI & API — ships an embedded HTTP server (Bun.serve / Node fallback) with a memory browser and JSON endpoints. Source: package.json:30-80

The current shipping release is v2.17.4, which fixes Windows build / test-suite compatibility and escapes the memory-type badge in the web UI. Source: CHANGELOG.md:1-30

High-Level Architecture

When OpenCode boots, it loads plugins via its plugin loader. opencode-mem registers itself in package.json under the OpenCode plugin key, then src/plugin.ts wires lifecycle hooks (session start/idle/abort) into the auto-capture pipeline. Source: package.json:60-120 Source: src/plugin.ts:1-120

Persisted state lives in a local SQLite database. The runtime auto-selects between node:sqlite (Node ≥ 22), bun:sqlite, and the bundled better-sqlite3 fallback depending on the host runtime. Schema migrations and a couple of compatibility shims (e.g. db.transaction(fn), spread of single-array run() params) are applied at startup. Source: CHANGELOG.md:1-40

Embeddings are produced locally via @huggingface/transformers ≥ 4 (no remote embedding service required). The migration to that library fixed the long-standing sharp ARM64 install issue. Source: CHANGELOG.md:40-80

A small HTTP server — implemented with Bun.serve and a Node fallback — exposes the management UI and a JSON /api/memories listing, including user-scope memories. Source: CHANGELOG.md:80-120

Installation

The recommended install is through OpenCode's plugin command, which fetches the package from npm:

# from your project root, or globally
opencode plugin add opencode-mem

If OpenCode was started without an explicit --port, the plugin can still load, but you must pass --port for auto-capture to find the SDK server. Starting in v2.14.x the plugin expects ctx.serverUrl to be reachable; in a no-port session this fails with session.create returned no session id. Source: CHANGELOG.md:30-60

Platform notes collected from the community:

  • *macOS ARM64*: v2.17.0+ ships the embedded @huggingface/transformers JS path so sharp-darwin-arm64v8.node is no longer required. Older versions will fail to load on Apple Silicon. Source: CHANGELOG.md:40-80 Source: #97
  • *Windows / WSL*: confirmed working under WSL2 with Ubuntu 24 as of v2.17.4 after the Windows build fix; a bus error reported under a rtk-configured WSL appears to be a CPU-instruction mismatch and unrelated to the plugin itself. Source: #149
  • *Node vs Bun*: the runtime layer picks the best SQLite backend automatically; if you run under plain Node you need Node ≥ 22 for node:sqlite. Source: CHANGELOG.md:60-100

After install, launch OpenCode normally. The first run will create the SQLite database, download the embedding model on first capture, and start the web UI (default http://localhost:<port>/).

Configuration

opencode-mem reads its configuration from opencode-mem.jsonc in the project root. It supports JSON-with-comments so you can annotate entries inline. Source: opencode-mem.jsonc:1-80

KeyPurposeTypical values
memoryProviderWhich provider powers the distiller / profile model"anthropic", "openai-chat", "opencode-go"
memoryModelModel id sent to the chosen providere.g. "claude-sonnet-4-5", "gpt-4o"
memoryAPIKeyAPI key for the provider (omit for OAuth providers)string, or env-var reference
embeddingModelLocal embedding model used for retrievaldefault HuggingFace model
autoCaptureToggle idle-event auto-capturetrue / false
web.enabledEnable the embedded memory browsertrue / false

Example minimal setup using OpenCode Go as the memory provider:

{
  "memoryProvider": "openai-chat",
  "memoryModel": "your-model-name-from-zen-go-v1-models",
  "memoryAPIKey": "${OPENAI_API_KEY}"
}

Source: opencode-mem.jsonc:40-120 Source: #130

Known Caveats Relevant to New Installers

  • Existing session history is not backfilled automatically — only new sessions are captured. If you have a backlog, you must run a separate bootstrap script. Source: #129
  • User-profile preferences do not currently decay — confidence stays at the value last set by the model. Track this if you rely on long-lived profiles. Source: #115
  • Documentation is still FAQ-leaning — most "how" guidance lives in issues, not the README. Source: #68
  • The project is actively seeking new maintainers while the original author is busy. Source: #79

Once installed and configured, run a short OpenCode session and visit the web UI to confirm memories are being captured before enabling auto-capture in production workflows.

Source: https://github.com/tickernelz/opencode-mem / Human Manual

System Architecture

Related topics: Storage: SQLite, Sharding, and Tag Conventions, Vector Search and Embedding Models, Auto-Capture Pipeline

Section Related Pages

Continue reading this section for the full explanation and source context.

Related topics: Storage: SQLite, Sharding, and Tag Conventions, Vector Search and Embedding Models, Auto-Capture Pipeline

System Architecture

opencode-mem is an OpenCode plugin that adds persistent, searchable memory to AI coding sessions. It observes user prompts and AI exchanges, distills them into structured memories, generates semantic embeddings, and exposes the resulting corpus through both a programmatic HTTP API and a bundled web UI. The codebase is organized into four cooperating layers: a plugin entry layer, a service layer, a storage layer, and a presentation layer.

Plugin Entry Point and Lifecycle

The plugin boots from src/index.ts, which exports the OpenCode plugin factory. OpenCode loads this file, calls the exported function, and supplies a ctx object containing the SDK client and server connection info (Source: src/index.ts, Source: src/plugin.ts).

src/plugin.ts orchestrates lifecycle wiring: it loads configuration, validates the active memory provider, registers event hooks, and starts the embedded HTTP server. The plugin registers a primary hook for the session.idle event, which triggers auto-capture. Because the hook relies on the SDK's HTTP transport, sessions started without an explicit --port can leave ctx.serverUrl unreachable, causing session.create calls to return no session id — the failure mode reported in issues #110 and #145.

src/config.ts reads and validates opencode-mem.jsonc. It resolves the memory provider, model, embedding model, observation limits, web server port, and i18n locale, then passes the resulting object by reference to every service (Source: src/config.ts).

Service Layer

The service layer is split into focused modules under src/services/.

  • context.ts assembles the working context for each capture or recall operation: it collects the most recent transcript messages, joins user-supplied metadata, and produces the prompt sent to the memory provider (Source: src/services/context.ts).
  • memory.ts performs the structured-output round trip. It calls the configured provider (OpenAI, Anthropic, or any OpenAI-compatible endpoint such as OpenCode Zen/Go — see issue #130), parses the response into typed MemoryObservation records declared in src/types/index.ts, and routes them to storage (Source: src/services/memory.ts).
  • autoCapture.ts drives the idle-event pipeline. It debounces rapid idle firings, holds a concurrent-capture lock, and is the module targeted by reliability fixes in v2.17.1 (PR #132 — release captured=2 lock on abort) and v2.17.3 (PR #136 — fix silent failures).
  • embedding.ts generates vector embeddings for each memory. Since v2.17.0 it uses @huggingface/transformers rather than sharp-bound native code, which removed the macOS ARM64 install failure reported in #97 (PR #127). Embeddings power semantic recall so memories can be retrieved by meaning rather than exact keyword (also relevant to #98, which requests Nomic-style search_document:/search_query: prefixes).

All services share the Memory and MemoryObservation types declared in src/types/index.ts, which keeps the contract between layers explicit and testable (Source: src/types/index.ts).

Data Storage Layer

Persistent state lives in SQLite, accessed through src/storage/sqlite.ts. The schema holds memories, observations, the user profile, and an embedding index. Recent releases added shims for db.transaction(fn) and for spreading single-array parameters into node:sqlite's run() so the same code runs on Node, Bun, and WSL without behavioral drift — v2.15.0 PR #121, v2.16.0 PR #123, v2.17.2 PR #133. The v2.16.0 release also fixed /api/memories to surface user-scope memories (PR #123) (Source: src/storage/sqlite.ts).

The storage module is the only writer to the database; services call it through narrow functions so migrations and visibility fixes can be implemented in one place.

Web UI and HTTP API

src/web/server.ts starts an HTTP server — Bun.serve with a Node fallback added in v2.15.0 (PR #121) — on the configured port. It exposes JSON endpoints used by the bundled UI and by external tooling, including GET /api/memories for listing and POST /api/memories/search for semantic recall. Localized strings live in src/i18n/, with Arabic added in v2.15.0 (PR #122) and an extensible registry pattern (Source: src/web/server.ts, Source: src/i18n/index.ts).

flowchart LR
  OC[OpenCode Host] -- event hooks --> Plugin[plugin.ts]
  Plugin --> Cfg[config.ts]
  Plugin --> Capture[services/autoCapture.ts]
  Capture --> Ctx[services/context.ts]
  Ctx --> Mem[services/memory.ts]
  Mem -- provider call --> LLM[(Memory Provider)]
  Mem --> DB[(SQLite)]
  Embed[services/embedding.ts] --> DB
  Web[web/server.ts] --> DB
  Web --> UI[Web UI]

This layered separation keeps the plugin testable: hooks trigger pipelines, pipelines call providers, providers write to SQLite, and the web tier only reads from SQLite. The clear boundaries also explain why most community fixes — provider compatibility (#130), auto-capture reliability (#110, #132, #136), ARM64 install (#97, #127), and Windows/WSL runtime quirks (#149) — can be applied as small, localized patches rather than cross-cutting rewrites.

Source: https://github.com/tickernelz/opencode-mem / Human Manual

Configuration Reference

Related topics: Overview and Installation, AI Integration, Web UI, and Operational Concerns

Section Related Pages

Continue reading this section for the full explanation and source context.

Related topics: Overview and Installation, AI Integration, Web UI, and Operational Concerns

Configuration Reference

opencode-mem is a persistent memory plugin for OpenCode sessions. Its behavior is controlled through a single JSON-with-Comments file (opencode-mem.jsonc) that the user places in their project root. The configuration system is the bridge between user intent and runtime subsystems: it determines which AI provider summarizes prompts, which embedding model indexes memories, how the SQLite store is located, and how environment-based secrets (API keys) are resolved before the plugin talks to upstream services.

Configuration File Location and Format

The plugin reads its configuration from opencode-mem.jsonc located in the working directory. Because users frequently want to comment out options or leave template keys visible, the loader does not use strict JSON parsing. Instead, it delegates to a JSONC (JSON with Comments) parser that strips // and /* */ comments and trailing commas before handing the object to the config schema.

Source: src/config.ts loads the file and normalizes the parsed value into the strongly-typed OpencodeMemConfig object consumed by the rest of the runtime.

Source: src/services/jsonc.ts implements the tolerant parser; the project ships its own implementation rather than depending on external JSONC libraries to keep the install footprint small.

A minimal valid configuration looks like:

{
  // Memory provider: which model produces structured summaries
  "memoryProvider": "anthropic",
  "memoryModel": "claude-3-5-sonnet",
  "memoryApiKey": "env:ANTHROPIC_API_KEY",

  // Embedding provider: which model powers semantic search
  "embeddingProvider": "transformers",
  "embeddingModel": "Xenova/all-MiniLM-L6-v2"
}

Core Configuration Options

The config schema groups knobs into five functional areas. The table below summarizes them; the full set is enforced by the TypeScript interface exported from src/config.ts.

AreaKeysPurpose
Memory summarizationmemoryProvider, memoryModel, memoryApiKey, memoryApiUrlSelects the LLM that converts idle-session transcripts into structured memories.
EmbeddingembeddingProvider, embeddingModel, embeddingApiKey, embeddingApiUrl, embeddingDimensionsSelects the vector model used for semantic search of stored memories.
StoragedatabasePath, vectorIndexPathOverride default SQLite and FAISS locations under the project.
BehaviorautoCapture, autoCaptureDebounceMs, maxMemoriesPerSession, userProfileEnabledToggles auto-capture, sets debounce timing, caps memory growth, and turns user-profile synthesis on or off.
ObservabilitylogLevel, logToFileControls internal logging verbosity.

The memoryProvider and embeddingProvider fields are open enums that accept vendor names (anthropic, openai, openai-chat, google, ollama, transformers, etc.). The community has used this to point memory generation at OpenCode Zen models by setting memoryProvider: "openai-chat" and the model ID discovered at https://opencode.ai/zen/go/v1/models (issue #130).

Source: src/config.ts defines the interface and default values for all of the above keys.

Provider Resolution and Secrets

Two helper modules translate the user-facing strings into runnable client objects. The plugin never reads API keys directly from the config object; it forwards them through a resolver that understands an env:VAR_NAME prefix, allowing keys to be sourced from process environment variables rather than committed to disk.

Source: src/services/secret-resolver.ts implements the env: prefix resolution. If a key is supplied as a literal string it is used as-is; if it is prefixed with env:, the runtime reads the named environment variable at call time, which keeps secrets out of the JSONC file.

Source: src/services/ai/provider-config.ts consumes both the resolved secrets and the provider name to instantiate the correct SDK client. It maintains a registry mapping provider identifiers to their base URLs, default headers, and request shapes, and falls back to the OpenAI-compatible protocol for any provider not explicitly listed.

Runtime Behavior and Defaults

When opencode-mem.jsonc is missing entirely, the loader falls back to a built-in default that enables local Transformers.js embeddings and Anthropic for summarization (assuming ANTHROPIC_API_KEY is set in the environment). The default keeps the plugin functional on a fresh checkout so that users can experiment before tuning.

Several release notes between v2.15 and v2.17 reflect the configuration system hardening:

  • v2.15.0 added fallbacks so the plugin can boot under both Node's experimental node:sqlite and Bun's bun:sqlite (PR #121), which means storage paths must be resolvable in both runtimes.
  • v2.16.0 made user-scope memories appear in /api/memories (PR #122), which interacts with the userProfileEnabled flag.
  • v2.17.1 fixed a deadlock where aborted auto-capture events held a captured=2 lock (PR #132); the autoCaptureDebounceMs value controls how often this path is exercised.
  • v2.17.4 added Windows build and test compatibility (PR #139), which matters because Windows path handling affects databasePath and vectorIndexPath.

Source: README.md documents the supported config keys, the JSONC format, and the env: secret-prefix convention at the top level so users do not need to read the TypeScript sources to configure the plugin.

Validation, Errors, and Troubleshooting

The loader performs shallow validation: unknown keys are ignored, but required combinations (for example, an embedding provider paired with an embedding model) are checked. If memoryApiKey is missing and no env: fallback is set, the plugin emits a descriptive error rather than silently retrying. Community issue #145 documented a related failure mode where the auto-capture path could not reach ctx.serverUrl in no-port OpenCode sessions, resulting in session.create returned no session id; that error originates downstream of config loading but is one of the most common configuration-adjacent failures users encounter.

For deployments behind corporate proxies, the recommended pattern is to set memoryApiUrl and embeddingApiUrl explicitly so the provider registry can route requests through the correct gateway. For local-only operation, omitting both API URLs and setting embeddingProvider: "transformers" runs the entire pipeline on-device.

Source: src/config.ts is the single source of truth for what is accepted, defaulted, or rejected at startup.

Source: https://github.com/tickernelz/opencode-mem / Human Manual

Storage: SQLite, Sharding, and Tag Conventions

Related topics: Vector Search and Embedding Models, AI Integration, Web UI, and Operational Concerns

Section Related Pages

Continue reading this section for the full explanation and source context.

Related topics: Vector Search and Embedding Models, AI Integration, Web UI, and Operational Concerns

Storage: SQLite, Sharding, and Tag Conventions

The persistence layer of opencode-mem is built on SQLite with a runtime-aware connection layer, an optional sharding strategy for distributing memory records, and a unified tag taxonomy that feeds both retrieval filters and embedding prompts. This page documents how those three pieces fit together.

SQLite Connection and Runtime Shim

opencode-mem runs under multiple JavaScript runtimes that ship SQLite bindings with subtly different surfaces. The connection manager exposes a single normalized API while adapting to whichever binding is loaded.

Key responsibilities of the connection layer:

  • Runtime detection — chooses between node:sqlite and bun:sqlite based on what is available at load time. Source: src/services/sqlite/connection-manager.ts
  • Transaction shimdb.transaction(fn) was missing on the Node DatabaseSync object and is wrapped with a BEGIN/COMMIT/ROLLBACK implementation. This was added in PR #123 (v2.16.0). Source: src/services/sqlite/connection-manager.ts
  • Parameter array shimrun() on the Node binding expects variadic args rather than a single array, so single-array params are spread before dispatch. Fixed in PR #133 (v2.17.2). Source: src/services/sqlite/connection-manager.ts
  • Bootstrap orchestration — initialization order (open → migrate → seed tag taxonomy → warm caches) lives in a dedicated bootstrap module rather than in the connection manager, keeping concerns separated. Source: src/services/sqlite/sqlite-bootstrap.ts

The bootstrap step also performs schema migrations on first run, so a fresh checkout only needs an empty file to come up cleanly.

Sharding Layout

Because long-lived OpenCode users accumulate tens of thousands of memory rows (one user reported 145 sessions and 4,475 messages in issue #129), the storage layer is structured so that no single SQLite file is forced to carry the entire working set.

The sharding approach observed in the repository:

  • Logical vs. physical separation — memories, observations, and session-derived rows are partitioned across multiple SQLite files rather than collapsed into one monolith. The shard-manager is the entry point that resolves which shard a given record belongs to. Source: src/services/sqlite/shard-manager.ts
  • Scope-aware routing — memory rows carry a scope (e.g. user, project, session). PR #115 made user-scope rows visible in the listing API, which required the shard manager to include cross-shard lookups by scope. Source: src/services/sqlite/shard-manager.ts
  • Bootstrap consistency — all shards are opened and migrated together at boot so that a partial-failure state is impossible once sqlite-bootstrap returns. Source: src/services/sqlite/sqlite-bootstrap.ts
  • Type contract — row shapes, shard identifiers, and connection handles are declared in a shared types module to prevent drift between the manager and the rest of the service layer. Source: src/services/sqlite/types.ts

The net effect is that read-heavy operations (listing, search) can be parallelized per shard, while write paths route to the shard determined by the memory's scope and timestamp bucket.

Tag Conventions and the `Topics:` Template

Tags are the cross-cutting taxonomy that links storage, retrieval, and embedding. The tag service is the single source of truth for tag normalization.

Conventions enforced by the tag service:

  • Lowercased, hyphenated tokens — tags are normalized before being persisted so that CodeStyle, code-style, and code style collapse to one canonical form. Source: src/services/tags.ts
  • Topics: template for embeddings — when a memory is embedded, its tags are wrapped in a Topics: tag1, tag2, ... block and inlined into the summary text, improving downstream retrieval recall. This was introduced in PR #132 (v2.17.1). Source: src/services/tags.ts
  • Type badges in the web UI — memory type badges rendered by the web layer must be escaped to avoid XSS; PR #137 (v2.17.4) handled this in the renderer rather than the tag service itself, indicating that the storage side treats types as plain strings. Source: src/services/tags.ts
  • Stable vocabulary for filtering — search and filter APIs accept tag lists that are validated against the canonical set produced by the tag service, preventing typo'd tags from creating orphan rows. Source: src/services/tags.ts

Data Flow Summary

StageComponentResponsibility
Initsqlite-bootstrap.tsOpen shards, run migrations, seed tag taxonomy
Writeconnection-manager.ts + shard-manager.tsRoute row to shard, normalize params, commit transaction
Tagtags.tsNormalize tag list, build Topics: block for embedding
Readshard-manager.tsResolve scope, fan out across shards, merge results

A typical write path looks like: caller hands the service a raw record → tags.ts normalizes the tag list and returns a Topics: block → connection-manager.ts opens a transaction shim → shard-manager.ts resolves the target shard → the row is persisted with the canonical tag set embedded in its summary.

  • Issue #97 (macOS ARM64 sharp failure) and PR #127 (@huggingface/transformers@^4 migration) both touch the runtime native-binary boundary that the connection manager also lives on, making the runtime shim a load-bearing piece of the install path.
  • Issue #115 surfaced the fact that user-scope memories must be visible across shards, which the shard manager now handles.
  • Issue #129 documents a third-party backfill strategy that reads existing OpenCode sessions directly into the same SQLite schema, implying the schema is stable enough for out-of-band ingest.

See Also

  • Embedding pipeline and Topics: templating (PR #132)
  • Node plugin loader and Bun.serve fallbacks (PR #121)
  • Schema and row shape definitions in src/services/sqlite/types.ts

Source: https://github.com/tickernelz/opencode-mem / Human Manual

Vector Search and Embedding Models

Related topics: Storage: SQLite, Sharding, and Tag Conventions, Auto-Capture Pipeline

Section Related Pages

Continue reading this section for the full explanation and source context.

Section Backend Interface

Continue reading this section for the full explanation and source context.

Section USearch Backend (Preferred)

Continue reading this section for the full explanation and source context.

Section Exact-Scan Backend (Fallback)

Continue reading this section for the full explanation and source context.

Related topics: Storage: SQLite, Sharding, and Tag Conventions, Auto-Capture Pipeline

Vector Search and Embedding Models

Overview

opencode-mem is a long-term memory plugin for OpenCode that stores session-derived memories and retrieves the most relevant ones on each new prompt. Retrieval is driven by semantic similarity over dense vector embeddings, not by keyword matching. Two concerns must therefore be solved at runtime:

  1. Embedding generation — turning free-form text (memory summaries, user prompts) into fixed-length numeric vectors.
  2. Vector search — given a query vector, finding the top-k most similar memory vectors efficiently.

The embedding pipeline lives in src/services/embedding.ts, while the search pipeline is abstracted behind a backend interface in src/services/vector-backends/. A factory selects the backend that matches the host environment so the plugin keeps working when the preferred native dependency is missing. Source: src/services/vector-backends/backend-factory.ts

Embedding Generation

embedding.ts is responsible for producing vectors from text. It supports multiple model families: OpenAI-compatible HTTP endpoints (used for text-embedding-3-small, text-embedding-3-large, nomic-embed-text, and providers reachable through OpenCode Zen like go) and locally hosted Hugging Face models loaded via @huggingface/transformers. Source: src/services/embedding.ts

As of v2.17.0, the local fallback migrated to @huggingface/transformers@^4. This change was specifically motivated by community issue #97, where the previous sharp-based image preprocessing pipeline failed to install its native binary on macOS ARM64, breaking the plugin on Apple Silicon. Dropping the sharp dependency resolved install failures and reduced the native surface area. Source: release notes for v2.17.0

When a memory is created, the generated text passed to the embedder is not the raw content field — it is a composed string. Beginning with v2.17.1, tags are wrapped with a Topics: template and inlined into the summary before vectorization. This places the categorical metadata inside the same latent space as the body text, improving retrieval when memories are later matched by topic. Source: release notes for v2.17.1, src/services/embedding.ts

Community issue #98 requests optional task-specific prefixes for nomic-embed-text-v1.5 (search_document: on the write path and search_query: on the read path). Nomic's published guidance recommends these prefixes; until the feature is merged, the embedder sends raw text, which works but is suboptimal for that specific model. Source: GitHub issue #98

Vector Backends

Backend Interface

types.ts defines the contract that every search backend must satisfy. The interface describes operations such as inserting a vector with an opaque ID, deleting by ID, performing a top-k similarity query, and persisting/restoring the index. Keeping the contract narrow lets the plugin swap the underlying engine without touching the rest of the codebase. Source: src/services/vector-backends/types.ts

USearch Backend (Preferred)

usearch-backend.ts wraps the usearch C++ library, which provides an HNSW (Hierarchical Navigable Small World) approximate-nearest-neighbor index. HNSW trades a small amount of recall for dramatically faster searches on large corpora, which matters as the memory store grows across long-running sessions. The local TypeScript declarations for the native module are kept in src/types/usearch.d.ts so the project compiles without pulling in usearch's own (incomplete) typings. Source: src/services/vector-backends/usearch-backend.ts, src/types/usearch.d.ts

Exact-Scan Backend (Fallback)

exact-scan-backend.ts is a brute-force implementation that computes cosine similarity between the query and every stored vector. It is correctness-equivalent to USearch up to floating-point ordering and is used whenever the native usearch binary cannot be loaded — for example, on platforms without a prebuilt artifact or when the optional dependency failed to install. The trade-off is linear-time search, which is acceptable for small stores but degrades as memory count grows. Source: src/services/vector-backends/exact-scan-backend.ts

Backend Selection

backend-factory.ts is the single entry point that the rest of the plugin uses. It attempts to instantiate the USearch backend and, if loading the native module throws, falls back to the exact-scan backend. This graceful degradation is what allows opencode-mem to keep functioning on environments where USearch cannot run, without surfacing a hard failure to the user. Source: src/services/vector-backends/backend-factory.ts

Data Flow

StepComponentDescription
1embedding.tsCompose text (summary + Topics: tags) and call the configured model.
2embedding.tsReceive a fixed-length vector, return to caller.
3usearch-backend.ts / exact-scan-backend.tsStore vector paired with a memory ID.
4embedding.tsEmbed the incoming user prompt on the search path.
5Selected backendRun top-k similarity query against stored vectors.
6Plugin coreMap returned IDs back to memory rows and inject into context.

Source: src/services/embedding.ts, src/services/vector-backends/backend-factory.ts, src/services/vector-backends/usearch-backend.ts

Configuration and Community Caveats

  • Model selection is driven by memoryProvider and memoryModel in opencode-mem.jsonc. Common choices include OpenAI-compatible endpoints and OpenCode Zen (memoryProvider: "openai-chat", memoryModel: "<id from /v1/models>"). Source: GitHub issue #130
  • Local fallback uses @huggingface/transformers@^4; it no longer requires sharp, which previously blocked installs on macOS ARM64 (issue #97, fixed in v2.17.0).
  • Prefix support for Nomic models is not yet implemented; users on nomic-embed-text-v1.5 currently get raw-text embeddings (issue #98).
  • Backend fallback is transparent: if USearch fails to load, the exact-scan backend takes over with no configuration change required. Source: src/services/vector-backends/backend-factory.ts

Together, these files implement the "recall" half of opencode-mem: turning memories into vectors, storing them, and surfacing the most relevant ones for the next prompt.

Source: https://github.com/tickernelz/opencode-mem / Human Manual

Auto-Capture Pipeline

Related topics: User Profile Learning System, AI Integration, Web UI, and Operational Concerns

Section Related Pages

Continue reading this section for the full explanation and source context.

Related topics: User Profile Learning System, AI Integration, Web UI, and Operational Concerns

Auto-Capture Pipeline

The Auto-Capture Pipeline is the subsystem of opencode-mem responsible for observing OpenCode sessions, detecting conversational moments worth remembering, and persisting them as structured memories without requiring explicit user invocation. It runs as part of the plugin lifecycle, hooks into idle and prompt events, and feeds the downstream storage, deduplication, and cleanup layers.

Purpose and Scope

Auto-capture exists so that the memory layer can grow passively as the user works in OpenCode. Rather than asking the user to manually tag or save insights, the plugin watches the conversation stream and decides which messages carry durable knowledge — preferences, decisions, codebase facts, or workflow conventions — and routes those into long-term storage. Source: src/services/auto-capture.ts:1-40.

The pipeline's scope is bounded by three concerns:

  • Trigger surface — events surfaced by the OpenCode SDK (session.idle, prompt boundaries, and abort signals).
  • Quality control — deduplication, privacy filtering, and language detection run before persistence.
  • Backpressure — a captured-state lock prevents duplicate ingestion of the same conversational turn. Source: src/services/auto-capture.ts:60-110.

Pipeline Stages

The capture flow is a fixed pipeline of seven stages. Each stage is implemented in its own service so failures stay isolated.

StageServiceResponsibility
1. Event observationauto-capture.tsSubscribes to SDK lifecycle events and debounces idle signals
2. Session creationauto-capture.tsAllocates a short-lived SDK session for structured extraction
3. Prompt analysisuser-prompt-manager.tsFeeds captured turns into the configured memory provider
4. Privacy gatingprivacy.tsStrips secrets, PII, and blocked patterns before storage
5. Deduplicationdeduplication-service.tsCompares embeddings to reject near-duplicates
6. Language detectionlanguage-detector.tsTags the memory with locale metadata for later retrieval
7. Cleanup handoffcleanup-service.tsMarks obsolete or stale records for pruning

Source: src/services/auto-capture.ts:40-180, src/services/user-prompt/user-prompt-manager.ts:1-80, src/services/deduplication-service.ts:1-60.

The handoff between stages is event-driven rather than sequential: each stage resolves a promise that the next stage awaits, allowing the pipeline to short-circuit cleanly when a turn is rejected by privacy or dedup.

flowchart LR
    A[OpenCode SDK events] --> B[auto-capture.ts]
    B --> C[user-prompt-manager.ts]
    C --> D[privacy.ts]
    D --> E[deduplication-service.ts]
    E --> F[language-detector.ts]
    F --> G[(SQLite memory store)]
    G --> H[cleanup-service.ts]

Concurrency and Failure Handling

Auto-capture runs on every idle event, so race conditions are a first-class concern. The service maintains an in-process captured state machine with three values — 0 (idle), 1 (capturing), 2 (captured-and-released) — keyed on the prompt id. If an abort arrives mid-capture, the state must transition back to 0; otherwise subsequent prompts can become stuck. Source: src/services/auto-capture.ts:120-200.

Silent failure is the worst-case mode for an auto-capture system: the user never sees an error, and memory simply does not grow. Recent releases have closed two specific silent paths:

Both fixes are referenced from the release notes for v2.17.1 and v2.17.3.

Configuration and Known Limitations

Auto-capture is governed by opencode-mem.jsonc. Relevant keys include:

  • memoryProvider — the model used for structured extraction (e.g. openai-chat, an Anthropic model, or the OpenCode Go provider as documented in issue #130).
  • memoryModel — the specific model id passed to that provider.
  • autoCapture — boolean toggle that enables or disables the entire pipeline.

Source: src/services/auto-capture.ts:30-70.

Community-reported limitations worth knowing before relying on auto-capture:

  • SDK drift — older @opencode-ai/sdk versions (notably v1.14.48) did not expose session.create, causing auto-capture to fail with session.create returned no session id. Reported in issue #110.
  • No-port sessions — when OpenCode is launched without --port, ctx.serverUrl is unreachable and auto-capture cannot create the structured-output session. Reported in issue #145.
  • Fresh sessions only — auto-capture does not retroactively index pre-existing OpenCode sessions; users with large histories must use a backfill script. Discussed in issue #129.
  • Native binaries — capture relies on sharp and node:sqlite; the macOS ARM64 build of sharp was missing until the Hugging Face transformers migration in v2.17.0 and Windows shims were completed in v2.17.2.

The pipeline does not exist in isolation. The deduplication layer (src/services/deduplication-service.ts) shares embedding infrastructure with retrieval, the privacy gate (src/services/privacy.ts) is the same module invoked on manual memory writes, and cleanup (src/services/cleanup-service.ts) prunes both auto-captured and manually stored memories. Language tags assigned by src/services/language-detector.ts feed downstream filters and were extended with Arabic localization in v2.15.0.

Together these services form the passive ingestion half of opencode-mem, complementing the explicit /remember and backfill pathways documented elsewhere in the wiki.

Source: https://github.com/tickernelz/opencode-mem / Human Manual

User Profile Learning System

Related topics: Auto-Capture Pipeline, AI Integration, Web UI, and Operational Concerns

Section Related Pages

Continue reading this section for the full explanation and source context.

Related topics: Auto-Capture Pipeline, AI Integration, Web UI, and Operational Concerns

User Profile Learning System

The User Profile Learning System is the subsystem inside opencode-mem that turns an opaque stream of OpenCode user prompts into a structured, queryable User Profile. The profile captures the user's coding style, tool and language preferences, expertise signals, and other recurring habits, and is then fed back into OpenCode as context so that future model calls reflect what the plugin has learned.

Source: src/services/user-profile/user-profile-manager.ts:1-1 Source: src/services/user-memory-learning.ts:1-1

Purpose and Scope

The system exists to close the loop between raw conversation history and personalized behavior. Rather than treating every prompt as stateless input, the plugin samples recent user messages, asks the configured memory model to extract structured profile items, validates the result against a fixed schema, persists it to SQLite, and renders a compact context snippet for downstream prompts.

Per issue #115, profile items are organized into categories such as *preferences* (code style, tooling, language) and carry a per-item satisfaction score intended to reflect how well-attested each item is.

Source: src/services/user-profile/types.ts:1-1

Architecture

The feature is decomposed across six files that separate I/O, orchestration, validation, and presentation:

FileRole
user-profile-manager.tsOwns the profile lifecycle, SQLite I/O, and confidence bookkeeping
profile-context.tsRenders the stored profile into a prompt-injectable string
profile-utils.tsPure helpers for merging, scoring, normalizing, and (intended) decaying items
types.tsShared TypeScript shapes for UserProfileItem, ProfileSection, etc.
user-memory-learning.tsOrchestrator that decides *when* to run an extraction pass
ai/validators/user-profile-validator.tsValidates AI output against the profile schema before persistence

Source: src/services/user-profile/profile-context.ts:1-1 Source: src/services/user-profile/profile-utils.ts:1-1 Source: src/services/ai/validators/user-profile-validator.ts:1-1

Extraction Workflow

The orchestrator (user-memory-learning.ts) is event-driven. On triggers such as session idle, accumulated message count, or a time interval, it gathers a slice of recent user prompts, calls the configured memoryProvider / memoryModel, and pipes the result through user-profile-validator.ts. Only validated payloads are forwarded to user-profile-manager.ts, which performs the upsert and updates the satisfaction score for matching items.

flowchart LR
  A[OpenCode user prompts] --> B[user-memory-learning.ts]
  B --> C[memoryProvider call]
  C --> D[user-profile-validator.ts]
  D --> E[user-profile-manager.ts]
  E --> F[(SQLite profile store)]
  F --> G[profile-context.ts]
  G --> H[Injected into future prompts]

Source: src/services/user-memory-learning.ts:1-1 Source: src/services/ai/validators/user-profile-validator.ts:1-1 Source: src/services/user-profile/user-profile-manager.ts:1-1 Source: src/services/user-profile/profile-context.ts:1-1

Configuration

The learning system reads its provider and trigger thresholds from opencode-mem.jsonc:

  • memoryProvider — provider id, e.g. anthropic, openai-chat, or a Zen Go model id (see issue #130).
  • memoryModel — model id used for extraction.
  • Trigger settings — interval or count thresholds that wake the orchestrator.

Source: src/services/user-memory-learning.ts:1-1

Known Limitations

Because the orchestrator is the only path that updates the profile, anything that prevents it from running freezes learning for the whole session.

  • Confidence does not decay (issue #115): items saturate at 100% and never lose weight, even after contradictory evidence. This implies the decay branch expected in profile-utils.ts is either gated behind an unset flag or never invoked by the orchestrator.
  • Auto-capture silently fails when the OpenCode SDK version does not expose session.create (issue #110), so the validator never receives new data.
  • Unreachable serverUrl in no-port OpenCode sessions (issue #145) prevents the orchestrator from contacting the memory provider entirely.

Source: src/services/user-profile/profile-utils.ts:1-1 Source: src/services/user-memory-learning.ts:1-1

See Also

Source: https://github.com/tickernelz/opencode-mem / Human Manual

AI Integration, Web UI, and Operational Concerns

Related topics: System Architecture, Configuration Reference, Vector Search and Embedding Models

Section Related Pages

Continue reading this section for the full explanation and source context.

Related topics: System Architecture, Configuration Reference, Vector Search and Embedding Models

AI Integration, Web UI, and Operational Concerns

AI Provider Abstraction

The plugin ships a pluggable AI layer that lets operators point memory generation, summarization, and embedding work at any compatible model. A BaseProvider defines the contract every backend must satisfy: complete(prompt), embed(texts), health probing, and capability flags (supportsStructuredOutput, supportsEmbeddings). Concrete subclasses implement the wire protocol for each vendor. Source: src/services/ai/providers/base-provider.ts:1-120.

The AiProviderFactory resolves the configured memoryProvider and embeddingProvider strings from opencode-mem.jsonc into instantiated providers. It validates that the chosen model advertises the capability required by the calling subsystem (for example, that the memory provider supports structured JSON output before auto-capture asks it to produce observation records). When validation fails, it throws a descriptive error so misconfiguration surfaces at startup instead of at idle time. Source: src/services/ai/ai-provider-factory.ts:40-160.

Three production adapters are bundled:

A fourth adapter, OpencodeProvider, is special: it does not call an external HTTP service. Instead it round-trips through the host OpenCode server's session.create + session.message APIs, allowing the plugin to reuse whatever provider the user already configured inside OpenCode (Claude, GPT, Copilot, Zen, etc.). Source: src/services/ai/opencode-provider.ts:30-140.

Auto-Capture Pipeline and SDK Coupling

Auto-capture listens for session.idle events from the OpenCode plugin hook and, when triggered, asks the AI provider to extract observations from the just-finished prompt/response pair. The capture flow is:

  1. Receive idle event with ctx.directory and ctx.serverUrl.
  2. If serverUrl is absent (e.g. OpenCode launched without --port), fall back to a local probe or skip capture with a warning.
  3. Create a transient session via opencode.session.create({ ... }).
  4. Send the prompt to the provider, asking for structured observations.
  5. Persist observations through the SQLite memory store and refresh the user-profile embeddings.

Source: src/services/capture/auto-capture.ts:80-260.

This pipeline is fragile when the host SDK and the plugin fall out of step. Issue #110 documents that SDK v1.14.48 removed session.create, causing auto-capture to log session.create returned no session id on every idle event. Issue #145 compounds the problem: even when the method exists, ctx.serverUrl may be unreachable because OpenCode was started without an explicit --port. The captured=2 lock introduced in v2.17.1 and refined through v2.17.3 (PR #136) prevents the system from leaving prompts stuck when an abort fires mid-capture. Source: src/services/capture/auto-capture.ts:300-380.

Web UI and HTTP API

The bundled web layer is a Bun-first HTTP server with Node fallbacks for environments without Bun.serve. The entry point boots an HTTP listener, mounts static assets for the SPA, and registers the JSON API routes under /api. Source: src/web/server.ts:1-140.

The API surface exposed to the UI (and to external tooling such as the backfill scripts in issue #129) includes:

EndpointMethodPurpose
/api/memoriesGETList memories, honoring user/project scope filters.
/api/memoriesPOSTManually inject a memory.
/api/searchGETHybrid keyword + vector search.
/api/profileGET/PUTInspect or edit the user-profile confidence map.
/api/healthGETProvider and database liveness probe.

The /api/memories listing fix in v2.16.0 (PR #123) ensures user-scope memories are visible alongside project-scope ones, which previously caused user memories to silently disappear. Memory-type badges rendered by the web UI were hardened against XSS in v2.17.4 (PR #137). Source: src/web/routes/api.ts:40-260.

Operational Concerns and Known Footguns

Several recurring operational issues surface in the community:

  • Confidence decay absent (#115). The user-profile map records a satisfaction ratio per preference but never decrements stale entries. Users on v2.14.2 see flat 100% values because the decay job is not scheduled. Source: src/config/schema.ts:120-180.
  • macOS ARM64 sharp load failure (#97, #94). The plugin depends on sharp for image-to-text observation capture. v2.17.0 (PR #127) migrates embeddings to @huggingface/transformers@^4, which removes the sharp dependency on Apple Silicon. Older installs still fail at plugin load with Cannot find module sharp-darwin-arm64v8.node.
  • Windows WSL bus error (#149). Reported when rtk is layered on top of opencode-mem inside WSL2; reproduction is sensitive to CPU feature flags. No plugin-side fix is shipped as of v2.17.4.
  • No-port OpenCode sessions (#145). When ctx.serverUrl is empty the auto-capture path must detect the condition and degrade to a no-op rather than throwing.
  • Backfill gap (#129). Capture begins from fresh sessions only; community-contributed scripts (bootstrap-memories.ts, rebuild-embeddings.ts) reconstruct history from the OpenCode session JSONL store.
  • Embedding prefix tuning (#98). Nomic nomic-embed-text-v1.5 benefits from search_document: / search_query: prefixes; the plugin currently sends raw text. A config opt-in is the proposed remedy.

Operators should pin the host OpenCode SDK to a version known to retain session.create, set memoryProvider explicitly even when relying on the OpenCode adapter, and treat the confidence map as advisory until a decay scheduler lands.

Source: https://github.com/tickernelz/opencode-mem / Human Manual

Doramagic Pitfall Log

Source-linked risks stay visible on the manual page so the preview does not read like a recommendation.

high Security or permission risk requires verification

May increase setup, validation, or first-run risk for the user.

medium Installation risk requires verification

May increase setup, validation, or first-run risk for the user.

medium Configuration risk requires verification

May increase setup, validation, or first-run risk for the user.

medium Configuration risk requires verification

May increase setup, validation, or first-run risk for the user.

Doramagic Pitfall Log

Found 12 structured pitfall item(s), including 1 high/blocking item(s). Top priority: Security or permission risk - Security or permission risk requires verification.

1. Security or permission risk: Security or permission risk requires verification

  • Severity: high
  • Finding: Project evidence flags a security or permission risk. Review the linked source before relying on this workflow.
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: community_evidence:github | https://github.com/tickernelz/opencode-mem/issues/110

2. Installation risk: Installation risk requires verification

  • Severity: medium
  • Finding: Project evidence flags a installation risk. Review the linked source before relying on this workflow.
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: community_evidence:github | https://github.com/tickernelz/opencode-mem/issues/115

3. Configuration risk: Configuration risk requires verification

  • Severity: medium
  • Finding: Project evidence flags a configuration risk. Review the linked source before relying on this workflow.
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: capability.host_targets | https://github.com/tickernelz/opencode-mem

4. Configuration risk: Configuration risk requires verification

  • Severity: medium
  • Finding: Project evidence flags a configuration risk. Review the linked source before relying on this workflow.
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: community_evidence:github | https://github.com/tickernelz/opencode-mem/issues/145

5. Capability evidence risk: Capability evidence risk requires verification

  • Severity: medium
  • Finding: README/documentation is current enough for a first validation pass.
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: capability.assumptions | https://github.com/tickernelz/opencode-mem

6. Runtime risk: Runtime risk requires verification

  • Severity: medium
  • Finding: Project evidence flags a runtime risk. Review the linked source before relying on this workflow.
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: community_evidence:github | https://github.com/tickernelz/opencode-mem/issues/149

7. Runtime risk: Runtime risk requires verification

  • Severity: medium
  • Finding: Project evidence flags a runtime risk. Review the linked source before relying on this workflow.
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: packet_text.keyword_scan | https://github.com/tickernelz/opencode-mem

8. Maintenance risk: Maintenance risk requires verification

  • Severity: medium
  • Finding: Project evidence flags a maintenance risk. Review the linked source before relying on this workflow.
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: evidence.maintainer_signals | https://github.com/tickernelz/opencode-mem

9. Security or permission risk: Security or permission risk requires verification

  • Severity: medium
  • Finding: no_demo
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: downstream_validation.risk_items | https://github.com/tickernelz/opencode-mem

10. Security or permission risk: Security or permission risk requires verification

  • Severity: medium
  • Finding: no_demo
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: risks.scoring_risks | https://github.com/tickernelz/opencode-mem

11. Maintenance risk: Maintenance risk requires verification

  • Severity: low
  • Finding: issue_or_pr_quality=unknown。
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: evidence.maintainer_signals | https://github.com/tickernelz/opencode-mem

12. Maintenance risk: Maintenance risk requires verification

  • Severity: low
  • Finding: release_recency=unknown。
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: evidence.maintainer_signals | https://github.com/tickernelz/opencode-mem

Source: Doramagic discovery, validation, and Project Pack records

Community Discussion Evidence

These external discussion links are review inputs, not standalone proof that the project is production-ready.

Sources 12

Count of project-level external discussion links exposed on this manual page.

Use Review before install

Open the linked issues or discussions before treating the pack as ready for your environment.

Community Discussion Evidence

Doramagic exposes project-level community discussion separately from official documentation. Review these links before using opencode-mem with real data or production workflows.

Source: Project Pack community evidence and pitfall evidence