GHMEM — GitLab Harness & Enhanced Memory
(unofficial; a.k.a. Greg H's Memory)
Persistent semantic memory and an agentic harness for AI coding assistants. Works with OpenCode, Claude Code, and other MCP-capable agents.
New here? Read docs/GETTING_STARTED.md to check that your install works, see what is stored, and find the settings. Agents read BOOTSTRAP.md.
Overview
GHMEM is an MCP (Model Context Protocol) server that gives AI coding sessions long-term memory plus a workflow harness (a priority queue, resource watchers, and background tasks). It automatically learns from your workflow, stores decisions and procedures, and provides relevant context when needed.
The project began as an OpenCode-only memory plugin and has since grown well beyond that, hence the rebrand. It now supports multiple agents against one shared daemon, and the "memory" scope has widened into memory plus harness. GHMEM is the brand; the technical identifiers below (the opencode-memory repo, the opencode_memory Python package, the opencode-semantic-memory PyPI distribution, the OPENCODE_MEMORY_* environment variables, and the ~/.local/share/opencode-memory data directory) keep their existing names for compatibility, so nothing about an existing install changes.
Key benefits:
- Remember decisions, blockers, procedures, and facts across sessions
- Semantic search finds relevant memories even with different wording
- Works across agents (OpenCode, Claude Code, and other MCP clients) sharing one memory
- Session coordination prevents conflicts when running multiple agent instances
- Project-scoped directives for per-project instructions
- Zero manual effort - learns passively from your workflow
Features
- Hybrid retrieval: Combines full-text search (FTS5) with vector similarity search
- Instant storage: Memories stored immediately, embeddings computed async
- Session coordination: Tracks active sessions, supports item claiming to prevent conflicts
- Contextual directives: Global + project-specific instructions loaded at boot
- Entity tracking: Links memories to MRs, issues, epics (!123, #456, &789)
- GitLab enrichment: Fetches metadata for entities from GitLab API
- Memory age: All outputs show age (
now,5m,2h,3d,2w,1mo,2y) to identify potentially outdated information - Proactive context injection: Automatically injects relevant memories before each LLM call (OpenCode only, via plugin)
- Agent-agnostic LLM support: Works with OpenCode, Claude CLI, GitLab Duo, Ollama, or custom commands
- GitLab bulk ingest (optional): Index an entire GitLab project's issues and MRs into a separate database for weak semantic matching in
memory_recall, with incremental daily sync to pick up new and updated issues/MRs - Queue defer/postpone: Defer queue items you're waiting on and have them automatically resurface when a linked reminder fires
- Secret-guard: Reversible redaction of credentials from ingested content. Secrets are masked with placeholders in transit (so they never reach the model or memory) and restored only on a safe round-trip (writing back to the same file), eliminating false-positive corruption; at true exposure sinks (memory, comments, foreign files) they are never restored. Includes pattern auto-discovery, an exclude-paths allowlist, and a
forgettool to undo a false positive. Setreversible: false(orSECRET_GUARD_REVERSIBLE=0) for the legacy one-way mode. See docs/SECRET_GUARD.md and the hybrid design. - Security self-audit (optional): A conservative, read-only pass (
SecurityAuditjob, default off;[security_audit] enabled = true) that scans recent memory content and the enabled-autonomy config for risk signals — secrets stored in memory content, prompt-injection phrasing from untrusted ingestion, and autonomy-surface config drift — and surfaces findings asblocker/ideamemories visible at session start. It never mutates the inspected content. See the threat model. - Tiered Docker sandboxing (optional): Run LLM-backed background tasks in a hardened container with per-task least-privilege GitLab tokens and a scrubbed LLM credential (default off;
mode = "host") - Migration system: Versioned schema migrations with structured blockers/remediation and data-safety checks
- Effectiveness judging: Optional LLM material-influence judging of recalled memories to track which memories actually help
- Review swarm (optional): A review method in which parallel specialised lenses, a collator and an antagonist review one artifact, with a seed set of memories that you can ingest to set it up. See docs/REVIEW_SWARM.md.
- Optional Rust runtime: Drop-in Rust backend sharing the same stores and HTTP/MCP API; selectable at install time
- Version visibility:
/healthand/statsreport the running server version so you can confirm an upgrade took effect - Web pages: the HTTP server on port 9824 serves three browser pages.
/worklistshows the priority queue,/command-centershows objectives and background jobs, and/dashboardshows health trends (search latency, database size, vector store, job queue)
Prerequisites
- Python 3.11+ (verify with
python3 --version- must show 3.11 or higher) - git (for cloning the repository)
- venv module (usually included with Python, or install via
python3-venvpackage)
On Ubuntu/Debian:
sudo apt install python3.11 python3.11-venv git
On macOS (with Homebrew):
brew install python@3.11 git
Installation
From PyPI
pip install opencode-semantic-memory
Quick Install Script
Works on both Linux and macOS, sets up the daemon as a background service:
curl -fsSL https://gitlab.com/ghavenga/opencode-memory/-/raw/master/scripts/setup.sh | bash
This will:
- Clone the repository to
~/.local/share/opencode-memory-install - Create a virtual environment and install dependencies
- Download the embedding model (~90MB, one-time, runs locally)
- Set up a background service:
- Linux: systemd user service
- macOS: launchd LaunchAgent
- Configure OpenCode integration
- Optionally bootstrap core directives and seed the core memories from
docs/BOOTSTRAP_MEMORIES.md(requires confirmation due to LLM costs). If you skip this, the boot context lists each missing core memory with thememory_ingest_filecall that seeds it.
Re-running the installer is safe and non-interactive. Your choices (runtime,
startup mode, LLM provider, permissions) are saved to
~/.config/opencode-memory/config.toml and reused on subsequent runs — re-runs
just update the install and fix anything missing, without re-asking. To change
your configuration, force the prompts with --reconfigure:
# from a local clone
bash scripts/setup.sh --reconfigure
# piped
curl -fsSL https://gitlab.com/ghavenga/opencode-memory/-/raw/master/scripts/setup.sh | bash -s -- --reconfigure
From Source
git clone https://gitlab.com/ghavenga/opencode-memory.git
cd opencode-memory
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
# Download the embedding model (required, ~90MB, runs locally)
python -c "from sentence_transformers import SentenceTransformer; SentenceTransformer('all-MiniLM-L6-v2')"
Important: After installing from source, you must manually set up the service (see Quick Start below). The service files in the repo assume the quick install path - you'll need to update the paths to match your clone location.
Cost Information
Default background processing is free - The memory daemon uses:
- Local embedding model (sentence-transformers, no API calls)
- Pattern-based extraction (regex, no LLM)
- Session summaries (stores conversations as-is, no LLM processing)
LLM-based knowledge extraction is OFF by default - There is an optional
feature that uses opencode run to analyze old conversations and extract
procedures, decisions, and facts. This:
- Runs every 6 hours, processing up to 50 conversations per cycle
- Makes LLM API calls for each conversation (significant cost potential)
- Is disabled by default to avoid unexpected charges
To enable LLM extraction (if you want it), add to ~/.config/opencode-memory/config.toml:
[ingestion]
llm_extraction = true
LLM Provider Configuration
The memory system supports multiple LLM providers for knowledge extraction and session analysis. By default, it uses OpenCode, but you can configure alternatives:
# ~/.config/opencode-memory/config.toml
# Option 1: OpenCode (default) - uses your configured OpenCode LLM
[llm]
provider = "opencode"
# Option 2: Anthropic Claude CLI (requires claude-cli package)
[llm]
provider = "claude"
model = "claude-sonnet-4-20250514" # optional
# Option 3: GitLab Duo CLI (requires glab with Duo enabled)
[llm]
provider = "duo"
# Option 4: Ollama (local models)
[llm]
provider = "ollama"
model = "qwen2.5:14b" # or any model you have pulled
# Option 5: Custom command
[llm]
provider = "custom"
command = "my-llm-cli"
args = ["--prompt"] # prompt is appended after these args
Note: Proactive context injection (automatic memory loading) works with OpenCode via the plugin system and with Claude Code via its SessionStart and UserPromptSubmit hooks (see the "Using with Claude Code" section). Other MCP agents can use all memory tools manually.
Updating
To update an existing installation, re-run the quick install script. It is idempotent and will pull the latest code, refresh dependencies, and restart the background services:
curl -fsSL https://gitlab.com/ghavenga/opencode-memory/-/raw/master/scripts/setup.sh | bash
The update is non-destructive for your data:
- Your memory database at
~/.local/share/opencode-memory/memory.dbis not touched - Your vector store at
~/.local/share/opencode-memory/vectors/is not touched - Your existing
~/.config/opencode-memory/config.tomlis preserved (the script only writes a default if one does not already exist)
The script's rm -rf operations are scoped to the install directory (~/.local/share/opencode-memory-install) and only trigger when the git checkout or virtual environment is in a broken state. Any uncommitted local changes inside the install directory will be discarded (git reset --hard + git clean -fd) before the pull.
If you want to be extra cautious before a major update, snapshot the data directory first:
# VACUUM INTO copies one consistent snapshot in one pass while the server continues to write.
# Do not use the CLI .backup command on a live database. It starts again after each server write.
TS=$(date +%Y%m%d-%H%M%S)
BACKUP_DIR="$HOME/.local/share/opencode-memory-backups/$TS"
mkdir -p "$BACKUP_DIR"
sqlite3 -cmd ".timeout 10000" "$HOME/.local/share/opencode-memory/memory.db" "VACUUM INTO '$BACKUP_DIR/memory.db'"
cp -R "$HOME/.local/share/opencode-memory/vectors" "$BACKUP_DIR/vectors"
cp "$HOME/.config/opencode-memory/config.toml" "$BACKUP_DIR/config.toml"
After the update, verify the service is healthy:
curl -s http://127.0.0.1:9824/health
# {"status":"ok","server":"opencode-memory","daemon":{"running":true}}
Opt-in auto-updates
Python daemon only; the Rust server does not ship the auto-updater. It has no
[update]config, noupdate_*/rollbackworkflow actions, and no update-check job.
The memory system can keep itself up to date. This is off by default; enable it in ~/.config/opencode-memory/config.toml:
[update]
enabled = true # run the periodic check (daily by default)
channel = "stable" # "stable" tracks release tags; "edge" tracks master
auto_patch = true # auto-apply x.y.Z bugfix releases
auto_minor = false # x.Y.0: notify and wait for confirmation
auto_major = false # X.0.0: notify and wait for confirmation
# pin = "0.13.0" # never auto-apply beyond this version
keep_backups = 3 # data snapshots kept before each apply
How it works:
- A background job checks the configured channel on
check_interval_seconds. - Patch releases can apply automatically. Minor and major releases are held back and surfaced in your boot context; apply them from a session with
workflow(action="update_apply")(or by re-running the installer). - Before applying, it records the current version and snapshots your data (
memory.db,vectors/,config.toml) so a rollback can always restore both code and data. If the snapshot fails (for example, not enough disk space), the update stops before it changes the checkout or the services. The update reuses a complete snapshot that is younger thanOPENCODE_MEMORY_SNAPSHOT_REUSE_HOURS(default 12,0turns reuse off) when the database, the snapshot and the target ref have the same schema version. A restore from a reused snapshot also loses the writes made between that snapshot and the update. - Applying restarts the background services. Because the MCP daemon is stateless the client reconnects transparently, unless the update changed the tool interface — in that case your boot context will tell you to restart opencode so the new tools load.
- On the edge channel there is no semver tier;
auto_patchgoverns whether newmastercommits auto-apply, since riding the tip is the point of edge.
Check status or roll back at any time from a session:
workflow(action="update_status") # current version, channel, pending update, restart hint
workflow(action="update_apply") # apply an available (or held-back) update now
workflow(action="rollback") # revert to the previous version
Or from the shell:
# Roll back to the previous version (code only; data untouched)
bash ~/.local/share/opencode-memory-install/scripts/rollback.sh
# Roll back to a specific release, restoring the newest complete data snapshot too
bash ~/.local/share/opencode-memory-install/scripts/rollback.sh --to v0.12.0 --restore-data
--restore-data uses the newest snapshot that auto-update.sh marked complete in its snapshot.json. It uses a snapshot without snapshot.json (from an older auto-update.sh) only when no complete snapshot exists. When no usable snapshot exists, it stops before it changes anything.
Quick Start
1. Start the Memory Services
The recommended setup uses 3 background services (see ARCHITECTURE.md):
Using the setup script (recommended):
# The setup script handles everything including service installation
curl -fsSL https://gitlab.com/ghavenga/opencode-memory/-/raw/master/scripts/setup.sh | bash
Manual start (development/testing):
source .venv/bin/activate
# Terminal 1: HTTP/MCP server
python -m opencode_memory.http_server
# Terminal 2: File watcher daemon
python -m opencode_memory.daemon
# Terminal 3: Background worker
python -m opencode_memory.jobs.worker
Install services manually (Linux systemd):
mkdir -p ~/.config/systemd/user
INSTALL_DIR="$PWD" # Run from your clone directory
# Copy service files
for svc in opencode-memory opencode-memory-daemon opencode-memory-worker; do
sed "s|%h|$HOME|g" "$INSTALL_DIR/${svc}.service" | \
sed "s|\.local/share/opencode-memory-install|${INSTALL_DIR#$HOME/}|g" \
> ~/.config/systemd/user/${svc}.service
done
# Enable and start
systemctl --user daemon-reload
systemctl --user enable --now opencode-memory opencode-memory-daemon opencode-memory-worker
# Check status
systemctl --user status opencode-memory*
Install services manually (macOS launchd):
See the setup script for plist templates, or run the setup script which handles this automatically.
2. Configure OpenCode
Add to ~/.config/opencode/opencode.json:
{
"mcp": {
"memory": {
"type": "remote",
"url": "http://localhost:9824/mcp"
}
}
}
The memory server can also be run as a local stdio process via
scripts/memory-mcp.sh. This launches a shim that serves the GitLab
workflow tool in-process (inheriting GITLAB_TOKEN from your shell) and
proxies every other memory_* tool to the shared HTTP daemon:
{
"mcp": {
"memory": {
"type": "local",
"command": ["/path/to/opencode-memory-install/scripts/memory-mcp.sh"]
}
}
}
Use the local (memory-mcp.sh) form if you want the workflow tools. The
remote (url) form lists workflow, but its GitLab actions fail there,
because workflow needs your session's GitLab token and the shared daemon
does not have it.
Reducing context: lazy-mcp
Since v0.12.0 the MCP surface is already consolidated: the shim advertises
only five tools up front (remember, recall, memory, workflow,
memory_shutdown), and the ~100 other operations are reached through
memory(action=...), which forwards to the daemon. So on any current release
the per-session memory tool overhead is already small; make sure you are
updated before reaching for anything more.
If you still want to squeeze that surface further, or you run several MCP
servers and want to lazy-load all of them together, front them with
lazy-mcp, a proxy that replaces a
server's schemas with four meta-tools (list_servers, list_commands,
describe_commands, invoke_command) and discovers the real tools on demand.
For memory alone the win is marginal now (five schemas down to four shared
across all proxied servers); the real value is aggregating a larger server
(for example a GitLab MCP with dozens of tools) in the same proxy.
Point lazy-mcp at the local shim so workflow keeps working (it needs your
session's GITLAB_TOKEN, which the shim inherits and the shared daemon does
not have). In ~/.config/lazy-mcp/servers.json:
{
"servers": [
{
"name": "memory",
"description": "opencode-memory: persistent semantic memory + GitLab workflow tools",
"command": ["/path/to/opencode-memory-install/scripts/memory-mcp.sh"]
}
]
}
Then replace the memory entry in opencode.json with a single lazy-mcp
entry, and set the plugin to { "tools": false } so it injects context without
adding its own tool schema either:
{
"mcp": {
"lazy-mcp": {
"type": "local",
"command": ["npx", "-y", "lazy-mcp@latest", "--config", "~/.config/lazy-mcp/servers.json"]
}
},
"plugin": [["/path/to/opencode-memory-install/plugin", { "tools": false }]]
}
Notes:
- Front the local shim (
scripts/memory-mcp.sh), not the daemonurl. The shim advertises the consolidated set (remember,recall,memory,workflow,memory_shutdown) and servesworkflowin-process with yourGITLAB_TOKEN. Fronting the daemon byurl("url": "http://localhost:9824/mcp") instead exposes the raw, un-shrunk tool list,workflowfails there with a missing-token error, andmemory_shutdownis not exposed. - Proactive context injection and boot context are unaffected: they are driven by the plugin's hooks, not by tool calls, so lazy discovery does not delay them.
- lazy-mcp can aggregate multiple servers (e.g. a GitLab MCP alongside memory)
in the same
servers.json, so this composes with other lazy-loaded servers.
3. Enable Proactive Context Injection (OpenCode Only)
The proactive context plugin automatically injects relevant memories before each LLM call, so the AI has historical context without needing explicit memory_recall calls.
Requirements:
- OpenCode (not supported with other LLM providers)
- Node.js 20+ (for building the plugin)
Setup:
The setup script automatically builds and configures the plugin if Node.js 20+ is available. To verify or configure manually:
// ~/.config/opencode/opencode.json
{
"mcp": { ... },
"plugin": ["/path/to/opencode-memory-install/plugin"]
}
How it works:
- User sends a message → Plugin extracts entities (!MR, #issue, @user)
- Plugin calls memory server for relevant context
- Context is injected into the system prompt before LLM call
- AI sees relevant history automatically
Thinking-aware injection: The plugin also analyzes the assistant's responses to inject context mid-conversation. If the AI is "thinking" about a topic it has prior knowledge of, relevant memories are injected before the next LLM call.
Relevance filtering: Items with semantic similarity below 0.35 are filtered out to reduce noise. This threshold was tuned based on effectiveness analysis showing low-relevance items had 0% actual usefulness.
Note: If you're using a different LLM provider (Claude, Duo, Ollama), you'll need to call memory tools explicitly - proactive injection only works with the OpenCode plugin.
For more details, see the plugin documentation.
4. Boot Context (Automatic)
When using the proactive context plugin (step 3), boot context is automatically injected at session start. This includes:
- Active parallel sessions (collision warning)
- Standing instructions (boot gates)
- Unresolved blockers
- Due reminders
- Memory usage instructions
No manual configuration needed - the plugin handles everything.
Legacy AGENTS.md: If you have an existing ~/.config/opencode/AGENTS.md with memory_get_boot_context() instructions, you can remove it - boot context is now auto-injected. The setup script will offer to clean this up.
Without the plugin: If you're not using the proactive context plugin (e.g., a raw claude CLI prompt or Ollama), you'll need to call memory_get_boot_context() manually at session start. Note this is different from Claude Code, which does get automatic injection through its own hooks (see Using with Claude Code below).
Using with Claude Code
opencode-memory works with Claude Code as well as OpenCode, and both can run against the same daemon at the same time. There is no either/or: one install can configure both agents.
Claude Code gets the two halves of the system through its own mechanisms:
- Memory tools via an MCP server registration, which exposes every
memory_*tool. - Automatic context injection via two hook scripts (
SessionStartandUserPromptSubmit) that mirror what the OpenCode plugin does. They live inclaude-code/hooks/and call the same daemon endpoints.
| OpenCode plugin hook | Claude Code hook | Purpose |
|---|---|---|
experimental.chat.system.transform |
SessionStart |
Inject boot context at session start |
chat.message (proactive) |
UserPromptSubmit |
Extract entities from the prompt, inject relevant memories |
The setup script offers Claude Code configuration when it finds the claude CLI. To pick agents explicitly (comma-separated, either or both), set OPENCODE_MEMORY_AGENTS:
# Configure both agents
OPENCODE_MEMORY_AGENTS=opencode,claude bash scripts/setup.sh --reconfigure
# Add Claude Code to an existing install
OPENCODE_MEMORY_AGENTS=claude bash scripts/setup.sh --reconfigure
Claude Code needs ANTHROPIC_API_KEY in your environment or an interactive claude /login; the installer never handles credentials. Full manual steps and environment variables are documented in claude-code/README.md.
Configuration
Create ~/.config/opencode-memory/config.toml:
[identity]
user = "your-gitlab-username" # Optional, auto-detected from git
instance = "gitlab.com"
[boot]
identity = true
active_sessions = true
hot_items = true
unresolved_blockers = true
recent_decisions = false
max_hot_items = 5
[ingestion]
watch_paths = ["~/.local/share/opencode/opencode.db"]
db_poll_interval = 30
llm_extraction = false
working_directory = "/path/to/your/projects"
[storage]
path = "~/.local/share/opencode-memory"
# export_dir = "~/opencode-memory-exports" # optional: confine export/import paths (see Backup and Migration)
# Optional. Timeouts for workflow actions that answer inside one MCP request.
# The values below are the defaults. Keep mcp_client_timeout_ms equal to
# mcp.memory.timeout in opencode.json; the sync caps (deep_review 160s,
# bg_wait 150s, one critique LLM rung 170s) are this ceiling minus a margin.
# Each cap bounds one call. Calls chained in one request can still pass the ceiling.
[workflow]
mcp_client_timeout_ms = 180000
invoke_subagent_timeout_seconds = 120
draft_comment_timeout_seconds = 120
gitlab_read_timeout_seconds = 30
Maintenance window
The maintenance window is off by default. When you turn it on, heavy
maintenance jobs wait for an idle window before they run. The window is
open when there was no keystroke on an interactive opencode terminal for
idle_minutes, and the 1 minute load per CPU and the CPU, I/O and memory PSI
values are at or below their limits. A signal that the worker cannot read does
not close the window. A job that waits for its max defer runs at the next
tick, even if the box is still busy. A job with no recorded success does not
wait. To turn it on, add this section to config.toml. The values other than
enabled are the defaults:
[maintenance_window]
enabled = true
idle_minutes = 15
max_load_per_cpu = 1.0
max_cpu_psi_some_avg10 = 20
max_io_psi_some_avg10 = 10
max_mem_psi_some_avg10 = 5
max_defer_hours = 24
job_max_defer_hours = { MemoryLinking = 0.25 }
jobs = ["MemoryLinking", "ConceptExtraction", "MemoryDbMaintenance"]
gate_vector_full_rebuild = true
jobs lists the scheduled job classes that wait. MemoryDbMaintenance includes
the dangling link purge. gate_vector_full_rebuild applies the window to the
full vector index rebuild in VectorMaintenance, only when the rebuild is due by
age. The 30 minute compaction, the incremental index step, a rebuild for an
unindexed backlog and the first build of a missing index do not wait.
MemoryLinking is due every 5 minutes and links at most 50 new memories a pass,
so its max defer is 15 minutes. It still runs every 20 minutes on a busy day.
Every other job waits for at most 24 hours. The load limit is 1.0 per CPU
because the autoscaler keeps the box near 0.5 to 0.8 per CPU with background
work while nobody is at the keyboard, and a limit of 0.5 kept the window shut
most of the time.
The worker applies a change when the file changes. The worker stats show a
held job with window_held_since and window_reason.
Catch-up after downtime
When the host was off for a while, every missed cron occurrence and every
daily job is due at the same moment, and that moment is usually boot, when
the host is busiest. The [catchup] section (on by default) spreads that work
out by host health instead of fixed timers. Work enters the gates only inside
a storm window. A storm window is the first storm_window_seconds after a
worker start within storm_window_seconds of boot, or after a resume (detected from the
growth of time in suspend). Work that the gates already hold keeps draining
one at a time after the window closes. Outside a window, new work runs as
before. The storm window needs Linux (CLOCK_BOOTTIME). On other platforms no
window opens. With storm_only = false the gates apply all the time, on any
platform.
- Cron catch-up lane. A due cron occurrence older than
[cron] grace_secondsis catch-up work. The lane releases one at a time. The next one starts only after the previous catch-up task has finished,observation_secondshave passed since that release, and the host is healthy. On-time occurrences fire as before. A downtime of several days still fires each job once, for its latest occurrence. The occurrence is recorded only when the task is enqueued, so a restart while waiting loses nothing. Setcatchup_priorityon a[[cron.jobs]]entry to order the lane (lower first). Jobs without one follow in configured order. - Interval stagger. When more than one interval job with an interval of
at least
stagger_min_interval_secondsis overdue in the same tick, the scheduler enqueues one per tick under the same gates. A lone overdue job and more frequent jobs run at once. Inside a storm window, the release of such a lone job starts the observation window that both lanes wait out. The interval stagger does not wait for the previous task to finish. - Autoscaler. With
gate_autoscaler, the autoscaler holds opportunistic tasks (everything not in[autoscaler] presence_exempt_factories) while the instantaneous I/O or memory PSI is over its limit, and on its first tick after a start.
Health uses the 1 minute load per CPU, the PSI avg10 values, and PSI stall
shares measured from the total= counters over a short window (*_1s
limits). avg10 is a 10 second moving average, so it still reads calm while a
boot storm builds. The counters do not lag. A limit set to an empty string
("") is not checked, and a signal the worker cannot read does not hold work
back. max_wait_seconds is the safety valve. Work held that long is released
whatever the host looks like. Time while the host was off or in suspend does
not count as waiting. Catch-up work is delayed, never dropped. The
values below are the defaults:
[catchup]
enabled = true
storm_only = true
storm_window_seconds = 1800
resume_min_suspend_seconds = 60
max_wait_seconds = 2700
observation_seconds = 30
sample_seconds = 1.0
max_load_per_cpu = 1.0
max_io_psi_some_avg10 = 20
max_io_psi_some_1s = 35
max_mem_psi_some_avg10 = 10
max_mem_psi_full_1s = 5
max_cpu_psi_some_avg10 = 30
max_swap_out_mbs = 4
stagger_interval_jobs = true
stagger_min_interval_seconds = 3600
gate_autoscaler = true
The worker stats show a staggered job with catchup_held_since and
catchup_reason, the CronTick result lists catchup_held and
catchup_reason, and the autoscaler status shows instant_pressure_hold.
See docs/catchup.md for the state the lanes keep.
MCP Tools
Note: Tools are registered with the
memory_prefix when accessed through the "memory" MCP server. For example,recallbecomesmemory_recallin OpenCode sessions. The names below are the remote-setup names. With the local shim or lazy-mcp, most of these tools are reached throughmemory(action=...). See Tool Names by Setup.
Core Memory Tools
| Tool | Description |
|---|---|
memory_recall(query, limit?, project?, category?, compact?) |
Semantic search across memories |
memory_remember(content, category, what?, why?, learned?, entities?, project?) |
Store a memory |
memory_get_context(entity_ref) |
Get all memories for !MR, #issue, or &epic |
memory_get_boot_context() |
Load startup context (identity, blockers, directives) |
memory_get_linked_memories(memory_id, link_types?) |
Get memories linked to a specific memory |
Session Coordination
| Tool | Description |
|---|---|
memory_session_start(session_id, working_on?) |
Register session |
memory_session_end(session_id, summary?) |
End session with summary |
memory_session_heartbeat(session_id) |
Keep session alive |
memory_get_active_sessions() |
List active sessions |
memory_claim_item(session_id, item_ref) |
Claim exclusive ownership |
memory_release_item(session_id, item_ref) |
Release claimed item |
Memory Management
| Tool | Description |
|---|---|
memory_resolve_blocker(memory_id) |
Mark blocker as resolved |
memory_unresolve_blocker(memory_id) |
Reopen a blocker |
memory_archive_memory(memory_id, reason?) |
Archive outdated memory (soft delete) |
memory_delete_memory(memory_id, also_delete_vector?) |
Permanently delete a memory |
memory_edit_memory(memory_id, content?, what?, why?, learned?) |
Edit memory content or metadata |
memory_create_link(source_memory_id, target_memory_id, link_type?, strength?, reason?, bidirectional?) |
Manually link two memories |
memory_bulk_archive(memory_ids?, category?, older_than_days?, reason) |
Archive multiple memories |
memory_consolidate_memory(days_stale?, project?) |
Find stale/duplicate memories |
memory_search_history(query, category?, limit?) |
Search with category filter |
Backup & Transfer
| Tool | Description |
|---|---|
memory_export_memories(output_path?, project?, categories?, since_days?, overwrite?) |
Export to JSON |
memory_import_memories(input_path, dry_run?, skip_duplicates?) |
Import from JSON |
Reminders
| Tool | Description |
|---|---|
memory_set_reminder(reminder_at, content?, memory_id?, recurrence?) |
Set time-triggered reminder |
memory_clear_reminder(memory_id) |
Dismiss a reminder |
memory_advance_reminder(memory_id) |
Move recurring reminder to next occurrence |
memory_mark_procedure_reviewed(memory_id) |
Mark procedure as still accurate |
memory_set_review_interval(memory_id, interval_days) |
Set procedure review frequency |
Knowledge Graph
| Tool | Description |
|---|---|
memory_graph_aware(entity_ref, max_depth?) |
Check what knowledge exists for an entity |
memory_graph_explore(entity_ref, types?, limit?) |
Discover related entities |
memory_graph_retrieve(entity_refs, include_memories?) |
Get full content for entities |
memory_graph_status(project?) |
Knowledge graph statistics |
Code Indexing
| Tool | Description |
|---|---|
memory_index_codebase(path?, incremental?) |
Index code structure (classes, functions) |
memory_initialize_workspace(path?, max_depth?) |
Scan directories for git repos |
memory_extract_concepts(batch_size?) |
Extract semantic concepts via LLM |
memory_search_code(query, symbol_types?) |
Search indexed code symbols |
GitLab Spider
| Tool | Description |
|---|---|
memory_spider_search(entity_ref, project?, max_depth?) |
Crawl GitLab entity into knowledge graph |
memory_spider_status() |
Check spider queue status |
memory_spider_query(query?, author?, entity_type?) |
Search crawled GitLab content |
Priority Queue
Work item queue for tracking todos, MRs to review, and authored MRs. Supports P0–P5 priorities with atomic claiming to prevent parallel session conflicts.
Items can also carry an optional due_date. As a deadline nears, an item's effective priority escalates toward P0 so milestone-driven work surfaces itself — without rewriting the stored base priority. Escalation only ever raises urgency, and pushing a due date out relaxes the item back to its base priority.
Escalation is lead-time-proportional: it tracks how far the item has travelled along its runway (from created_at to due_date), not an absolute number of days. The effective priority climbs incrementally from base toward P0 across the whole runway, clamping to P0 at/after the due date. So a task planned far in the future starts climbing early and gradually (a long horizon implies commensurate effort), while a short-runway task climbs quickly — both escalate over their own span. The curve is configurable (start_fraction, gamma shape, max_runway_days cap). Ordering uses effective priority, then soonest due date, then base priority, then age.
| Tool | Description |
|---|---|
memory_queue_get_next(project?, source?) |
Get next unclaimed item by effective priority |
memory_queue_claim(item_id, session_id) |
Claim item for your session |
memory_queue_release(item_id, session_id) |
Release claimed item back to pending |
memory_queue_complete(item_id, session_id, outcome_notes?) |
Mark item as completed |
memory_queue_skip(item_id, reason?, session_id?) |
Skip item (no longer relevant) |
memory_queue_list(status?, priority?, source?, project?) |
List queue items with filters (shows effective priority + due date) |
memory_queue_stats() |
Queue counts by status, priority, source, and due-date pressure |
memory_queue_reprioritize(item_id, new_priority) |
Change item base priority |
memory_queue_set_due_date(item_id, due_date?) |
Set or clear a due date (drives escalation) |
memory_queue_requeue(archived_id) |
Move archived item back to queue |
memory_queue_populate(items, refresh_id?) |
Bulk add/update queue items (accepts due_date per item) |
Analytics
| Tool | Description |
|---|---|
memory_effectiveness_report(days?) |
Memory usage patterns, injection effectiveness, and suggestions |
memory_injection_metrics_trend(days?) |
Daily injection metrics over time for tracking improvements |
memory_background_report(days?) |
Background process activity |
memory_tool_metrics(action?, since_hours?) |
MCP tool call timing metrics |
memory_vector_index(action?) |
Manage vector search index |
Fact Provenance & Truth Maintenance
Requires the Rust memory-server binary — see
docs/adr/0001-fact-provenance-and-truth-maintenance.md
for the full design. These tools maintain a deduplicated, derivable set of
current facts (structured subject predicate object triples) alongside
the episodic memory store above — distinct from, and complementary to,
semantic recall. Not available when running the Python-only server (the
schema is present so the database never drifts, but the engine is Rust-only).
Per docs/adr/0002-fact-provenance-as-mandatory-write-path.md,
untrusted/parsed-content ingestion (memory_ingest_file, memory_import_memories,
the daemon's session/Claude-Code observers, and the bulk-ingest GitLab summarizer)
asserts a provenance fact for every memory it produces automatically — this is not
something callers of those tools need to invoke themselves.
| Tool | Description |
|---|---|
memory_observe(subject, predicate, object, source_episode_id, confidence?, source_kind?, extractor?) |
Assert a structured fact. Use source_kind="untrusted" for unverified tool/web/file content |
memory_query(subject?, predicate?, object?, as_of?) |
Read-only pattern match over the maintained fact set, optionally at a past point in time |
memory_why(fact_kind, fact_id) |
Full proof tree down to base facts, both structured and natural-language. Returns found: false (never fabricated) if unestablished |
memory_what_if(facts) |
Explore hypothetical consequences in a scratch evaluation; never writes to the real store |
memory_retract(base_fact_id, reason?) |
Retract a fact; everything derived from it is invalidated unless an alternate derivation survives |
memory_install_rules(batch_id, rules) |
Install a stratified-Datalog rule batch (entity-relationship closures, exclusivity checks) |
memory_uninstall_rules(batch_id) |
Uninstall a rule batch and retract everything it derived |
memory_why_memory(memory_id) |
ADR 0002: why does this memory exist and is it still trusted — proof for its own ingestion provenance plus any facts derived from its content, in one call |
Utilities
| Tool | Description |
|---|---|
memory_ingest_file(file_path) |
Manually ingest a markdown file |
memory_enrich_entity(entity_ref, project?) |
Fetch GitLab metadata |
memory_bootstrap_memory(path?) |
Scan project files for initial facts |
memory_log_session(summary, learnings?, entities?) |
Log session summary |
memory_memory_status() |
Check system health and queue status |
memory_get_proactive_context(query, entities?, project?) |
Get context for proactive injection (plugin use) |
Warm Ruby/Rails REPL (opt-in prototype)
memory_workflow actions ruby_repl_start, ruby_repl_eval, ruby_repl_stop
and ruby_repl_list boot rails console once in a detached tmux session and
then evaluate many snippets against it, so agents stop paying a full Rails boot
on every bin/rails runner call. Off by default ([ruby_repl] enabled = false).
See docs/RUBY_REPL.md for when to use it instead of rspec.
GitLab Bulk Ingest (Optional)
Bulk ingest indexes all issues and merge requests from a GitLab project (default: gitlab-org/gitlab) into a separate database, so memory_recall can surface related upstream issues/MRs alongside your personal memories.
It is fully isolated from your main memory: a dedicated SQLite DB and vector store that can be disabled or deleted at any time without affecting your memories.
How it works
- Fetch - pulls issue/MR metadata (title, description, labels, state, dates) via the GitLab API, rate-limited and resumable.
- Keywords - extracts the top ~50 keywords per entity (stop words, URLs, @mentions, and paths stripped). No LLM required.
- Index - embeds title + description + labels with the same local
all-MiniLM-L6-v2model used for memories. - Search - results merge into
memory_recall, scored by vector similarity (weighted0.8vs personal memories) plus a small keyword-overlap boost (0.1 * overlapping_keywords).
All processing is local. The only network calls are to the GitLab API (fetching metadata) and a one-time embedding-model download.
Data isolation
| Main memory | Bulk ingest | |
|---|---|---|
| SQLite | ~/.local/share/opencode-memory/memory.db |
~/.local/share/opencode-memory/gitlab-org.db |
| Vectors | ~/.local/share/opencode-memory/vectors/ |
~/.local/share/opencode-memory/gitlab-org.lance/ |
Bulk-ingested entities are never written into your main memory DB, knowledge graph, or consolidation/archiving flows. They are a read-time overlay only.
Activation
# 1. Create the config file (~/.config/opencode-memory/bulk_ingest.toml)
opencode-memory bulk-ingest config --init
# 2. Fetch all issues + MRs (resumable; safe to re-run if interrupted)
# For gitlab-org/gitlab this is ~580K entities and takes ~2 hours.
GITLAB_TOKEN=<your-token> opencode-memory bulk-ingest fetch
# 3. Build the vector index (embeds locally; ~3 hours for 580K on CPU)
opencode-memory bulk-ingest index --create-index
# 4. Check progress / counts at any time
opencode-memory bulk-ingest status
Once indexed, results appear automatically in memory_recall with match_type: "bulk_ingest" and a bulk_ingest:<project>#<iid> source.
Keeping it up to date
To pick up newly created and recently updated issues/MRs, run an incremental sync. It fetches everything changed since the latest updated_at already in storage, so it catches both new entities and edits to existing ones:
# One-shot incremental sync
GITLAB_TOKEN=<your-token> opencode-memory bulk-ingest sync
# Explicit window
opencode-memory bulk-ingest sync --since 2026-06-01T00:00:00Z
For hands-off operation, the BulkIngestDaemon runs the incremental sync on a schedule (default every 24 hours, configurable via sync_interval_hours), optionally re-indexing new entities after each pass. Schedule it however you prefer (systemd timer, cron, or the daemon's own loop). The sync is poll-based rather than webhook-driven, so a daily cadence is the typical setup.
CLI commands
| Command | Description |
|---|---|
bulk-ingest config --init |
Create the default config file |
bulk-ingest fetch |
Fetch issue/MR metadata (resumable) |
bulk-ingest index --create-index |
Embed entities and build the IVF-PQ search index |
bulk-ingest index --reindex |
Re-embed everything (default skips already-indexed) |
bulk-ingest sync |
Incremental fetch of entities updated since last run |
bulk-ingest status |
Show entity counts, tiers, and DB size |
bulk-ingest enable / disable |
Toggle inclusion in memory_recall |
Configuration
Key settings in ~/.config/opencode-memory/bulk_ingest.toml:
[gitlab]
project = "gitlab-org/gitlab" # any project path
api_rate_limit = 100 # requests per minute
[query]
enabled = true # include results in memory_recall
relevance_weight = 0.8 # discount vs personal memories
[summarizer]
enabled = false # LLM summaries are OPTIONAL and off by default
provider = "none" # title + description is enough for search
Note: LLM summarization is optional and disabled by default. Avoid the
opencodesummarizer provider - it has MCP access and would write entries into your main memory DB.
Disabling
opencode-memory bulk-ingest disable # stop including in recall (keeps data)
# or remove the data entirely:
rm -rf ~/.local/share/opencode-memory/gitlab-org.db ~/.local/share/opencode-memory/gitlab-org.lance
Memory Categories
| Category | Use For |
|---|---|
boot_gate |
STOP trigger shown every session (≤400 chars, points to directive) |
directive |
Full standing instructions (loaded via recall) |
procedure |
How-to knowledge, workflows |
decision |
Architectural choices, design decisions |
blocker |
Obstacles preventing progress |
fact |
Project-specific information |
plan |
Sequence of steps with end state |
goal |
Sustained target/threshold (ongoing, no end state) |
idea |
Future possibilities, deferred considerations |
event |
Significant occurrences |
conversation |
Full conversation content (auto-generated) |
Storage
All data is stored locally:
- SQLite:
~/.local/share/opencode-memory/memory.db- Memories, entities, sessions, FTS index - LanceDB:
~/.local/share/opencode-memory/vectors/- Vector embeddings for semantic search
The daemon automatically:
- Cleans up old resolved blockers (>90 days)
- Archives old conversations (>180 days)
- Compacts LanceDB versions (keeps last 10)
Environment Variables
| Variable | Description |
|---|---|
GITLAB_TOKEN |
Enable GitLab entity enrichment |
HF_HUB_OFFLINE=1 |
Prevent model downloads (use cached) |
TRANSFORMERS_OFFLINE=1 |
Prevent model downloads |
OPENCODE_MEMORY_HOST |
HTTP server bind address (default: 127.0.0.1) |
OPENCODE_MEMORY_PORT |
HTTP server port (default: 9824) |
OPENCODE_MEMORY_API_KEY |
Optional API key for authentication |
OPENCODE_MEMORY_RATE_LIMIT |
Requests per minute per client (default: 60) |
OPENCODE_MEMORY_MAX_BODY_BYTES |
HTTP request body size limit in bytes (default: 1048576); larger bodies get 413, including /mcp tool calls. A value that is not a positive integer logs a warning and uses the default |
SKIP_BOOTSTRAP |
Set to 1 to skip directive bootstrapping prompt in setup script |
Development
# Activate environment
source .venv/bin/activate
# Run tests
python -m pytest tests/ -v
# Lint
ruff check src/
# Type check
mypy src/
# Check memory stats
python -m opencode_memory.cli stats
Contributing
Contributions are welcome — bug reports, feature ideas, docs fixes, and code changes. See CONTRIBUTING.md for development setup, testing, linting, coding conventions, and the merge request process.
Architecture
opencode-memory uses a 3-process architecture to separate concerns:
┌─────────────────────────────────────────────────────────────────────────────┐
│ opencode-memory │
├─────────────────────────────────────────────────────────────────────────────┤
│ ┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ HTTP/MCP Server │ │ Daemon │ │ Background Worker │ │
│ │ (opencode-memory) │ │ (opencode-memory- │ │ (opencode-memory- │ │
│ │ │ │ daemon) │ │ worker) │ │
│ ├─────────────────────┤ ├─────────────────────┤ ├─────────────────────┤ │
│ │ • MCP tool handlers │ │ • File watching │ │ • Embedding compute │ │
│ │ • Request/response │ │ • OpenCode DB poll │ │ • GitLab enrichment │ │
│ │ • Health endpoints │ │ • Queue work items │ │ • Memory linking │ │
│ │ │ │ │ │ • Cleanup/archival │ │
│ │ │ │ │ │ • LLM extraction │ │
│ └─────────┬───────────┘ └─────────┬───────────┘ └─────────┬───────────┘ │
│ │ │ │ │
│ └────────────────────────┼────────────────────────┘ │
│ ┌──────────▼──────────┐ │
│ │ Shared Storage │ │
│ ├─────────────────────┤ │
│ │ • SQLite + FTS5 │ │
│ │ • LanceDB (vectors) │ │
│ │ • Work queues │ │
│ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
| Process | Purpose | Module |
|---|---|---|
| HTTP Server | MCP tools, health endpoints | opencode_memory.http_server |
| Daemon | File watching, DB polling | opencode_memory.daemon |
| Worker | All heavy background processing | opencode_memory.jobs.worker |
This separation ensures:
- API requests are never blocked by background processing
- Worker can crash/restart without affecting the API
- Multiple workers can run in parallel for scaling
See ARCHITECTURE.md for detailed documentation.
Startup mode: always-on vs lazy
The HTTP server keeps the embedding model resident (~2 GB) so semantic search is instant and shared across sessions. The installer lets you choose when the services run:
| Mode | Boot behaviour | First-session cost | Idle RAM |
|---|---|---|---|
| Always-on (default) | Started at login | None | ~2 GB resident |
| Lazy / on-demand | Not started at boot | A few seconds (model load) | 0 until first use |
In lazy mode the services start automatically the first time an opencode
session connects (the memory-mcp.sh wrapper sees the marker at
~/.config/opencode-memory/lazy and runs memory-ctl.sh start), then stay
running until explicitly stopped. This suits low-RAM machines.
Choose lazy at install time interactively, or non-interactively:
OPENCODE_MEMORY_STARTUP=lazy bash scripts/setup.sh
Start/stop/inspect the stack at any time:
scripts/memory-ctl.sh status # services + current mode
scripts/memory-ctl.sh start # start all three (no-op if already healthy)
scripts/memory-ctl.sh stop # stop all three, freeing the model's RAM
From inside a session you can free the RAM on demand with the memory_shutdown
MCP tool, which stops all three services. In lazy mode the next session restarts
them automatically; in always-on mode restart with memory-ctl.sh start.
Operations
Log Rotation
When running as a systemd service, logs are managed by journald. To configure log rotation:
# Check current log size
journalctl --user -u opencode-memory --disk-usage
# Set max journal size (add to ~/.config/systemd/user.conf or override)
# Or create ~/.config/systemd/journald.conf.d/opencode-memory.conf:
cat > ~/.config/systemd/journald.conf.d/opencode-memory.conf << 'EOF'
[Journal]
SystemMaxUse=100M
MaxRetentionSec=7d
EOF
# Or manually vacuum old logs
journalctl --user --vacuum-time=7d
journalctl --user --vacuum-size=100M
For file-based logging, configure rotation in the systemd service:
[Service]
StandardOutput=append:/var/log/opencode-memory/server.log
StandardError=append:/var/log/opencode-memory/error.log
Then use logrotate (/etc/logrotate.d/opencode-memory):
/var/log/opencode-memory/*.log {
daily
rotate 7
compress
delaycompress
missingok
notifempty
create 0640 $USER $USER
}
Monitoring
The HTTP server exposes several endpoints:
| Endpoint | Description |
|---|---|
/health |
Basic health check (returns 200 if healthy, 503 if degraded) |
/stats |
Detailed statistics (memories, cache, queue, links) |
/metrics |
Prometheus-format metrics |
Example monitoring with curl:
# Health check (for load balancers/monitoring)
curl -s http://localhost:9824/health | jq .
# Full stats
curl -s http://localhost:9824/stats | jq .
# Prometheus metrics
curl -s http://localhost:9824/metrics
CLI Tools
# Show comprehensive statistics
python -m opencode_memory.cli stats
# Ingest markdown files
python -m opencode_memory.cli ingest /path/to/notes --recursive
# Archive old memories
python -m opencode_memory.cli cleanup --dry-run
# Enrich entities with GitLab metadata
python -m opencode_memory.cli enrich --limit 100
Troubleshooting
Check service status
Linux (systemd):
# Check all services
systemctl --user status opencode-memory*
# View logs for specific service
journalctl --user -u opencode-memory -f # Server logs
journalctl --user -u opencode-memory-daemon -f # Daemon logs
journalctl --user -u opencode-memory-worker -f # Worker logs
# Restart all services
systemctl --user restart opencode-memory opencode-memory-daemon opencode-memory-worker
macOS (launchd):
# Check all services
launchctl list | grep opencode
# View logs
tail -f ~/.local/state/opencode-memory/server.log
tail -f ~/.local/state/opencode-memory/daemon.log
tail -f ~/.local/state/opencode-memory/worker.log
# Restart all services
for svc in com.opencode.memory com.opencode.memory.daemon com.opencode.memory.worker; do
launchctl unload ~/Library/LaunchAgents/${svc}.plist 2>/dev/null
launchctl load ~/Library/LaunchAgents/${svc}.plist
done
Check memory system health
Use memory_status tool or:
# HTTP health endpoint
curl http://localhost:9824/health
# Detailed stats
curl http://localhost:9824/stats
Prometheus metrics
The HTTP server exposes metrics at /metrics:
curl http://localhost:9824/metrics
Backup and restore
Export and import read and write files on the host, so both need an API key
(OPENCODE_MEMORY_API_KEY or the secrets/api_key file). Without a key the
server refuses them on /tools/* and /mcp alike.
export_memories will not replace an existing file unless you pass
overwrite=true. To limit both tools to one directory, set
[storage] export_dir. Paths are then resolved (symlinks included), a
relative path is taken relative to that directory, the default export file is
opencode-memory-export.json inside it, and any path that resolves outside it
is refused.
# Export all memories
memory_export_memories(output_path="/backup/memories.json")
# Import on new machine
memory_import_memories(input_path="/backup/memories.json", dry_run=True) # Preview
memory_import_memories(input_path="/backup/memories.json") # Actually import
Reset memory database
Linux:
systemctl --user stop opencode-memory opencode-memory-daemon opencode-memory-worker
rm -rf ~/.local/share/opencode-memory/
systemctl --user start opencode-memory opencode-memory-daemon opencode-memory-worker
macOS:
for svc in com.opencode.memory com.opencode.memory.daemon com.opencode.memory.worker; do
launchctl unload ~/Library/LaunchAgents/${svc}.plist 2>/dev/null
done
rm -rf ~/.local/share/opencode-memory/
for svc in com.opencode.memory com.opencode.memory.daemon com.opencode.memory.worker; do
launchctl load ~/Library/LaunchAgents/${svc}.plist
done
Upgrade from older versions
If you're upgrading from a version that used opencode-memory-enrich.service, the setup script will automatically migrate to the new 3-process architecture. Run the setup script again:
curl -fsSL https://gitlab.com/ghavenga/opencode-memory/-/raw/master/scripts/setup.sh | bash
This will:
- Stop and disable the old
opencode-memory-enrichservice - Install the new
opencode-memory-daemonandopencode-memory-workerservices - Start all services
License
MIT
Metadata
Release files for opencode-semantic-memory 0.69.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| opencode_semantic_memory-0.69.0-py3-none-any.whl | Python 3 | none | any | Details for opencode_semantic_memory-0.69.0-py3-none-any.whl |
Release files / opencode_semantic_memory-0.69.0-py3-none-any.whl
| Download URL | opencode_semantic_memory-0.69.0-py3-none-any.whl |
|---|---|
| Size | 2.1 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
28687476fcee0508476be5b5f457e539b0951a2faac437f18f7dd344804220d1
|
|
BLAKE2b-256 checksum How to use checksums |
43db744a8adda84ee020086a34031edf3601f6dd062e5c003957bc89ec9a1ee0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.18
|
Security for opencode-semantic-memory 0.69.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.