Files
yvgude--lean-ctx/docs/reference/20-hermes-context-engine.md
wehub-resource-sync 26382a7ac6
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
JetBrains Plugin / Actionlint (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
CodeQL / Analyze (rust) (push) Waiting to run
JetBrains Plugin / Validation (push) Waiting to run
JetBrains Plugin / Build (push) Waiting to run
JetBrains Plugin / Test (push) Blocked by required conditions
Security Check / Security Scan (push) Waiting to run
CI / Clippy (push) Failing after 15m13s
CI / Test (ubuntu-latest) (push) Failing after 16m1s
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Build (no embeddings / no ORT) (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / Cookbook (Node) (push) Has been cancelled
CI / Pi Extension (Node) (push) Has been cancelled
CI / Rust SDK (lean-ctx-client) (push) Has been cancelled
CI / Embed SDK (lean-ctx-sdk) (push) Has been cancelled
CI / Python SDK (leanctx) (push) Has been cancelled
CI / Hermes Plugin (Python) (push) Has been cancelled
CI / SDK Conformance Matrix (push) Has been cancelled
CI / Coverage (push) Has been cancelled
CI / cargo-deny (push) Has been cancelled
CI / Adversarial Safety (push) Has been cancelled
CI / Benchmarks (push) Has been cancelled
CI / Output-Quality Gate (eval A/B) (push) Has been cancelled
CI / Documentation (push) Has been cancelled
CI / CI Green (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:35:30 +08:00

9.3 KiB
Raw Permalink Blame History

Journey 20 — Hermes Context Engine

You're embedding lean-ctx inside an agent framework rather than calling it as an MCP server. This journey covers making lean-ctx the active context engine for Hermes Agent: it replaces Hermes' built-in ContextCompressor, owns the context window, and gives the agent first-class recall tools to page durable memory back in losslessly.

Source files referenced here:

  • rust/src/tools/ctx_transcript_compact.rscompact_messages, render_result, serialize_transcript (the deterministic compaction core)
  • rust/src/tools/registered/ctx_transcript_compact.rs — the MCP//v1 tool wrapper
  • integrations/hermes-lean-ctx/engine.pyLeanCtxEngine (the ContextEngine adapter)
  • integrations/hermes-lean-ctx/compaction.py — the local Python fallback
  • integrations/hermes-lean-ctx/tools.py, schemas.py — native recall tools
  • integrations/hermes-lean-ctx/transport.py, config.py, presets.py/v1 client, LEANCTX_* config, model-window presets
  • integrations/hermes-lean-ctx/plugin.yaml — the Hermes plugin manifest

0. The mental model — engine vs. MCP server

Every other journey treats lean-ctx as a tool an agent calls. Here it is the other way around: lean-ctx becomes the component the agent loop delegates its context window to.

  • As an MCP server, lean-ctx answers ctx_* tool calls the model decides to make. It never sees the full conversation.
  • As a context engine, the host (Hermes) hands lean-ctx the entire message array on every turn and asks it to compact it. lean-ctx decides what stays verbatim, what becomes a recoverable summary, and what gets offloaded into durable session memory.

Only one context engine can be active at a time, so lean-ctx and Hermes' built-in ContextCompressor (or hermes-lcm) are mutually exclusive.


1. The compaction core — ctx_transcript_compact

What it does: Compacts an OpenAI-format message array deterministically. It keeps the system preamble and a fresh tail verbatim, replaces older turns with a recoverable summary, and offloads the raw turns into lean-ctx session memory so the recall tools (and the autonomy consolidation pipeline) can page them back in.

It is the 77th MCP tool and is exposed on both the MCP surface and the HTTP /v1 tools API, so every client — this plugin, the CLI, other editors — gets the same tested behaviour.

ctx_transcript_compact messages=<OpenAI message array>
                        fresh_tail_tokens=4000      # recent tokens kept verbatim
                        protect_min_messages=6      # min recent messages kept verbatim
                        focus_topic="auth refactor" # optional: bias the summary
Parameter Default Meaning
messages (required) OpenAI-format message array to compact
fresh_tail_tokens 4000 Recent tokens kept verbatim (the fresh tail)
protect_min_messages 6 Minimum recent messages kept verbatim
focus_topic Optional topic to prioritise in the summary

Returns JSON {messages, stats}: the compacted array plus deterministic stats (original_tokens, compacted_tokens, did_compact, …).

Two invariants, enforced and tested:

  1. A tool_call and its tool_result are never split across the compaction boundary. Truncating between them would leave the model with a dangling call.
  2. Output is byte-stable for the same input — no timestamps, counters or randomness — so it preserves the provider's prompt-cache prefix (#498).

Under the hood: compact_messages() (rust/src/tools/ctx_transcript_compact.rs) splits the array into the protected head + tail and the summarizable middle, renders the summary, and returns a CompactResult. The registered wrapper (registered/ctx_transcript_compact.rs) then best-effort offloads the summarized turns into the bound session via ctx_session (as a finding), capped at OFFLOAD_MAX_CHARS (8 000). Offload is skipped when no session is bound (e.g. a one-shot CLI call), so the tool is safe to call anywhere.


2. The plugin — integrations/hermes-lean-ctx

What it does: A thin Python ContextEngine (LeanCtxEngine, engine.py) that Hermes loads via register_context_engine. It is an adapter, not a re-implementation — the heavy lifting stays in the daemon.

Hermes agent loop
   └─ ContextEngine ABC ── LeanCtxEngine (this plugin, thin adapter)
                               └─ leanctx SDK ── HTTP /v1 ── lean-ctx daemon
                                                              └─ ctx_transcript_compact,
                                                                 ctx_search, ctx_knowledge, …
  • compress(messages) keeps the system preamble + fresh tail verbatim and replaces older turns with a recoverable summary. It calls the daemon's ctx_transcript_compact; if the daemon is unreachable it falls back to a pure Python compaction (compaction.py) so the agent loop never breaks.
  • Native recall tools (tools.py / schemas.py) inject ctx_search, ctx_semantic_search, ctx_read, ctx_expand, ctx_knowledge and ctx_summary into the agent's tool list, so the model can page detail back in on demand after a compaction.
  • Cross-session persistence via session lifecycle hooks: resume on start, ctx_summary + a deterministic ctx_handoff ledger on end.
  • Model-window presets (presets.py) infer the context window from the model name until the host calls update_model(context_length=…), which always wins.

3. Setup

# 1. Install the plugin (symlinks this checkout into ~/.hermes/plugins).
cd integrations/hermes-lean-ctx && ./scripts/install.sh

# 2. Start the lean-ctx HTTP tools API (serves /v1; default port 8080).
#    NOTE: the always-on proxy (4444+) does NOT serve /v1/tools — use `serve`.
lean-ctx serve --host 127.0.0.1 --port 8080

# 3. Install the SDK in Hermes' Python.
pip install lean-ctx-client

# 4. Activate the engine in ~/.hermes/config.yaml:
#    context:
#      engine: "lean-ctx"

lean-ctx init --agent hermes prints this same engine-plugin hint, so the onboarding path points here automatically.

If the server is not on the default, point the plugin at it:

export LEANCTX_BASE_URL=http://127.0.0.1:8080
export LEANCTX_TOKEN=<token>     # only if you ran serve with --auth-token

4. Configuration — LEANCTX_* env vars

Read by config.py; Hermes' explicit update_model(context_length=…) always overrides the inferred window.

Variable Default Meaning
LEANCTX_BASE_URL http://127.0.0.1:8080 lean-ctx /v1 base URL
LEANCTX_HTTP_PORT 8080 Port used when LEANCTX_BASE_URL is unset
LEANCTX_TOKEN Bearer token (if serve --auth-token)
LEANCTX_TIMEOUT 30.0 HTTP timeout (seconds)
LEANCTX_CONTEXT_LENGTH 200000 Window used until the host calls update_model
LEANCTX_THRESHOLD_FRACTION 0.75 Fraction of the window at which compaction fires
LEANCTX_PROTECT_FRACTION 0.25 Recent fraction kept verbatim (the fresh tail)
LEANCTX_PROTECT_MIN_MESSAGES 6 Minimum recent messages kept verbatim
LEANCTX_PROTECT_MIN_TOKENS 2000 Minimum tail token budget
LEANCTX_ENABLE_TOOLS 1 Inject native recall tools into the agent
LEANCTX_CORE_COMPACTION 1 Prefer the daemon tool (fallback: local Python)
LEANCTX_WORKSPACE_ID / LEANCTX_CHANNEL_ID Optional routing for multi-workspace daemons

5. Why lean-ctx over the alternatives

built-in ContextCompressor hermes-lcm hermes-lean-ctx
Strategy summarize + drop DAG + SQLite + FTS BM25 + graph + knowledge + semantic + LITM placement
Recall after compaction lossy lossless (grep/expand) lossless (ctx_search/ctx_semantic_search/ctx_expand/ctx_read/ctx_knowledge)
Cross-session memory no per-project yes (sessions, knowledge, handoff ledgers)
Determinism / prompt-cache n/a partial deterministic, byte-stable output
Engine location in-agent in-plugin in the lean-ctx daemon (single source of truth)

6. Testing & benchmarks

# Hermetic unit tests (no daemon required):
cd integrations/hermes-lean-ctx && python -m pytest

# Live integration against a real daemon:
lean-ctx serve --host 127.0.0.1 --port 8080 --auth-token test-token &
LEANCTX_LIVE_URL=http://127.0.0.1:8080 LEANCTX_LIVE_TOKEN=test-token \
  python -m pytest tests/test_live_daemon.py -v

benchmarks/ (run.py) is a real, runnable head-to-head harness — token savings, compress() latency and recoverable-recall against the import-guarded ContextCompressor / hermes-lcm. No mock data: unit tests exercise the real compaction logic and a recording gateway; live tests hit a real daemon.


Where the neighbouring topics live

Topic Reference
The /v1 HTTP+SSE contract and SDKs Team, Cloud & CI
lean-ctx serve over HTTP, multi-repo Advanced & Integrations
Sessions, knowledge, handoff ledgers Memory & Knowledge
Writing your own plugins / WASM Advanced & Integrations