hermes atlas
310·repos hermes·v0.21.5 ★ star this repo

electricsheephq/lcm-x

LCM-X — Lossless Context Memory eXtension

★ 116 langPython licenseMIT updated2026-11-09

LCM-X is a lossless context memory extension for Hermes Agent that stores raw messages in SQLite and compacts older context into a hierarchical summary DAG. It keeps active context bounded and provides tools to search, inspect, and recover compacted messages and payloads in bounded pages.

  • Preserves raw messages and summary source lineage.
  • Provides bounded recall and context-budget controls.
  • Supports temporal memory and semantic retrieval.

Basic static screen: 5453972 (November 9, 2026), not security certification. Reviewed development main, not necessarily the stable release. Raw SQLite records and externalized payloads can retain secrets; durable redaction is off by default. Summaries and optional embeddings use configured model services.

full readme from github

LCM-X

Lossless Context Memory eXtension

CI Latest tag Python 3.11-3.14 License: MIT

Lossless context memory for Hermes Agent.

Bounded context, unbounded memory. Nothing is ever lost.

Benchmark highlights (OmniMemEval, contributor-run, accepted upstream)

Benchmark LCM-X Placement
BEAM 100K 71.84 1st of 12
BEAM 10M 63.32 1st of 12
LongMemEval 87.00 2nd of 13
LoCoMo 78.83 5th of 15
HaluMem 81.68 5th of 13
PersonaMem v2 33.96 8th of 13

Configuration, evidence and disclosures: benchmarks/RESULTS.md.

LCM-X replaces one-shot active-context compression with a SQLite-backed, DAG-based context engine. It keeps the live prompt bounded, preserves raw messages, and gives the agent tools to recover exact detail after compaction. The promise says exactly what "nothing is ever lost" covers; known exceptions are bugs against it, listed in the latest release's Known issues.

