Hermes Context Optimization logo

Hermes Context Optimization

Community
AtlasOmnia
hermes-context-optimization

hermes-context-optimization — Optimize Hermes startup/context payloads, compression, tool-schema loading, memory/skill injection, and visual-context experiments.

Overview

PublisherAtlasOmnia
Repositorydonna-starter
Skill namehermes-context-optimization
Stars
131
Forks
9
Bundled files
13
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • 13 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

    Published by AtlasOmnia on GitHub. Read the source before you install it.

Installation

Install the Hermes Context Optimization AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/AtlasOmnia/donna-starter/tree/main/skills/autonomous-ai-agents/hermes-context-optimization
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/AtlasOmnia/donna-starter.git /tmp/donna-starter
mkdir -p .claude/skills
cp -r /tmp/donna-starter/skills/autonomous-ai-agents/hermes-context-optimization .claude/skills/hermes-context-optimization
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Hermes Context Optimization in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Hermes Context Optimization on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Hermes Context Optimization is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Hermes Context Optimization

Use this when the user asks about Hermes prompt/context size, “hello” startup cost, compression behavior, memory/profile bloat, tool-schema overhead, skill loading, session-store/search-index storage, or multimodal/visual-context approaches such as Snapcompact.

For large state.db search-index maintenance, resumable optimize-storage --no-vacuum runs, foreground timeout handling, and post-run verification, follow references/session-store-optimization.md.

When bounded workers repeatedly fail with session_persistence_failed because a live profile DB is lock-contended, preserve the worktree handoff and use the non-disruptive isolated-HERMES_HOME workflow in references/isolated-worker-state-store.md. Diagnose the profile-specific DB and lock owners first; never terminate a user's live TUI or optimize its store merely to unblock a coding worker.

Operating style for the user

  • Be concise first. If he asks for the savings, give the numbers, not a lecture.
  • Separate hard blockers from engineering choices.
  • Prefer token/accounting estimates grounded in actual provider/runtime data when available; use ratios only when clearly labeled.
  • Do not imply that all startup prompt text is interchangeable. Bucket it by runtime role.

Long standing-goal payloads

When a long /goal prompt is displayed as a compact token such as [[ … [77 lines] … ]], treat that representation as potentially lossy rendered text, not as the authoritative goal body. Hermes' goal state stores the string it receives and does not dereference wiki-style links or recover omitted lines. Before allowing a goal worker to edit anything, require it to read the complete authoritative specification from a file and verify that the first turn contains the expected task scope. Use a short file-backed goal such as: Read and execute the complete goal specification at /absolute/path/goal.md; treat that file as authoritative; do not act on truncated display text. If the worker loads Obsidian or searches for a note matching the compact label, pause/clear the goal: it received the display placeholder, not the task. Do not accept a “goal achieved” result caused only by missing input; require artifact or test evidence.

Reference: references/goal-long-prompt-preservation.md.

Native first-turn inspection

When the question is what Hermes loads or sends on the first message of a fresh session, start with the native surfaces rather than reconstructing the prompt manually:

  • Preflight fixed payload: hermes prompt-size --platform <cli|telegram|discord|...>; add --json for machine-readable output. It runs offline and reports system-prompt tiers, skills, memory/profile, and tool-schema bytes/count using platform-resolved tools.
  • Live request after the first turn: /usage; it separates system prompt, built-in tools, rules, skills, MCP schemas, subagent definitions, memory, and conversation.
  • Treat the displayed token categories as estimates (chars / 4), not exact provider-tokenizer counts. Use provider-reported input tokens for the exact total. Do not present proportionally scaled category estimates as exact tokenizer measurements; label them as approximations if used.
  • Start from the fixed-payload basics: memory block, user-profile block, skills index, core system prompt, built-in tool schemas, delegation schema, MCP schemas, then conversation. If the user asks specifically how large user.md or memory.md is under Mnemosyne, report the live generated prompt blocks even when no literal Markdown files exist.
  • Do not blame an intentionally isolated/test Tool Router merely because the default profile exposes a broad tool surface. First establish the normal baseline composition. Inspect router policy or logs only when the question is about routing, an unexpected regression, or whether schemas should have been narrowed.
  • For finer offline categories, construct the same inspection agent and call agent.context_breakdown.compute_session_context_breakdown(agent, messages=[]). This separates core system prompt, built-in tool definitions, skills, MCP, delegation schema, and combined memory/profile. Remember that this command also uses the rough chars / 4 estimator.
  • In Docker, run the same command through docker exec -it <container> hermes prompt-size ... or docker compose exec <service> hermes prompt-size ....
  • Distinguish a composition report from a raw wire dump: prompt-size does not print the complete prompt contents, and live MCP registration is best verified through /usage.

