9.3 KiB
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.rs—compact_messages,render_result,serialize_transcript(the deterministic compaction core)rust/src/tools/registered/ctx_transcript_compact.rs— the MCP//v1tool wrapperintegrations/hermes-lean-ctx/engine.py—LeanCtxEngine(theContextEngineadapter)integrations/hermes-lean-ctx/compaction.py— the local Python fallbackintegrations/hermes-lean-ctx/tools.py,schemas.py— native recall toolsintegrations/hermes-lean-ctx/transport.py,config.py,presets.py—/v1client,LEANCTX_*config, model-window presetsintegrations/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:
- A
tool_calland itstool_resultare never split across the compaction boundary. Truncating between them would leave the model with a dangling call. - 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'sctx_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) injectctx_search,ctx_semantic_search,ctx_read,ctx_expand,ctx_knowledgeandctx_summaryinto 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:
resumeon start,ctx_summary+ a deterministicctx_handoffledger on end. - Model-window presets (
presets.py) infer the context window from the model name until the host callsupdate_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 |