The project name is LCM-X. From v0.24.0 the plugin is named hermes-lcm-x and the runtime context engine lcm-x (#471); this is a breaking change for existing installs — see Migrating from hermes-lcm. Unchanged: lcm.db, the lcm: config block, LCM_* environment variables, the lcm_* tools, /lcm, and the bundled skill name hermes-lcm.

Based on the LCM paper by Ehrlich & Blackman (Voltropy PBC, Feb 2026). Inspired by lossless-claw for OpenClaw. For an interactive visualization of the LCM idea, see losslesscontext.ai. Project site: losslesscontext.com.

What we are building and why: VISION.md. What ships when: ROADMAP.md and the tracker #658.

Table of contents

What it does

Hermes Agent's built-in compressor is a practical continuity layer: when the prompt crosses its configured threshold, it prunes older tool results, asks an auxiliary model to summarize the middle/older conversation, and rebuilds the active prompt from that summary plus a protected recent tail. The original session rows can still live in Hermes state.db and remain searchable through host tools such as session_search, but the model's active context no longer contains the compacted turns verbatim or a structured drill-down path back to them.

LCM-X instead:

  1. Persists messages in a plugin-local SQLite store with FTS metadata.
  2. Compacts older context into depth-aware summary nodes.
  3. Condenses summaries into a hierarchical DAG as they accumulate.
  4. Assembles active context from system prompt, highest-value summaries, and the protected fresh tail.
  5. Provides recall tools so agents can search, inspect, and expand compacted material without flooding the main prompt.

Nothing is lost in normal operation. Raw messages stay recoverable in bounded pages, summaries retain source lineage, and oversized externalized payloads keep stable refs for later expansion.

Standard compression

LCM compression

Core capabilities:

  • SQLite message store - preserves raw messages before compaction
  • Summary DAG - builds depth-aware summary nodes over compacted history
  • Bounded recovery - pages raw messages, child summaries, and externalized payloads instead of dumping everything into the prompt
  • Agent tools - lcm_grep, lcm_recall, lcm_query_state, lcm_compute, lcm_compile_evidence, lcm_evidence_pack, lcm_retrieve, lcm_recent, lcm_load_session, lcm_describe, lcm_expand, lcm_expand_query, lcm_status, lcm_inspect, lcm_doctor, and the opt-in lcm_memory
  • Source-aware retrieval - filters raw rows and summaries by descendant source lineage
  • Session controls - ignore noisy sessions or keep sessions read-only with glob patterns
  • Large payload controls - externalize oversized tool/media/raw payloads and protect SQLite from inline media-ish base64 blobs
  • Sensitive-pattern controls - two independent layers: opt-in durable redaction of API keys, bearer tokens, passwords, and private keys before LCM stores or summarizes them (off by default — the durable store is lossless), and automatic protection of the copies sent to known cloud embedding providers
  • Diagnostics - runtime health, database checks, optional /lcm slash commands, backup-first repair/rotate paths

Beyond the core loop, three feature families extend LCM from a compression layer into a memory system: large-output externalization and context-budget controls (giant tool results move to recoverable refs instead of crowding the prompt), temporal memory (day/week/month rollups plus natural-time recall through lcm_recent), and semantic retrieval (automatic local recall with in-process FastEmbed, plus lcm_grep semantic/hybrid modes and explicit Voyage, OpenAI-compatible or Ollama options). See the Feature overview for what each family does and why, and Agent configuration profiles for copy-paste setups per agent type. Externalization and temporal rollups are on by default.

Project status

The latest stable release is v0.29.1 at 3a4a08ff501347a79669fab51444c471b09b551d, released 2026-11-03 (plugin hermes-lcm-x, engine lcm-x). Compaction gets past a row the host still holds, keeps its 10-minute pause, and summarises in store order, and a restart after a cleanup pass no longer stores the conversation again. See project status for the release summary, the disclosed qualification decisions and current work. It requires Hermes 0.21.3 or newer (#1080). It carries the v0.23.2 lossless default (durable sensitive-pattern redaction is opt-in; cloud-embedding privacy is an independent flag that transforms only the provider-bound copy — see "Sensitive-pattern redaction" below), the PEM private-key redaction hardening, loud privacy-policy failures on the recall path, and the rc-first release gauntlet, while preserving the 15-tool surface, schema-5 core store, and default-off experimental features.

Stable release identity and the continuing main development line are separate proof planes; do not describe an arbitrary main checkout as the installed stable product.

v0.29.1 was cut from the release/v0.29.x maintenance branch, not from main; v0.29.x patch releases are cut from that branch. The main line carries the forward identity hermes-lcm-x v0.30.0 — nothing lost — which the v0.30.0-rc1 tag carries first. That tag is cut from main, while v0.29.2 comes from release/v0.29.x. An identity is never a restamp of any past commit's own recorded identity (#385 fixed the earlier drift). The manifest declares 16 tools; runtime discovery advertises 13 by default, or 16 with assertion, adaptive-retrieval and entity flags on.

Every compaction LCM-X starts today runs on the turn thread, so the user waits for it. v0.25.0 makes a hidden backlog drain at turn pace (#597); v0.31.0 removes the wait by preparing summaries in the background and publishing them at the threshold (#787). Recall is re-baselined on the shipped default configuration. See VISION.md, the roadmap, the tracker #658, Current project state and Benchmark methodology for current identities, gaps, and proof boundaries.

Results

BEAM 100K 1st of 12; BEAM 10M 1st of 12 among backends in the upstream reproduced Result Summary. See the benchmark highlights above and results, configuration, evidence and disclosures.

LCM vs built-in compression

Hermes core may persist original conversation history in state.db before built-in compression rewrites the active prompt. Built-in compression can still be lossy in the active context, but previous content may be recoverable later through host-level history tools such as session_search.

LCM-X is different because recall is part of the active context engine:

  • plugin-local store and DAG built specifically for drill-down
  • current-session retrieval through LCM tools, not an auxiliary cross-session search step
  • explicit source-lineage and session-boundary rules

Position LCM around retrieval quality, autonomy, and drill-down behavior. Do not claim that Hermes core has no persisted record of pre-compression history.

Quick start

Prerequisites

  • Hermes Agent 0.21.3 or later (requires_hermes in plugin.yaml; LCM-X needs the plugin register_hook API)
  • Python 3.11+
  • No required third-party runtime dependencies

tiktoken is used if available; otherwise LCM falls back to character-based token estimates. /lcm status and /lcm doctor show which one is active (token_counter: tiktoken or token_counter: char_estimate; not_loaded before anything has been counted in the process). regex is used if available to apply timeouts to message ignore patterns; without it, message-level regex filtering is disabled with a warning rather than running unbounded stdlib re matches.

Install the plugin

Catalog installs use the generated runtime payload pinned to an immutable dist commit.

Canonical install path: clone LCM-X as a general user plugin into plugins/hermes-lcm-x.

git clone https://github.com/electricsheephq/lcm-x \
  "${HOME}/.hermes/plugins/hermes-lcm-x"

For a profile-specific install:

git clone https://github.com/electricsheephq/lcm-x \
  "${HOME}/.hermes/profiles/myprofile/plugins/hermes-lcm-x"

From an existing checkout, install a symlink:

./scripts/install.sh

# Optional profile-aware install:
HERMES_PROFILE=myprofile ./scripts/install.sh

The installer requires canonical lowercase, unpadded ASCII profile names (1–64 letters/digits, with _ or - allowed after the first character). Use HERMES_PROFILE=default ./scripts/install.sh for Default, or HERMES_PROFILE=work ./scripts/install.sh for padded Work. Other spellings are refused before links or host calls; the native Hermes CLI still accepts its aliases.

Run scripts/install.sh even when the checkout already lives at the canonical plugin path. It leaves that checkout in place and exposes the bundled hermes-lcm skill in the matching global/profile skills/ directory. The installer preflights both paths and refuses conflicts before creating links. When it finds a pre-0.24 install (plugins/hermes-lcm, or a config.yaml that enables hermes-lcm / selects context.engine: lcm), it prints the migration steps below and never deletes the old directory. With Hermes available it enables the core through the host (which updates config.yaml) and natively installs the optional local-embeddings companion with host-owned consent. A separate older copy prevents automatic core enablement until migration. HERMES_PROFILE selects a named profile; with a root-shaped home, unset or default selects the root, even with a sticky active profile. HERMES_HOME sets that install root. Printed host commands preserve both selections. A profile-shaped HERMES_HOME=<root>/profiles/<name> selects that profile; a conflicting HERMES_PROFILE is refused before links. Companion failure leaves a successful core install usable with full-text recall and prints a retry command; context-engine selection remains manual.

Local semantic recall is enabled by default with fastembed. The model config stays empty; FastEmbed resolves it to BAAI/bge-small-en-v1.5. An explicit model wins with an explicit provider; a runtime model alone stays disabled/unconfigured. Non-local providers require LCM_EMBEDDINGS_ENABLED=true, an explicit model and their existing privacy/consent steps. Invalid enable values fall back to OFF with a warning; a set-but-blank value also means OFF, without a warning, so leave the variable unset to keep the local default. The companion uses the host's normal dependency consent; see installation and recovery. After it is enabled and Hermes restarts, model warmup and index catch-up run in the background. While the dependency, model or index is unavailable or preparing, recall uses full text with degraded=true, a reason and actual arm coverage; queries never wait for a model download or a backfill (a recall that finds no local model starts the background preparation, which may download it). /lcm doctor distinguishes model readiness from summary/chunk coverage. Existing rows and vectors are preserved. Set LCM_EMBEDDINGS_ENABLED=false to opt out without loading or downloading a model. Under the product default, hosts with at most 2 effective CPUs use full-text recall immediately while background catch-up continues. An explicit LCM_EMBEDDINGS_ENABLED value or programmatic config keeps control; see small-host behavior.

Optional: free Voyage AI embeddings

For stronger recall, Voyage AI offers every account free tokens per model; local FastEmbed stays the default and needs no account.

export LCM_EMBEDDINGS_ENABLED=true
export LCM_EMBEDDING_PROVIDER=voyage
export LCM_EMBEDDING_MODEL=voyage-4-lite
export VOYAGE_API_KEY=...
export LCM_ENABLE_SLASH_COMMAND=1   # registers /lcm for the setup commands

After setting these in Hermes' environment and restarting, run /lcm embed warmup, /lcm embed backfill (dry run), /lcm embed backfill --apply, then /lcm status. See Voyage setup and key creation and cloud privacy.

Activate it

The plugin has two names:

  • plugin manifest name: hermes-lcm-x
  • runtime context engine name: lcm-x

Both must be configured:

plugins:
  enabled:
    - hermes-lcm-x

context:
  engine: lcm-x

Restart Hermes after changing plugin or context-engine config.

Verify it loaded

Run:

hermes plugins list

Expected signals:

  • plugin list includes hermes-lcm-x
  • selected context engine is lcm-x
  • tool list includes lcm_grep, lcm_recall, lcm_recent, lcm_load_session, lcm_describe, lcm_expand, lcm_expand_query, lcm_compute, lcm_compile_evidence, lcm_evidence_pack, lcm_status, lcm_inspect, and lcm_doctor (13 by default)
  • the normal available-skills index includes hermes-lcm; current hosts can also resolve the explicit plugin-qualified skill hermes-lcm-x:hermes-lcm

On the main line, output with all three opt-in flags enabled is:

Plugins (1):
  ✓ hermes-lcm-x v0.30.0 (16 tools)

Provider Plugins:
  Context Engine: lcm-x

Older v0.23.x stable tags instead report hermes-lcm v0.23.x (15 tools) and engine lcm; verify the loaded commit before treating either string as release proof.

For source checkouts, lcm_status, /lcm status, lcm_inspect, lcm_doctor, and /lcm doctor also report the loaded plugin path and best-effort git identity: plugin_git_commit, plugin_git_branch, and plugin_git_dirty.

If startup logs say LCM tools are available through context-engine schemas or mention the Path B fallback, that is expected on older Hermes hosts such as Hermes Agent v0.16. The 13 default lcm_* tools and any flag-enabled tools remain available through the context-engine path; standalone plugin-registry registration is not required there.

Update it

If you cloned directly into the plugin directory:

cd "${HOME}/.hermes/plugins/hermes-lcm-x" && git pull --ff-only

For a profile-specific install:

cd "${HOME}/.hermes/profiles/myprofile/plugins/hermes-lcm-x" && git pull --ff-only

A clone from v0.23.x or earlier still lives at plugins/hermes-lcm. Stop Hermes, update that clone in place (Hermes matches the manifest name, not the directory), then make the config change in Migrating from hermes-lcm before you start Hermes again:

cd "${HOME}/.hermes/plugins/hermes-lcm" && git pull --ff-only
# profile-specific:
cd "${HOME}/.hermes/profiles/myprofile/plugins/hermes-lcm" && git pull --ff-only

If you installed a symlink from a separate checkout, update that checkout and rerun the installer (it is idempotent):

git -C /path/to/lcm-x pull --ff-only && /path/to/lcm-x/scripts/install.sh

A catalog install (hermes plugins install hermes-lcm-x) updates only through the reviewed catalog pin: hermes plugins update hermes-lcm-x. LCM-X ships no self-updater. An install pinned with --ref <sha> (outside the catalog) refuses hermes plugins update; move it to the new reviewed commit with hermes plugins install https://github.com/electricsheephq/lcm-x --force --ref <40-character commit SHA> (the refusal prints that command).

Restart Hermes after updating — and restart every other long-running process that imports Hermes or LCM-X, not only the gateway: for example a separately launched web UI, dashboard, or custom runner. This applies after hermes update as well as after an LCM-X update. A process that keeps running holds the old modules in memory and mixes them with the new files on disk the next time it imports one lazily. See Troubleshooting.

Before upgrading to v0.23.1, resolve the effective database path from LCM_DATABASE_PATH or lcm_status. For an online backup, set LCM_ENABLE_SLASH_COMMAND=true in the old runtime's launch environment, restart that runtime so /lcm is registered, then run /lcm backup. Otherwise stop every SQLite writer and copy that configured database with its WAL/SHM companions as one cold set. Check out the exact stable tag/SHA, restart Hermes, and verify the loaded path, version, database path, schema 5, and all 15 tools. No manual core migration is required for the upgrade itself. Existing cloud vectors from a pre-v0.23.1 identity require privacy-policy warmup, an identity check from the warmup/profile output, and the finite monotonic dry-run/apply loop in the operator guide before semantic coverage is complete.

Cloud embeddings are a separate opt-in. Provider-bound copies use the configured sensitive-pattern catalog by default, while durable messages and FTS remain raw. LCM_EMBEDDING_PRIVACY_ENABLED=false explicitly opts out and binds vectors to the distinguished privacy:off revision. When provider-copy privacy is on, the catalog must be nonempty and contain only supported pattern names (any unknown name fails warmup and dispatch loudly). Dry-run the backfill first; raw-chunk cloud backfill also requires explicit raw-text consent. See the operator upgrade and privacy notes before selecting a cloud embedding provider.

Migrating from hermes-lcm (v0.23.x and earlier)

BREAKING in v0.24.0. The plugin manifest is now hermes-lcm-x and the context engine lcm-x (#471). Hermes matches plugins.enabled against the manifest name, not the install directory, so a config that enables only hermes-lcm stops loading LCM-X after an update. Run exactly one LCM copy, and change the config while Hermes is stopped:

  1. Stop Hermes (every process that uses this profile).

  2. Update the code: git pull --ff-only in the existing clone (it may stay at plugins/hermes-lcm; see Update it), or install a new checkout with scripts/install.sh.

  3. Config: in plugins.enabled, replace hermes-lcm with hermes-lcm-x, and set context.engine: lcm-x:

    plugins:
      enabled:
        - hermes-lcm-x
    context:
      engine: lcm-x
    
    • If a separate older copy is still installed (for example v0.23.x left at plugins/hermes-lcm next to a new plugins/hermes-lcm-x), do not keep both names enabled: both copies would load. LCM-X detects that state, logs Another LCM generation is already loaded, and stays inert so only one copy writes lcm.db, but LCM-X itself is then not running.
    • Keeping hermes-lcm listed next to hermes-lcm-x is harmless only when plugins/hermes-lcm is this same checkout (an in-place clone, or the link scripts/install.sh reuses): there is one physical copy.
    • The legacy context.engine: lcm still selects LCM-X through an alias, but logs a once-per-process DEPRECATED LCM-X config warning and reports an identity_migration field in lcm_status and a warn check in lcm_doctor naming the exact change.
  4. Start Hermes and verify: hermes plugins list shows hermes-lcm-x as enabled and hermes-lcm as not enabled (an old directory kept for rollback may still be listed), and the log shows LCM plugin loaded — lossless context management active.

  5. Later, by hand: after verifying, remove a separate old directory (and its skills/hermes-lcm link). Keep it until then for rollback (a rollback also needs the steps in the operator guide). The installer never deletes anything. Host-owned core enablement updates config.yaml, except when a separate older copy requires the manual migration above first.

If the config is not updated: Hermes logs Context engine 'lcm' not found — falling back to built-in compressor and runs without LCM-X. The existing lcm.db is untouched, but turns handled while Hermes runs without LCM-X (built-in compressor) are not in lcm.db and their compacted content may not be recoverable. Therefore update the config before restarting Hermes after the update.

Managed fleets (PCS / managed-plugin payloads) stay pinned to v0.23.x until their config stages hermes-lcm-x in plugins.enabled. That staging is a separate fleet-migration change owned outside this repository.

Hermes plugin catalog and install-scanner notes

The catalog build follows the plugin-catalog admission rules: no self-updater (scripts/update.sh was removed; catalog installs update only through hermes plugins update hermes-lcm-x), provides_hooks matches what register() registers (pre_llm_call, post_llm_call, subagent_start, subagent_stop, all through ctx.register_hook), and hermes plugins validate reports the install scanner verdict safe: zero dangerous and zero caution (high-severity) findings. Planted-secret fixtures build their PEM markers by string concatenation and the bench env captures avoid piping the env listing, so test data is not read as a real key or an environment dump.

The scanner still lists informational (medium/low) findings; none affects the verdict:

  • embedded_private_key, hardcoded_secret, openai_key_leaked — planted, non-functional fixtures in tests/ that prove redaction and privacy controls.
  • python_subprocess, hex_encoded_string, uv_run, path_traversal — benchmark and release-gauntlet harnesses under bench/ and benchmarks/ that drive local Hermes runs; not loaded by the plugin.
  • git_clone, unpinned_pip_install, dump_all_env — install/contributor docs and CI (pip install pytest numpy), plus one doc describing an env dump; documentation only.
  • eval_string, proc_access, system_passwd_access — test assertions that such strings are rejected or handled.
  • oversized_file, oversized_bundle, too_many_files — the repository ships docs images, large test modules, and benchmark tooling with the plugin.
  • agent_config_ref, hermes_config_ref, hardcoded_ip_port (low) — maintainer skills and config examples that reference AGENTS.md, config.yaml, or loopback endpoints.

hermes plugins validate also warns that the 16 provides_tools are not registered by register(): LCM-X serves its tools through the context-engine tool schemas (ContextEngine.get_tool_schemas), which is the intended path.

Commands and tools

Agent tools

Use these tools for current-session recall after compaction. Use Hermes session_search for earlier separate sessions or broad cross-session history outside the LCM database.

Both host paths hide lcm_query_state until LCM_ASSERTIONS_ENABLED=true and lcm_retrieve until LCM_ADAPTIVE_RETRIEVAL_ENABLED=true, plus lcm_memory until LCM_ENTITIES_ENABLED=1. The other 13 schemas remain advertised by default. LCM_DISABLED_TOOLS hides any named tool, including a flag-enabled tool. Restart after changing the loaded flags. The manifest still declares all 16 tools.

Tool Use
lcm_grep Search current-session raw messages and summaries. Opt into `content_scope='externalized'
lcm_recall Search the entire memory across ALL conversations and all time by meaning. Fuses full-text, summary-vector, and chunk-vector arms with RRF, then applies a soft current-conversation (scope_bias) and recency prior. Returns bounded summary and verbatim-excerpt hits with lcm_expand handles; uses full text with visible coverage/reason while local semantic recall prepares or is unavailable, and when embeddings are disabled.
lcm_query_state Query the opt-in V4 assertion sidecar for bounded current or historical facts, preferences, recommendations, commitments, actions, and status. Every result carries an exact message ref/span/quote; conflicts stay visible.
lcm_compute Execute supported dates, distinct counts, compatible-unit sums, directed/absolute differences, ordering, and latest-state operations over exact cited evidence. Planning, arithmetic, and trace verification are dependency-free and provider-neutral; ambiguous or incomplete inputs fail closed.
lcm_compile_evidence Validate one bounded provider-neutral semantic proposal against exact stored refs and return a compact evidence brief with explicit sufficiency state and an optional canonical computation. It never returns final prose or treats model claims as finite-coverage proof.
lcm_evidence_pack Hydrate and validate bounded baseline exact refs in the same lcm.db, repair only unique in-window quote spans, preserve occurrence/observation time separation, and optionally emit an immutable canonical computation trace without prose.
lcm_retrieve Opt-in bounded controller for one continuous answerer tool turn. It tracks typed evidence slots, permits at most three targeted calls to existing retrieval tools, accepts only exact observed refs, caps the evidence context, and can finish through lcm_compute. It persists evidence views and traces, never final prose.
lcm_recent Retrieve recent summaries by natural UTC period, preferring ready rollups and transparently falling back to time-bounded leaf summaries.
lcm_load_session Load one ordered raw-message transcript page for an explicit session_id. Continues with after_store_id from next_cursor; opt into exact slice refs with include_exact_ref=true.
lcm_describe Inspect the current-session DAG or preview an externalized_ref without loading full content.
lcm_expand Recover source messages, child summaries, or externalized payloads with pagination. Use store_id to fetch a single raw message from a cross-session lcm_grep result and include_exact_ref=true when its returned slice must be cited or computed.
lcm_expand_query Answer a question using expanded current-session LCM context while returning a bounded answer.
lcm_status Show runtime health, context pressure, config, source lineage, and lifecycle stats.
lcm_inspect Read-only operator inventory for current-session lineage, frontier/fresh-tail metadata, externalized refs/readability, compaction skip/no-op reasons, and matched ignore/stateless patterns. Returns metadata only; use retrieval tools for content.
lcm_doctor Run database, FTS, lifecycle, config, and context-pressure diagnostics.
lcm_memory Default-off read-only lookup/list over this profile. Lookup accepts a name and up to 8 aliases; returns mention counts, event/write-labelled first/last times (carried times marked time_uncertain: carried) and sessions over all matching rows, plus capped cited snippets and compatible assertion facts. Optional question ranks up to 5 mentions across all entity matches, independently of the card limit. List groups assertion subjects by namespace; without assertions there is no registry until E2. No writes or model calls.

Recall skill and policy

LCM-X ships skills/hermes-lcm/SKILL.md plus progressive-disclosure references for configuration, architecture, diagnostics, recall routing, and session lifecycle. The installer links that directory into the active Hermes profile so it appears in ordinary skill discovery. On hosts with plugin skill registration, it is also available explicitly as hermes-lcm-x:hermes-lcm.

The canonical policy is distributed through that bundled skill. It is not injected through pre_llm_call: current Hermes persists hook context in the current user's api_content and replays it as user-authored history. Keeping policy out of that seam prevents attribution errors and one-copy-per-user-turn replay growth while preserving clean transcript and LCM ingest. The policy:

  • treats summaries as recall cues rather than exact proof;
  • prefers newer source-backed evidence and verifies contradictions;
  • teaches narrow FTS query construction and bounded scope selection;
  • routes current compacted, cross-conversation, and recent/time-bounded recall through the appropriate existing tools;
  • does not force a tool call when the current context is already sufficient.

The canonical bytes live in skills/hermes-lcm/references/recall-policy.md. The active-LCM pre_llm_call hook remains available for bounded pre-answer evidence when that feature is explicitly enabled, but it never prepends the recall policy. Older hosts without skill or hook registration keep their existing schema-driven behavior.

Slash commands

Slash commands are disabled by default. Enable them only in trusted operator contexts:

export LCM_ENABLE_SLASH_COMMAND=1

Available commands:

  • /lcm or /lcm status - current runtime/session status
  • /lcm doctor - read-only health checks
  • /lcm doctor clean - read-only scan for obvious junk/noise session candidates
  • /lcm doctor clean apply - backup-first cleanup for safe pattern-matched candidates; requires LCM_DOCTOR_CLEAN_APPLY_ENABLED=true
  • /lcm doctor repair - read-only SQLite/FTS repair diagnostics
  • /lcm doctor repair apply - backup-first SQLite/FTS repair
  • /lcm doctor source - read-only scan for legacy blank-source rows
  • /lcm doctor source apply - backup-first normalization of legacy blank-source rows to unknown
  • /lcm doctor retention - read-only retention analysis
  • /lcm backup - timestamped SQLite backup. The snapshot holds the database only; copy the externalized payload directory named in the output alongside it when moving a backup to another host.
  • /lcm rotate - read-only preview of an in-place tail-preserving compact of the active session
  • /lcm rotate apply - refused on Hermes (#1141); use /compress here N
  • /lcm help - command help

Apply paths are intentionally narrow and backup-first. Start with diagnostics before cleanup or repair.

Rotate: read-only preview

/lcm rotate previews the tail-preserving frontier for the active session without changing session_id or conversation_id. It reports the configured fresh tail, pre-tail message count, current and planned lifecycle frontier, and rolling backup path. The preview refuses ignored or stateless sessions.

/lcm rotate apply is refused on Hermes (#1141): Hermes reloads its own transcript after a restart, so advancing the frontier without summaries leaves rows LCM-X cannot map. No backup is created and no lifecycle state is changed. Use /compress here N to summarise everything except the most recent N messages. If an earlier LCM-X already rotated this session and Hermes restarted since, compaction can stop on rows below that frontier and /compress here N will not repair it: start a new session with /new (raw rows stay recoverable via lcm_load_session; in-place fix tracked in #1184). Raw source rows remain recoverable through lcm_load_session and lcm_expand.

lcm_status and /lcm status still surface last_rotate_at and the rolling backup path for existing stores.

Configuration

The generated scalar environment reference lists every scalar registry key, type, declared default, diagnostic range/choice, and deprecation release. The tables below provide behavior and tuning guidance.

Most installs only need plugins.enabled and context.engine: lcm-x.

In Hermes Desktop, Settings ▸ Plugins ▸ hermes-lcm-x offers masked inputs for VOYAGE_API_KEY and LCM_EMBEDDING_API_KEY; secret values are stored in the profile's .env, never config.yaml.

LCM_SQLITE_MMAP_SIZE sets the SQLite memory-map size in bytes (default 268435456, unchanged). On SD-card or single-board hosts, set LCM_SQLITE_MMAP_SIZE=0: a storage read error on a mapped page kills the process with SIGBUS, while 0 returns a catchable I/O error. Invalid or negative values use the default with one WARNING per process. /lcm doctor shows the effective sqlite_mmap_bytes value; SQLite may cap it below the requested size.

Common settings

Variable Default Use
LCM_CONTEXT_THRESHOLD 0.75 Fraction of the context window that triggers LCM compaction; the survival fit (LCM_SURVIVAL_FIT) protects against overflow, so the trigger does not need to be low for safety
LCM_ABSOLUTE_THRESHOLD_TOKENS 0 If > 0, force compaction at this absolute prompt-token count instead of context_length × LCM_CONTEXT_THRESHOLD. Cross-model context-health setpoint (common coding default: 130000) so large windows do not delay compaction and degrade recall. Also settable as lcm.context_threshold_tokens in config.yaml; the environment variable wins when it holds an integer (0 turns the YAML value off); an empty or non-integer value falls back to the YAML key.
LCM_MODEL_THRESHOLDS empty Per-model threshold overrides. Format: "glm-5.2:0.70,glm-5.2-1M:0.25". Keys matched as substrings (longest wins). Also settable as lcm.model_thresholds in config.yaml.
LCM_FRESH_TAIL_COUNT 24 Recent messages protected from compaction
LCM_FRESH_TAIL_MAX_TOKENS 24000 Token cap for the protected fresh tail; while LCM_FRESH_TAIL_COUNT is nonzero it always retains the newest message and complete assistant/tool-result groups (LCM_FRESH_TAIL_COUNT=0 keeps no fresh tail, whatever the cap). 0 removes the explicit cap (on a window of 50k tokens or more the engine then derives one at half the window)
LCM_FRESH_TAIL_PRESSURE_YIELD_ENABLED true Default-on: when compaction is deadlocked because the count-protected tail covers the whole over-threshold session (#441), the tail yields to a derived token bound so compaction can progress; false restores the strict count tail (rollback switch)
LCM_FRESH_TAIL_PRESSURE_YIELD_MIN_OBSERVATIONS 3 Consecutive tail-blocked compaction attempts under host-observed pressure before the yield engages; any attempt not blocked by the tail resets the count; 1 yields on first observation
LCM_INCREMENTAL_MAX_DEPTH 3 Max DAG condensation depth (-1 = unlimited, 0 = leaf only); enables hierarchical summarization
LCM_LEAF_CHUNK_TOKENS 24000 Raw-backlog floor before leaf compaction; with dynamic chunking enabled, the base chunk target
LCM_LEAF_TARGET_RATIO 0.20 Leaf summary target as a share of the leaf's source tokens (> 0 and <= 1); target = min(MAX, max(MIN, int(source_tokens * RATIO))) and the first summary call gets max_tokens = 2 × target (#614). Setting only the ratio has no effect while MIN equals MAX and emits a warning; set LCM_LEAF_TARGET_MAX_TOKENS above MIN to enable the ratio
LCM_LEAF_TARGET_MIN_TOKENS 2000 Floor of the leaf summary target (>= 1); a minimum above the default cap also needs LCM_LEAF_TARGET_MAX_TOKENS, otherwise it resets with a warning naming that key
LCM_LEAF_TARGET_MAX_TOKENS 2000 Cap of the leaf summary target (>= the floor); an out-of-range value of any of the three keys falls back to its default with a config warning
LCM_DYNAMIC_LEAF_CHUNK_ENABLED false Enable backlog-sized leaf chunk targets; when off, one leaf takes at most max(LCM_LEAF_CHUNK_TOKENS, LCM_DYNAMIC_LEAF_CHUNK_MAX) of the oldest non-tail raw backlog, capped at 40% of a known window
LCM_DYNAMIC_LEAF_CHUNK_MAX 40000 Upper bound for dynamic leaf chunk targets
LCM_THRESHOLD_FULL_SWEEP_ENABLED true At threshold, run one synchronous bounded sweep that drains chunked raw history before publishing one new active context; false compacts only until pressure falls below the trigger
LCM_SUMMARY_PREFIX_TARGET_TOKENS 0 Sweep-only summary-frontier target; 0 uses one leaf budget, capped at a quarter of the compaction threshold
LCM_BACKGROUND_LEAF_PREP_ENABLED false #787 slice 1: after each turn, summarise the oldest raw backlog above the frontier off-turn (never the fresh tail) once the host count passes P = threshold − 0.15 × window, and at the threshold crossing publish those prepared leaves through the fenced publication, with no model call, down to L = threshold − max(2 × chunk, 0.1 × window); still above L, the turn runs the normal path. Prepared leaves are in memory only. Off on windows where P ≤ 0.30 × window or L ≤ 0, when auxiliary.compression.max_concurrency is 1, and while ignore-message patterns are set. /lcm status reports background_leaf_prep counters while on
LCM_BACKGROUND_LEAF_PREP_MAX_CALLS 8 The background preparation's own spend guard: summariser calls per window (it also waits while the foreground guard has fewer than 2 calls left, and uses its own prep: circuit keys)
LCM_BACKGROUND_LEAF_PREP_WINDOW_S 600 The window of that guard, in seconds
LCM_FOREGROUND_SOFT_SECONDS 60 Sweep soft target, counted from compress() entry: after the first stored leaf or condensed node, no summariser call starts unless its recent duration says it ends by then (0 = none; at most the hard bound)
LCM_FOREGROUND_HARD_SECONDS 120 Sweep hard bound, counted from compress() entry: no summariser call starts unless it is expected to end, with a finalize reserve, by then, and none gets a timeout past it (0 or invalid = 120)
LCM_NEW_SESSION_RETAIN_DEPTH 2 DAG depth retained after manual /new (-1 all, 0 none)
LCM_DATABASE_PATH auto SQLite database path. Empty config resolves to HERMES_HOME/lcm.db; plugin installs or operators may set this env var to another profile-scoped path such as ${HOME}/.hermes/hermes-lcm.db.
LCM_FTS_INTEGRITY_CHECK_INTERVAL_HOURS 24 Minimum hours between startup FTS5 deep integrity-checks (O(index size)). 0 checks every startup; a negative value never checks on startup. Structural checks always run regardless.
LCM_ENABLE_SLASH_COMMAND false Enable the optional /lcm operator command surface

When LCM_FRESH_TAIL_MAX_TOKENS is above 0, the protected suffix must satisfy both the message-count and token bounds. While LCM_FRESH_TAIL_COUNT is nonzero the newest message is never dropped (0 keeps no fresh tail), and a boundary that would begin inside an assistant tool-call/result group is moved back to that assistant even when doing so exceeds a configured bound.

Filtering and storage settings

Variable Default Use
LCM_IGNORE_SESSION_PATTERNS empty Comma-separated session globs excluded from LCM storage
LCM_STATELESS_SESSION_PATTERNS empty Comma-separated session globs kept read-only
LCM_IGNORE_MESSAGE_PATTERNS empty Comma-separated regex patterns; matching message content is excluded from LCM storage
LCM_SENSITIVE_PATTERNS_ENABLED false Opt in to durable deterministic redaction before LCM storage, FTS indexing, summarization, active replay, and externalized ingest payloads; it does not control cloud embedding privacy
LCM_EMBEDDING_PRIVACY_ENABLED unset (auto) Protect provider-bound copies for known cloud embedding providers without rewriting durable data. Set false for an explicit raw-cloud opt-out (privacy:off vector revision); local providers remain unchanged
LCM_SENSITIVE_PATTERNS api_key,bearer_token,password_assignment,private_key Comma-separated named sensitive pattern catalog entries to apply when redaction is enabled
LCM_LARGE_OUTPUT_EXTERNALIZATION_ENABLED true Store oversized ingest payloads, including tool results, media blocks, and generic raw content, in plugin-managed JSON files
LCM_LARGE_OUTPUT_EXTERNALIZATION_THRESHOLD_CHARS 12000 Externalization threshold for normalized payload text
LCM_LARGE_OUTPUT_ACTIVE_REPLAY_STUBBING_ENABLED true Externalize token-heavy textual tool results at ingest; below-threshold size-only host adoption waits for a real compression boundary; historical assembly respects the protected fresh tail; requires large-output externalization
LCM_LARGE_OUTPUT_ACTIVE_REPLAY_STUB_THRESHOLD_TOKENS 10000 First-sight threshold: a new tool result over this many tokens is stubbed at ingest
LCM_LARGE_OUTPUT_ACTIVE_REPLAY_STUB_AGED_THRESHOLD_TOKENS 2000 Aged tier: at a compaction, a tool result outside the fresh tail is stubbed from this many tokens (0 = the first-sight threshold; never above it)
LCM_STUB_FIRST_EXIT_TARGET_RATIO 0 Lowers the stub-first exit's target (#671) to min(built-in, int(ratio x threshold)); 0 keeps the built-in min(threshold - LCM_LEAF_CHUNK_TOKENS, 0.95 x threshold); >= 0 and <= 1, else the built-in target with a config warning
LCM_LARGE_OUTPUT_TRANSCRIPT_GC_ENABLED false Removed in v0.30.0; parsed and ignored (#1246). Setting it true logs one warning per process; summarized tool rows are no longer rewritten
LCM_DOCTOR_CLEAN_APPLY_ENABLED false Permit destructive /lcm doctor clean apply in trusted operator contexts
LCM_EMPTY_LIFECYCLE_GC_ENABLED true Master toggle for automatic pruning of lifecycle rows for sessions that never ingested any messages or summary nodes
LCM_EMPTY_LIFECYCLE_GC_THRESHOLD 200 Number of lifecycle rows at which the GC pass fires
LCM_EMPTY_LIFECYCLE_GC_MAX_AGE_HOURS 24 Automatic GC only deletes empty lifecycle rows at least this old; set 0 only in trusted/test environments that intentionally want immediate empty-row pruning
LCM_DISABLED_TOOLS empty Comma-separated lcm_* tool names excluded from injected tool schemas, refused in handle_tool_call before message ingest, and filtered from plugin-registry registration — disabled tools cost zero tokens per turn. Unset to restore all tools.

Model and timeout settings

Variable Default Use
LCM_SUMMARY_MODEL auxiliary Override summarization model
LCM_SUMMARY_REASONING_EFFORT task/provider default Summary reasoning override: none, minimal, low, medium, high, or xhigh; YAML key: lcm.summary_reasoning_effort. An unsupported value is ignored (falling back to this default) and reported by /lcm status and /lcm doctor
LCM_SUMMARY_FALLBACK_MODELS empty Comma-separated summarization models tried after LCM_SUMMARY_MODEL or the auxiliary task default fails
LCM_SUMMARY_CIRCUIT_BREAKER_FAILURE_THRESHOLD 2 Consecutive failed summarization calls before a route is skipped temporarily
LCM_SUMMARY_CIRCUIT_BREAKER_COOLDOWN_SECONDS 300 Seconds to skip an open summary route before retrying it
LCM_SUMMARY_CIRCUIT_BREAKER_REJECTION_THRESHOLD 6 Consecutive rejected summary results (no usable text, or not shorter than the source) before a route is skipped temporarily
LCM_EXPANSION_MODEL summary model / auxiliary Override lcm_expand_query synthesis model
LCM_EXPANSION_REASONING_EFFORT task/provider default Expansion synthesis reasoning override with the same supported values and the same ignore-and-report handling; YAML key: lcm.expansion_reasoning_effort
LCM_EXPANSION_CONTEXT_TOKENS 32000 Context budget used by the auxiliary LLM for lcm_expand_query
LCM_SUMMARY_TIMEOUT_MS 120000 Timeout for one summarization call (inherits auxiliary.compression.timeout when that is set); the foreground time budget can cut a call shorter
LCM_SHUTDOWN_COMPACTION_WAIT_S 10.0 Seconds shutdown waits for active compaction after taking the lifecycle lease; on timeout connections stay open for a later shutdown retry. New compactions return the host messages unchanged once shutdown starts. Finite nonnegative float, capped at 86400; invalid values use the default.
LCM_SUMMARY_PROMPT_VERSION 1 1 = today's prompts; 2 = the frontier prompt with the focus directives in the policy and a 3× output ceiling
LCM_NATIVE_RECOVERY false removed in v0.25.0; ignored if set (true logs one WARNING each time the plugin loads)
LCM_SURVIVAL_FIT true When compaction cannot bring the returned list under the model window (a publication failure, a sweep deadline, a lock), drop the oldest whole user turns from live context until it fits; an oversized newest turn gets a bounded projection. Nothing is deleted: the rows stay stored and reachable with lcm_grep / lcm_load_session. Logs LCM survival fit applied, warns the user once, and /lcm doctor reports survival_fit as history without changing its status. The lcm_doctor tool returns it in the top-level survival_fit field, or null when no fit is recorded
LCM_SURVIVAL_RESERVE 0.15 Share of the model window the survival fit keeps free for the response and host overhead (the fit target is window x (1 - reserve), minus the observed host overhead)
LCM_EXPANSION_TIMEOUT_MS 120000 Timeout for one lcm_expand_query synthesis call
LCM_CRITICAL_BUDGET_PRESSURE_RATIO 0.0 Disabled at 0.0; when set, permits critical-pressure bypasses for bounded deferred catch-up only

Advanced compaction, assembly, and extraction knobs are defined in config.py.

Sensitive-pattern redaction

LCM is lossless by default: durable messages, FTS rows, summaries, and payloads remain untouched unless `LCM_SENSITIVE_PATTERNS_ENABLED=


README truncated. Continue reading on GitHub