Full command matrix, Docker examples, and interpretation pitfalls: references/native-context-inspection.md.

Explain schema growth correctly

Tool definitions do not grow merely because a conversation gets older. They are a fixed schema surface for that session/request. The baseline grows when Hermes updates, enabled toolsets, plugins, MCP servers, Office integrations, or dynamic provider tools register additional callable schemas. Prompt caching may reduce repeat billing and latency, but it does not return the occupied context window. State these distinctions before recommending pruning, profile specialization, consolidation, or dynamic routing.

Initial payload triage

When analyzing Hermes startup cost, split the payload into these buckets:

BucketCan be compressed/lazy-loaded?Notes
Core system/developer rulesPartly, but keep authoritative textIdentity, safety, tool policy, authority hierarchy, injection handling, platform rules need reliable system-channel text.
Tool schemasNo, unless tools are prunedProvider needs machine-readable JSON schemas to expose function calls. A screenshot of schemas is not a callable tool registry.
Memory/user profileYesGood candidate for tiering, retrieval, or visual appendix.
Skill directory/indexYesPrefer tiny router/index at startup; load skills on demand.
Environment/project notesYesReference context; retrieve or attach only when relevant.
Prior sessions/compressed historyYesGood candidate for summary + artifact fallback.

Preferred optimization order

  1. Measure the payload composition if tools/logs are available: system text, memory/profile, skill directory, tool schemas, platform metadata.
  2. Prune/gate tool schemas first. Tool schemas often dominate startup cost and cannot be replaced by images.
  3. Shrink the authoritative bootloader to core identity/rules only.
  4. Tier memory/profile into always-loaded core facts vs domain/project facts retrieved on demand.
  5. Gate skill directory behind a tiny class-level index and explicit skill_view loading.
  6. Use visual context/Snapcompact only for reference material, not for authority or tool registration.

Live latency attribution

For gateway or voice latency, measure one clean tool-free turn first and inspect logs from a separate controller afterward. Never have the measured agent call log/file tools inside its own latency probe; those calls add model round trips and invalidate the sample. Break the result into model time, actual tool execution, framework overhead, client-to-gateway transport, STT endpointing, and TTS synthesis/playback. Label boundaries that the current build does not timestamp as unmeasured rather than estimating them.

Tool-heavy turns commonly spend most of their time in the repeated model calls around tools, not in tool execution. Compare single-call turns at several input-token sizes to prove context-pressure effects. Tool Router can reduce schemas and unnecessary tool selection, but it does not shrink accumulated conversation history; pair it with an explicit compression/context policy when history is the bottleneck.

Full calculation method and voice-specific acceptance shape: references/gateway-voice-latency-attribution.md.

Snapcompact / visual-context guidance

Snapcompact-style image context can reduce reference-text tokens by roughly the article’s observed ratio:

text
10,000 text tokens -> 3,279 image tokens ≈ 67.2% reduction

Quick estimate:

text
image_tokens ≈ text_tokens * 0.3279
saved_tokens ≈ text_tokens * 0.6721

For Hermes startup, report realistic savings only for the imageable portion:

  • 4k imageable reference tokens -> saves ~2.7k
  • 8k imageable reference tokens -> saves ~5.4k
  • 12k imageable reference tokens -> saves ~8.1k

Do not claim the entire initial payload can become an image unless tool schemas and authoritative rules are separately handled.

Safe architecture

Recommended shape:

text
minimal authoritative text bootloader
+ typed user prompt
+ minimal/gated tool schemas
+ optional visual reference appendix
+ retrieval/lazy loading for memory, skills, and project/domain context

For local-first setups, consider:

text
snapcompact image -> local vision model extracts relevant section -> main model receives extracted text

This avoids forcing the primary model to OCR every appendix on every turn, but adds latency and OCR risk.

Memory compaction pattern (for reducing initial prompt)

When shrinking always-loaded memory/user_profile:

  • Remove entries that duplicate existing skills, AGENTS.md, or vault docs.
  • Merge overlapping preferences into single compact bullets.
  • Drop stale/session-specific notes and auto-captured noise.
  • Keep only durable behavioral facts that materially affect how the agent acts on every turn.

Operational pitfalls

  • Vague profile labels cause downstream hallucination. A label like "20 years HK experience" is too broad — a model can't tell if it means finance, hospitality, teaching, or import/export, so it guesses. When the user corrects a mistaken extrapolation, trace back to the SOURCE label and make it specific (e.g., "20 years Hong Kong housekeeping/hospitality experience"). Don't just correct the session output — fix the underlying profile data. See references/profile-data-accuracy.md.
  • Memory tool loop trap: calling the memory tool without specifying a valid operation causes repeated identical failures and hard-stops further calls to it in that turn. Always include an explicit action field with concrete old_text/new content; if unsure, use terminal or file inspection first instead of guessing entries. Never ping it as “read current state”; rely on last known list or external checks.
  • Repeated-call guardrail: the same tool call (identical arguments) failing 3 times triggers repeated_exact_failure_block and blocks that tool until you change strategy. When a tool fails:
  • Do NOT retry with identical arguments hoping it will work next time.
  • Read the error message; adjust parameters, shape, or approach before calling again.
  • If the tool does not support what you’re trying (e.g., “read” when only add/replace/remove exist), stop using it for that purpose and switch to an alternative method.
  • Do not over-explain when the user asks for a numerical answer; compute or estimate and answer directly.

Memory compaction (proven batch workflow)

When shrinking memory or user_profile:

  1. Inspect current entries via terminal if possible:
  • python3 -c "import yaml; c=yaml.safe_load(open('~/.hermes/config.yaml')); print(c.get('mnemosyne',{}).get('memory',[]))"
  • If that is empty but you see injected MEMORY/USER PROFILE at startup, those are in mnemosyne's DB (not config.yaml). You must work from the text already injected into your context — there is no read-only memory() call.
  1. Use a single operations batch instead of many small calls:
  • Build one operations list with add/replace/remove entries only for facts you can match as substrings in what's currently injected.
  • Batch rules:
  • Remove: domain notes already covered by skills (business, work, community, browser, content-style).
  • Replace: verbose environment/model/provider notes → compact bullets.
  • Add: only durable behavioral facts that affect every turn.
  • Watch for all-or-nothing behavior: if one replace/remove fails due to a mismatched old_text, the entire batch is rejected — ensure exact substring matches against what you see injected.
  1. Avoid tool-loop traps:
  • memory() has no read-only mode; calling it with action=None or identical arguments repeatedly will trigger repeated_exact_failure_block and halt further calls.
  • If blocked, stop retrying unchanged; switch to terminal inspection or file reads instead.
  1. Expected savings:
  • A well-maintained system memory can be 1–2K chars (around 300–600 tokens). Anything above ~3K is usually hoarding stale task notes that belong in skills, vault files, or session history.

Pitfalls

  • Do not put mandatory behavior rules only inside an image; image text is reference content, not a reliable system-channel control surface.
  • Do not put tool schemas only inside an image; providers require structured schemas for tool calling.
  • Do not attach large visual appendices to trivial greetings unless the provider's image-token accounting and latency justify it.
  • Compression routing pitfall: auxiliary.compression overrides compression.summary_model. If compression seems stuck on an unexpected provider (e.g., DeepSeek), check auxiliary.compression first — it is usually the actual source of truth.

Displayed output vs model-visible context

When the user asks whether a long tool result shown on screen is also fully reread by the model, distinguish the presentation layer from the active prompt. The UI/gateway may display the complete tool output, while Hermes later compacts the model-visible transcript. During context cleanup, the compressor receives the full transcript including tool messages, then performs a cheap pre-pass before any LLM summary: older tool results are replaced with informative one-line records (tool name, operation, exit status or size), identical results are deduplicated while the newest full copy is retained, and old tool-call arguments are truncated. Recent tool results remain verbatim. This is context pruning, not merely a JSON transport separator; JSON is used for message/tool structure and structured summaries, while the token savings come from replacing old content. The original full session remains available in the session store.

Use this concise explanation when asked: “The screen and model context are separate. Hermes can show the full result, but older tool output is later replaced internally with a compact record; recent output stays full.” Do not incorrectly imply that every long result is immediately omitted or that JSON encoding itself causes the reduction.

Compression model requirements (from official behavior)

  • Hermes sends the entire selected middle section in one call_llm(task="compression") request; it does not map-reduce or chunk the middle automatically.
  • Conservative default: give the compressor at least the parent model's effective compression trigger/window. This avoids auto-lowering, truncation, and hidden instruction/summary overhead.
  • Measured narrow exception: a smaller compressor can be valid when the actual middle is calculated and live-proven. Approximate the first-cycle payload as:
text
usable_prompt = parent_context - parent_max_tokens
trigger = usable_prompt × compression.threshold
middle ≈ trigger × (1 - compression.target_ratio)

Then add headroom for the structured-summary instructions, prior summaries, tool metadata, and token-estimation variance. Never describe the raw middle estimate as the whole request size.

  • Shared-compressor rule: size the compressor for the largest active parent plane it serves. A Gemma instance that compresses both an 80K local worker and a 184K cloud orchestrator must satisfy the cloud parent's trigger, not merely the worker's smaller requirement.
  • Keep providers.<custom>.models.<exact-id>.context_length, auxiliary.compression.context_length, and the backend's live loaded context truthful and aligned. Do not advertise a larger context merely to bypass a conservative guard; either load the larger slot or accept an earlier trigger.
  • Verify near the boundary with a real compression-shaped request and inspect returned model, prompt tokens, finish reason, content, and elapsed time.

Ref: https://hermes-agent.nousresearch.com/docs/developer-guide/context-compression-and-caching

Compression runtime behavior

How Hermes actually routes compression requests:

  • The ContextCompressor calls call_llm(task="compression").
  • This is handled by auxiliary_client, which prioritizes the config under:
  • auxiliary.compression.provider
  • auxiliary.compression.model
  • If those are set, they override any compression.summary_model / compression.summary_provider values. This is a common source of confusion: your "compression" block may look correct but be ignored because auxiliary.compression takes precedence.

So the effective path is:

Compression → call_llm(task="compression") → auxiliary_client reads auxiliary.compression.* → uses that provider/model for the summary call

Implication: to debug "compression still using main model", check BOTH:

  • auxiliary.compression (primary routing; this one often wins)
  • compression.summary_model / compression.summary_provider (fallback/legacy; may be ignored)

A frequent scenario:

  • You set a local summary model in compression.*, restart, but you still see traffic on DeepSeek or another cloud provider.
  • Cause: auxiliary.compression.provider is set to that cloud provider and overrides your intent.
  • Fix: align auxiliary.compression with your desired endpoint (e.g., custom:lmstudio-windows), not just the compression.* block.

Compression troubleshooting (silent failures)

If compression appears stuck, still hitting a previous provider, or silently falling back:

  1. Confirm auxiliary.compression is what you expect:
  • Run: python3 -c "import yaml; c=yaml.safe_load(open('~/.hermes/config.yaml')); print(c.get('auxiliary',{}).get('compression',{}))"
  • If provider is not your desired endpoint (e.g., it's deepseek), that is why compression isn't using your local model.
  1. Confirm config:
  • Run: python3 -c "import yaml; c=yaml.safe_load(open('~/.hermes/config.yaml')); print(c.get('compression',{}))"
  • Ensure summary_provider/summary_model are set (e.g., custom:lmstudio-windows), but remember they may be overridden by auxiliary.compression.
  1. Check if compression.summary_model is wired into agent_init.py:
  • Bug (fixed): agent_init.py historically passed summary_model_override=None, ignoring compression.summary_model from config.yaml — so compression always used the main model.
  • Verify fix present: grep "summary_model_override=_summary_model" ~/.hermes/hermes-agent/agent/agent_init.py should return a match. If not, the patch is missing and compression will silently use the main (expensive) model.
  1. Verify the provider block is NOT empty:
  • Run: python3 -c "import yaml; c=yaml.safe_load(open('~/.hermes/config.yaml')); print(c.get('providers',{}).get('lmstudio-windows',{}))"
  • If this prints {} or missing base_url/api_key, compression will silently fail to reach your local endpoint and may fall back to an older/default provider.
  1. Fix empty provider:
  • Use Python YAML read/write (not patch tool on config.yaml).
  • Ensure base_url and api_key are set, e.g.: providers: lmstudio-windows: base_url: http://127.0.0.1:1234/v1 api_key: lm-studio
  1. Restart gateway:
  • launchctl stop ai.hermes.gateway && sleep 3 && launchctl start ai.hermes.gateway
  1. Confirm live:
  • Trigger /compress in a large session.
  • Check logs: grep "2026-06" ~/.hermes/logs/gateway.log | grep -iE "compression|127.0.0.1"
  • If you see 127.0.0.1 in compression lines, it is using the local model.
  • With 148+ skills registered, the skills index alone contributes ~5k tokens to the initial prompt. Before suggesting Snapcompact or memory tiering, prune unused skills — it is the highest-ROI first step. See references/prompt-composition-baseline.md for the measured breakdown and references/skill-pruning-workflow.md for the end-to-end procedure.
  • Post-pruning reality: Even after removing unused tools, disabling 84+ skills, and trimming the index to ~2k tokens, initial prompt is still ~21–22k tokens because tool schemas (~11–13k) dominate. Do not claim further large savings without disabling actual tools or reducing loaded toolsets.
  • Always use live measurements over stale baselines. When asked about context size: run the measurement commands in references/prompt-composition-baseline.md instead of relying on old numbers. Hermes updates frequently change prompt_builder, guidance blocks, and tool wiring — assumptions age fast.
  • Tool-router pattern for reducing initial payload:**
  • Plugin lives at ~/.hermes/plugins/hermes-token-router/ (update-safe).
  • Status: OPERATIONAL. Router predicts toolsets with a separately configured lightweight model and an 8s fallback timeout; inspect the live router policy before naming its current provider/model.
  • a test profile is an isolated test harness, but its current model/provider must be read live. Do not restore a retired model from this historical example.
  • Conservative settings: confidence_threshold=0.0, long_message_decline_chars=2000, floor_toolsets=[terminal,file,web], short_message_bypass_chars=0.
  • Supported router providers: openrouter, deepseek, openai-codex. Provider choice is configuration-dependent; validate API compatibility and latency rather than preserving a historical default.
  • Key pitfalls: curly braces in ROUTER_SYSTEM_PROMPT must be escaped ({{ }}), print() inside docstrings doesn't execute, external routing variability requires timeout wrapper.
  • See references/tool-router-architecture.md for full architecture, config, pitfalls, and testing commands.

See references/prompt-composition-baseline.md for measured breakdowns; use those to decide whether enabling the router justifies added latency vs token savings for a given profile.

  • Do NOT use patch tool on ~/.hermes/config.yaml — the agent refuses with "security-sensitive configuration." Use Python to read/write YAML directly, or sed for simple single-line inserts. For multiline sed inserts on macOS, prefer Python — macOS sed -i '' chokes on \n in the replacement string.
  • After modifying config.yaml disabled list, verify with YAML parsing — check for duplicate disabled: keys (YAML silently uses the last one) and confirm the count matches expectations: grep -n "disabled:" ~/.hermes/config.yaml must return exactly one line.
  • Gateway restart required after config changes: launchctl stop ai.hermes.gateway && sleep 2 && launchctl start ai.hermes.gateway. Do NOT restart from within a Hermes session that depends on the gateway — restart in a separate terminal call.

Silent fallback to main model during compression (IMPORTANT)

Hermes will silently fall back to the main (often expensive/cloud) model for context compression when the configured summary model fails, times out, or returns invalid JSON. This is implemented as _fallback_to_main_for_compression in agent/context_compressor.py.

Behavior:

  • On first failure of the summary model, Hermes logs a warning and sets summary_model = "", meaning "use main model from now on."
  • This fallback persists for the rest of the session — all subsequent compressions use the main model until the session ends or is restarted.
  • You may see messages like "📦 Preflight compression: ~116,065 tokens >= 108,800 threshold" and assume it's using your cheap/local summary model — but if fallback fired, it is actually hitting your main model (often at much higher cost).

Common triggers:

  • LM Studio or local endpoint is slow under large-context loads (100k+ tokens) → timeout.
  • Transient network error between Hermes and summary endpoint.
  • Summary model returns malformed/empty JSON.

How to detect:

  • Check gateway logs:
  • grep -E "Fallback.*compression|Summary model" ~/.hermes/logs/gateway.log
  • Look for lines like:
  • Summary model '...' timed out/unavailable. Falling back to main model '...' for compression.

How to mitigate (without patching Hermes core):

  • Use a faster, smaller summary model that can handle large context without timing out (e.g., 9B-class tuned for instruction).
  • Ensure summary endpoint is on a reliable local machine and not competing with heavy workloads.
  • Consider setting compression.abort_on_summary_failure: true if you prefer Hermes to abort compression with a simple continuity note instead of burning expensive tokens on the main model.

If you need to prevent fallback entirely, that requires patching _fallback_to_main_for_compression in context_compressor.py. Prefer adjusting model choice or timeout behavior first.

Skill pruning workflow

When reducing initial prompt size by disabling unused skills:

  1. Extract actually-used skill names from session history via state.db. The raw query returns category-prefixed names (apple:imessage, productivity:some-domain-skill) — strip prefixes with sed to match directory names:
bash
sqlite3 ~/.hermes/state.db "
SELECT DISTINCT json_extract(args, '\$.name')
FROM (
SELECT json_extract(tool_calls, '\$[0].function.arguments') as args
FROM messages
WHERE tool_calls LIKE '%skill_view%' OR tool_calls LIKE '%skill_manage%'
)
WHERE args IS NOT NULL
AND json_extract(args, '\$.name') IS NOT NULL
AND json_extract(args, '\$.name') != ''
" | sed 's|^[a-z]*:||; s|.*/||' | sort -u > /tmp/used_skills.txt
  1. Get all installed skill names:
bash
find ~/.hermes/skills -name "SKILL.md" -exec dirname {} \; | xargs -I{} basename {} | sort > /tmp/all_skills.txt
  1. Diff to find unused skills:
bash
comm -23 /tmp/all_skills.txt /tmp/used_skills.txt > /tmp/unused_skills.txt
  1. Check cron jobs for skill references before disabling — a skill may be loaded by cron even if never called interactively. Search actual YAML config files, not log output:
bash
find ~/.hermes/cron -name "*.yaml" -exec grep -l "skills:" {} \; 2>/dev/null
for f in $(find ~/.hermes/cron -name "*.yaml" 2>/dev/null); do
if grep -q "skills:" "$f"; then
echo "=== $f ===" && grep -A5 "skills:" "$f"
fi
done
  1. Add skills.disabled list to ~/.hermes/config.yaml under the top-level skills: key (same level as external_dirs). The patch tool refuses config.yaml — use Python instead:
python
  import yaml
  with open('~/.hermes/config.yaml') as f:
      content = f.read()
  # Insert before '  config:' or append to existing disabled list
  content = content.replace(
      '    - last-skill\n  config:',
      '    - last-skill\n    - new-skill\n  config:'
  )
  with open('~/.hermes/config.yaml', 'w') as f:
      f.write(content)

Alternatively use sed for single-line inserts: sed -i '' '/ - last-skill/a\ - new-skill' ~/.hermes/config.yaml. Avoid multiline \n in macOS sed — it breaks. 6. Verify with Python YAML parsing — watch for duplicate disabled: keys (YAML silently uses the last one):

bash
grep -n "disabled:" ~/.hermes/config.yaml # must be exactly one
python3 -c "import yaml; c=yaml.safe_load(open('~/.hermes/config.yaml')); print(len(c['skills']['disabled']))"
  1. Restart gateway for changes to take effect:
bash
launchctl stop ai.hermes.gateway && sleep 2 && launchctl start ai.hermes.gateway

See references/skill-pruning-workflow.md for the full session transcript and rationale.

Jinja chat-template auditing

When reviewing a custom Jinja chat-template (e.g., for llama.cpp, vLLM, or Hermes gateway), check for these recurring issues:

  1. Hardcoded overrides block caller params — {%- set enable_thinking = false %} at top level always wins over any external enable_thinking passed in. Fix: wrap in {%- if enable_thinking is not defined %} guard.
  2. Image/video items emit no data — detecting the item type but only outputting \n\n without passing through item.image_url, item.url, or item.image. The model receives a placeholder with no reference to act on. Fix: add .get() fallbacks for common key names.
  3. Double-counting from multi-pass rendering — if render_content is called both in a reverse-scan pass and the main forward loop, ensure the scan pass uses do_vision_count=false so namespace counters stay correct.
  4. Tool arguments as JSON string — OpenAI-format tool calls often send arguments as a JSON string, not a dict. Calling |items on a string iterates characters. Fix: branch on {%- if args is string %} and json.loads(args) first.
  5. Reverse-scan heuristics are fragile — checking content.startswith('...') for tool-result boundaries breaks if user messages coincidentally match. Prefer explicit role-based checks where possible.
  6. Unknown roles crash silently — always include the actual role in the error: 'Unexpected message role: ' + message.role.
  7. Test with representative payloads — use Jinja2's DictLoader + env.globals['raise_exception'] to validate before deploying. Cover: basic conversation, multi-modal content, tool calls with string args, consecutive tool results, reasoning_content, and edge cases (None content, unknown roles).

See references/jinja-chat-template-audit.md for the full audit checklist and test harness pattern.

Recovery from an unrecoverable session context

When a CLI or messaging session reports that context length was exceeded and Hermes cannot compress further, treat the failure as session-scoped, not as evidence that the gateway, topic, project files, or external application are corrupted.

  1. Confirm the failure boundary from the live logs or status surface. A message such as Max compression attempts reached or cannot compress further means another /compress retry is unlikely to recover the in-memory conversation.
  2. Preserve the old session. Do not delete it, and do not restore it with /resume or /topic <session-id> merely to continue the task; those paths can reload the same oversized history.
  3. Start a fresh session in the same messaging topic with /new (or the equivalent new-session command for the client). In Telegram topics, /new resets only the current topic's session and leaves other topics untouched.
  4. Continue with a short, authoritative handoff: current checkout/writer ownership, last verified artifact or acceptance boundary, remaining gate, exclusions, and the evidence required before claiming success. Prefer a file-backed handoff for long work; never ferry the entire transcript into the new session.
  5. Keep the first fresh-session turn narrow and verification-first. Reconcile the checkout and incumbent writer before any write-capable resume, then work only the remaining gate.
  6. Use /context or /usage after several turns. If the new session is again approaching its compression threshold, close it at a clean handoff boundary and hand off again instead of repeatedly forcing compression.

Thin control planes for multi-profile work

A dedicated specialist Telegram bot is not the default fix for context overflow. Keep the assistant as the single user-facing control plane and route long Dev work into tracked profile processes or Kanban instead:

text
the user → assistant intake/status → domain controller → owning specialist worker

The coordinator topic should carry scope, approvals, compact status, and final evidence—not the worker's full implementation transcript. The domain controller owns architecture/integration and may route bounded child work to specialist profiles; workers return short, file-backed handoffs rather than screenshots, accessibility trees, raw logs, or copied conversation history. Create the durable root card before launch when work outlives the turn, spans profiles, has dependencies/review, or needs restart recovery. Use a separate specialist bot only for deliberate direct access or independent notifications; it adds another gateway/session surface and does not repair delegation by itself.

When a session overflows, do not bypass the control plane by restoring the old transcript in a specialist surface. Start a fresh session, preserve the old history, and resume from a concise handoff after reconciling the exact profile, checkout, and writer owner.

For the Telegram-specific recovery recipe, official command behavior, and the verified incident boundary, see references/context-overflow-recovery.md.

References

  • references/compression-runtime-fallback-notes.md — how the compressor chooses models, when it falls back to main, and how to debug "still using main model".
  • references/context-overflow-recovery.md — recover a session after Hermes exhausts context compression without deleting history or reloading the oversized transcript.
  • references/snapcompact-hermes-boot-analysis.md — session-derived notes on applying Snapcompact to Hermes startup payloads and realistic savings estimates.
  • references/jinja-chat-template-audit.md — 7-point audit checklist for custom Jinja chat-templates with test harness pattern.
  • references/profile-data-accuracy.md — why vague user profile labels ("20 years HK experience") cause downstream model hallucination, and the rule for fixing them.

Public support files

  • references/compression-runtime-fallback-notes.md
  • references/context-overflow-recovery.md
  • references/gateway-voice-latency-attribution.md
  • references/goal-long-prompt-preservation.md
  • references/isolated-worker-state-store.md
  • references/jinja-chat-template-audit.md
  • references/native-context-inspection.md
  • references/profile-data-accuracy.md
  • references/prompt-composition-baseline.md
  • references/session-store-optimization.md
  • references/skill-pruning-workflow.md
  • references/snapcompact-hermes-boot-analysis.md
  • references/tool-router-architecture.md

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the Hermes Context Optimization AI skill do?

hermes-context-optimization — Optimize Hermes startup/context payloads, compression, tool-schema loading, memory/skill injection, and visual-context experiments.

Why use Hermes Context Optimization on TypingMind?

Because you install it once and use it with any model. Hermes Context Optimization is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Hermes Context Optimization in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/AtlasOmnia/donna-starter/tree/main/skills/autonomous-ai-agents/hermes-context-optimization. TypingMind reads its SKILL.md and bundles its files and installs it as a skill you can enable per chat.

Which AI models can use Hermes Context Optimization?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Hermes Context Optimization?

As many as you like. As long as a model supports skills, you can use Hermes Context Optimization with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Hermes Context Optimization AI skill free?

Yes. It is published on GitHub by AtlasOmnia under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