Files
homelabstack/docs/superpowers/plans/2026-06-27-hermes-ecosystem-integration.md
T
ginnoirandClaude Opus 4.8 dc2225d384 docs: fold ecosystem expansion + delegation fabric into Hermes plan
Add hermes-motif (skill discovery; complementary to curator, not a rival),
hermes-web-search-plus (mature multi-provider search, pairs with camofox),
optional llmtrim/rtk context efficiency, and the claude/codex/cursor/antigravity
delegation fabric. Record that Claude Code + Codex are now installed on valhalla.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 05:45:34 -05:00

42 KiB
Raw Blame History

Hermes Ecosystem Integration Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Integrate selected Hermes-ecosystem tools into ginnoir's live valhalla deployment — a host-side delegation skill and self-improvement plugin (Phase 1), a stealth-browser homelab stack and a skill pre-filter trial (Phase 2), and an optional UI upgrade (Phase 3) — each reversible and sized for the single-P100 / weak-local-model constraints.

Architecture: Two integration classes. Class A (host-side Hermes plugins/skills) install into ~/.hermes/ on the valhalla host and are applied by SSH + hermes CLI + sudo systemctl restart hermes-gateway.service; they are host-managed, NOT committed to this repo (tracked in project memory + the Obsidian vault, like the rustdesk/obsidian/xvfb units). Class B (Docker services) become stacks/<name>/ entries deployed via the normal Gitea-poll path, fronted by Caddy internal_only + Authentik. The companion spec is docs/superpowers/specs/2026-06-27-hermes-ecosystem-integration-design.md.

Tech Stack: Hermes Agent v0.17.0 (host systemd), llama-swap/Tesla P100 backend (gpt-oss-20b, --parallel 1), Python 3.11 (~/.hermes/hermes-agent/venv), uv, SQLite, Docker Compose + Portainer (Gitea-polled), Caddy, Authentik, Codex + Claude Code CLIs.


How to read this plan (operational, not codebase-TDD)

These are operational integrations against a live host and third-party services, so the TDD rhythm is adapted: each task is back up → change → verify with a smoke test → document/commit. The "test" is a real verification command with expected output. Class A (host) changes are not git commits — their checkpoint is a backup + smoke test + a memory/vault note. Class B (repo) changes do commit (and push triggers Portainer). Run every step from the Windows workstation; host steps use ssh -o BatchMode=yes ginnoir@valhalla "...".

Global guardrails (apply to every task):

  • hermes is only on the login-shell PATH → over SSH call it by full path: ~/.local/bin/hermes.
  • Gateway restart needs root: sudo systemctl restart hermes-gateway.service.
  • Always back up ~/.hermes/config.yaml before editing (cp ...bak.$(date +%s)).
  • Never load a second model onto the P100. Keep curator/eagle-eye semantic layers on CPU or off.
  • Read third-party code before running it (curator writes skills; acp-skill spawns external agents; camofox automates a browser).

Decisions (RESOLVED 2026-06-27 — all phases actionable): trial both UIs and keep the winner (Phase 3); curator stays report-only (Task 3); trial eagle-eye — it's the only direct skill-router in the ecosystem (Task 6); camofox wired as a minimal 2-tool skill (Task 5 Step 8).


PHASE 1 — Host-side, reversible, high-leverage (actionable now)

Task 1: Pre-flight — capture current Hermes state

Files:

  • Host only (no repo files).

  • Step 1: Verify host reachability and Hermes services are up

Run:

ssh -o BatchMode=yes ginnoir@valhalla "systemctl is-active hermes-gateway.service hermes-dashboard.service hermes-webui.service"

Expected: three lines, each active.

  • Step 2: Snapshot config + inventory skills/plugins/sessions dirs

Run:

ssh -o BatchMode=yes ginnoir@valhalla "cp ~/.hermes/config.yaml ~/.hermes/config.yaml.bak.preflight.$(date +%s) && ls -la ~/.hermes/skills ~/.hermes/plugins ~/.hermes/sessions 2>&1 | head -60 && ~/.local/bin/hermes --version"

Expected: a backup is created; directory listings print (note whether ~/.hermes/plugins exists yet); hermes prints a version (≈ v0.17.0). Record the skills-dir path — confirms ~/.hermes/skills is correct for later tasks.

  • Step 3: Confirm Codex and Claude Code are invocable — DONE 2026-06-27 (installed this session)

Both delegation CLIs were installed on valhalla this session:

  • claude~/.local/bin/claude v2.1.195 (login pending)
  • codex/usr/bin/codex v0.142.3 (login pending; harmless PATH-alias warning at install)

Gotcha recorded: /home/ginnoir/.claude existed as an empty root-owned dir (created Jun 17, likely a prior sudo op) and blocked the installer until sudo chown ginnoir:ginnoir ~/.claude. Codex global install needs sudo (npm global prefix is /usr). Re-verify any time with:

ssh -o BatchMode=yes ginnoir@valhalla "bash -lc 'claude --version; codex --version'"

ginnoir must log in to each (claude, then codex login) before Task 2's external delegation smoke-tests will succeed. Cursor + Antigravity targets are added later in Task 10.

  • Step 4: Checkpoint

No commit (host inventory only). Record findings (skills-dir path, whether plugins/ exists, Codex/Claude availability) in the session notes for use in Tasks 23.


Task 2: Install hermes-agent-acp-skill (multi-agent delegation)

Files:

  • Host: ~/.hermes/skills/hermes-acp-orchestrator/ (skill files), ~/.hermes/config.yaml (delegation block).

  • Scratch: clone under /storage1/hermes/workspace/clones/ (never root; see the disk gotcha).

  • Step 1: Clone and read the skill before installing

Run:

ssh -o BatchMode=yes ginnoir@valhalla "mkdir -p /storage1/hermes/workspace/clones && git -C /storage1/hermes/workspace/clones clone https://github.com/Rainhoole/hermes-agent-acp-skill && sed -n '1,200p' /storage1/hermes/workspace/clones/hermes-agent-acp-skill/SKILL.md"

Expected: repo clones; SKILL.md prints. Read it to confirm: the skill folder/name, how delegate_task() is wired, and whether it expects a specific install path or a config key. The README omits install steps, so the SKILL.md is authoritative — follow whatever placement it documents. If SKILL.md specifies a different mechanism than the manual copy below, use SKILL.md's.

  • Step 2: Place the skill into the Hermes skills directory

Run (adjust the destination name to match SKILL.md's declared skill name):

ssh -o BatchMode=yes ginnoir@valhalla "mkdir -p ~/.hermes/skills/hermes-acp-orchestrator && cp -r /storage1/hermes/workspace/clones/hermes-agent-acp-skill/SKILL.md /storage1/hermes/workspace/clones/hermes-agent-acp-skill/README.md ~/.hermes/skills/hermes-acp-orchestrator/ && ls -la ~/.hermes/skills/hermes-acp-orchestrator/"

Expected: SKILL.md and README.md present in the new skill dir.

  • Step 3: Add the delegation config block (with a safe backup)

Run:

ssh -o BatchMode=yes ginnoir@valhalla "cp ~/.hermes/config.yaml ~/.hermes/config.yaml.bak.acp.$(date +%s) && printf '\ndelegation:\n  external_timeout_seconds: 900\n  external_max_output_chars: 24000\n' >> ~/.hermes/config.yaml && tail -8 ~/.hermes/config.yaml"

Expected: a .bak.acp.* backup exists; the delegation: block is appended and printed. (If SKILL.md says the block belongs under a different key or nesting, edit accordingly instead of this append.)

  • Step 4: Restart the gateway and confirm the skill registers

Run:

ssh -o BatchMode=yes ginnoir@valhalla "sudo systemctl restart hermes-gateway.service && sleep 5 && systemctl is-active hermes-gateway.service && ~/.local/bin/hermes skills list 2>&1 | grep -i acp"

Expected: gateway active; the ACP/orchestrator skill appears in hermes skills list. (If the subcommand differs, use ~/.local/bin/hermes skills --help to find the list command — verify on host.)

  • Step 5: Smoke-test a trivial delegation to the local hermes subagent first

Run (a no-external-dependency delegation — routes to hermes, not Codex/Claude, to isolate the skill from CLI availability):

ssh -o BatchMode=yes ginnoir@valhalla "~/.local/bin/hermes run 'Use delegate_task to ask a hermes subagent to reply with exactly the word PONG, then report its output.' 2>&1 | tail -30"

Expected: the delegated subagent returns PONG and the parent reports it. This proves the skill mechanics. (Exact hermes one-shot invocation may differ — confirm the non-interactive run command via ~/.local/bin/hermes --help in Step 1's read-through.)

  • Step 6: Smoke-test an external delegation (only if Codex/Claude were found in Task 1)

Run:

ssh -o BatchMode=yes ginnoir@valhalla "~/.local/bin/hermes run 'Use delegate_task with agent=claude-code to print the current working directory and nothing else, then report it.' 2>&1 | tail -40"

Expected: Claude Code is spawned within the 900 s timeout, returns the cwd, output is captured under the 24,000-char cap. If it hangs or auths interactively, the external CLI needs non-interactive credentials on the service env — note for ginnoir; the hermes-target path (Step 5) still works.

  • Step 7: Checkpoint (host note + reversibility recorded)

No git commit. Record in session notes: skill installed at ~/.hermes/skills/hermes-acp-orchestrator/, config backup at ~/.hermes/config.yaml.bak.acp.*. Rollback = rm -rf ~/.hermes/skills/hermes-acp-orchestrator, restore the .bak.acp.*, restart gateway.


Task 3: Install hermes-curator-evolver (self-improvement, report-only)

Files:

  • Host: ~/.hermes/plugins/curator-evolver/ (plugin + data/evidence.sqlite), systemd user timer.

  • Step 1: Read the plugin source before installing (it can write to skills)

Run:

ssh -o BatchMode=yes ginnoir@valhalla "git -C /storage1/hermes/workspace/clones clone https://github.com/pingchesu/hermes-curator-evolver && sed -n '1,160p' /storage1/hermes/workspace/clones/hermes-curator-evolver/README.md"

Expected: repo clones; README prints. Confirm the apply path requires --approve (it does per the README) and that auto-run without --apply-low-risk --approve-auto-apply is dry-run only.

  • Step 2: Install the plugin (no semantic/embedding extras — keep it off the P100)

Run:

ssh -o BatchMode=yes ginnoir@valhalla "bash -lc '~/.local/bin/hermes plugins install pingchesu/hermes-curator-evolver --enable && uv pip install --python ~/.hermes/hermes-agent/venv/bin/python -e ~/.hermes/plugins/curator-evolver && ~/.hermes/hermes-agent/venv/bin/hermes-curator-evolver bootstrap'"

Expected: plugin installs to ~/.hermes/plugins/curator-evolver; editable pip install succeeds; bootstrap configures and installs a systemd user timer. Do NOT pass --semantic (that pulls Qwen/BGE models — CPU/VRAM cost we're avoiding for now; BM25/FTS ranking is the v1 default).

  • Step 3: Backfill recent sessions and generate the first dry-run report

Run:

ssh -o BatchMode=yes ginnoir@valhalla "~/.hermes/hermes-agent/venv/bin/hermes-curator-evolver backfill-sessions --sessions-dir ~/.hermes/sessions --days 30 --format json 2>&1 | tail -20 && ~/.hermes/hermes-agent/venv/bin/hermes-curator-evolver report --days 7 --format json 2>&1 | tail -40"

Expected: evidence is mined into ~/.hermes/plugins/curator-evolver/data/evidence.sqlite; report prints a JSON summary of candidate skill improvements. No skill files are modified (report is read-only).

  • Step 4: Generate a dry-run proposal for one skill and inspect it

Run:

ssh -o BatchMode=yes ginnoir@valhalla "~/.hermes/hermes-agent/venv/bin/hermes-curator-evolver auto-run --skills-dir ~/.hermes/skills --format json 2>&1 | tail -60"

Expected: a JSON set of proposed (not applied) edits. Confirm no files under ~/.hermes/skills changed:

ssh -o BatchMode=yes ginnoir@valhalla "find ~/.hermes/skills -newermt '-10 minutes' -type f 2>/dev/null"

Expected: empty output (nothing modified) — proves dry-run safety.

  • Step 5: Confirm the scheduled timer is report-only

Run:

ssh -o BatchMode=yes ginnoir@valhalla "bash -lc 'systemctl --user list-timers \"hermes-curator-evolver*\" --all --no-pager' && ssh -o BatchMode=yes ginnoir@valhalla \"systemctl --user cat 'hermes-curator-evolver*' 2>&1 | grep -iE 'ExecStart|approve|apply'\""

Expected: a timer is listed; its ExecStart runs auto-run without --apply-low-risk/--approve-auto-apply. If the bootstrap-installed unit includes those flags, override it to remove them (the morning decision in spec §6.2 defaults to report-only). If user-lingering isn't enabled the timer won't fire across logout — enable with sudo loginctl enable-linger ginnoir (note for ginnoir).

  • Step 6: Checkpoint (host note + reversibility recorded)

No git commit. Record: plugin at ~/.hermes/plugins/curator-evolver, DB at .../data/evidence.sqlite, timer name, report-only confirmed. Rollback = systemctl --user disable --now <timer>, ~/.local/bin/hermes plugins uninstall curator-evolver (verify exact uninstall verb), rm -rf ~/.hermes/plugins/curator-evolver.


Task 3b: Install hermes-motif (skill DISCOVERY, proposal-only)

Complements curator-evolver — does not compete with it (spec §7.1). motif discovers new skills by mining repeated tool sequences; curator refines existing ones. Zero P100 cost (makes no LLM calls). Together with eagle-eye (routing) they form a skill factory: motif creates → curator refines → eagle-eye routes.

Files:

  • Host: ~/.hermes/plugins/ (motif plugin), ~/.hermes/plugins/<motif>/plugin/plugin.yaml.

  • Step 1: Clone and read; confirm proposal-only config

Run:

ssh -o BatchMode=yes ginnoir@valhalla "git -C /storage1/hermes/workspace/clones clone https://github.com/Saurav0989/hermes-motif && sed -n '1,160p' /storage1/hermes/workspace/clones/hermes-motif/README.md && cat /storage1/hermes/workspace/clones/hermes-motif/plugin/plugin.yaml 2>&1"

Expected: README + plugin.yaml print. Confirm auto_install: false (proposal-only) and note min_occurrences / sequence-length thresholds. Note the referenced Hermes trace bug (#12922) that can affect trace completeness — acceptable for a proposal-only trial.

  • Step 2: Install the plugin

Run (per its README — clone + pip + scripts/install_plugin.sh):

ssh -o BatchMode=yes ginnoir@valhalla "cd /storage1/hermes/workspace/clones/hermes-motif && bash scripts/install_plugin.sh 2>&1 | tail -20"

Expected: the plugin lands under ~/.hermes/plugins/ and registers. (If the script expects a different layout, follow the README's exact steps.)

  • Step 3: Verify it mines and PROPOSES without modifying skills

Restart the gateway, run the agent through a couple of repeated multi-tool workflows, then check for proposals (drafts), confirming nothing under ~/.hermes/skills was auto-written:

ssh -o BatchMode=yes ginnoir@valhalla "sudo systemctl restart hermes-gateway.service && sleep 5 && find ~/.hermes/plugins -iname '*propos*' -o -iname '*draft*' 2>/dev/null | head && find ~/.hermes/skills -newermt '-10 minutes' -type f 2>/dev/null"

Expected: proposal/draft artifacts may appear under the plugin dir; the second find is empty (no skill files auto-modified) — proves auto_install: false safety.

  • Step 4: Checkpoint

No repo commit (host-side). Rollback = remove the motif plugin dir + restart gateway. Record in memory/hermes-extensions.md alongside curator (skill factory: motif=create, curator=refine).


Task 4: Document Phase 1 in memory + vault (durable knowledge)

Files:

  • Memory: C:\Users\MattC\.claude\projects\C--Users-MattC-Documents-homelabstack\memory\hermes-extensions.md + MEMORY.md pointer.

  • Vault: append to the Hermes project note via Obsidian MCP (mcp__obsidian__*).

  • Step 1: Write the memory file

Create memory/hermes-extensions.md (frontmatter type: project) recording: acp-skill installed (delegation to hermes/codex/claude-code/cursor/antigravity, 900s/24k caps); curator-evolver installed report-only (CPU ranking, no --semantic, no auto-apply flags); motif installed proposal-only (skill factory: motif creates → curator refines → eagle-eye routes); claude v2.1.195 + codex v0.142.3 installed on valhalla 2026-06-27 (login pending; ~/.claude was root-owned → chowned); exact paths and rollback commands; the host-vs-repo boundary. Link [[llm-stack-hermes]], [[multi-agent-tool-configs]], [[obsidian-app-on-valhalla]].

  • Step 2: Add the MEMORY.md index pointer

Append one line to MEMORY.md: - [Hermes host extensions](hermes-extensions.md) — acp delegation skill + curator-evolver (report-only) on valhalla; host-managed in ~/.hermes, not in the repo

  • Step 3: Write back to the Obsidian vault

Per the global rule, use the Obsidian MCP (never write CouchDB directly) to append a session note to the Hermes project folder summarizing Phase 1 (what, why report-only, rollback). If the MCP is unreachable, tell ginnoir and skip — do not hand-edit.

  • Step 4: Checkpoint

No code commit required (memory files live outside the repo). Phase 1 complete and documented.


PHASE 2 — New capability + experiment

Decisions resolved (spec §6.3 eagle-eye trial; §6.4 camofox minimal). Actionable.

Task 5: camofox-browser as a homelab stack (Class B)

Files:

  • Create: stacks/camofox/docker-compose.yml, stacks/camofox/stack.env.

  • Modify: Caddyfile (new site block), bookmarks-domains.html + bookmarks-ports.html (regenerated).

  • Host (image): build under /storage1/hermes/workspace/clones/camofox-browser.

  • Step 1: Decide image provenance and build it

camofox publishes no registry image (make up builds locally). Recommended default: build on valhalla and tag camofox-browser:local, reference that tag from compose (Watchtower already disabled for pinned infra). Run:

ssh -o BatchMode=yes ginnoir@valhalla "git -C /storage1/hermes/workspace/clones clone https://github.com/jo-inc/camofox-browser && cd /storage1/hermes/workspace/clones/camofox-browser && docker build -t camofox-browser:local . 2>&1 | tail -20 && docker image ls camofox-browser:local"

Expected: image builds; camofox-browser:local is listed. Alternative (if a reproducible/Gitea-Actions build is preferred, like famapp): build + push to registry.ginnoir.com/ginnoir/camofox-browser and reference that instead — flag this choice for ginnoir.

  • Step 2: Write the stack compose

Create stacks/camofox/docker-compose.yml:

# camofox stack — stealth headless browser REST API for the Hermes agent.
# No published image: built on-host as camofox-browser:local (see plan Task 5).
# Internal-only; reachable by Caddy over edge and by host-side Hermes.
services:
  camofox:
    image: camofox-browser:local
    container_name: camofox
    restart: unless-stopped
    labels:
      - "com.centurylabs.watchtower.enable=false"
    env_file:
      - stack.env
    networks: [edge, camofox]
    volumes:
      - /config/camofox/cookies:/home/node/.camofox/cookies
      - /config/camofox/profiles:/home/node/.camofox/profiles
    ports:
      - "172.20.0.1:9377:9377"
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://localhost:9377/health"]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 40s

networks:
  edge:
    external: true
  camofox:
    name: camofox
    driver: bridge

(The 172.20.0.1:9377 host-port mirrors the llm stack's pattern so host-side Hermes can reach it directly; Caddy reaches it over edge by container name.)

  • Step 3: Write stack.env (secrets; LF endings)

Create stacks/camofox/stack.env with a generated bearer key (replace the value with a real secret before push):

CAMOFOX_ACCESS_KEY=GENERATE_A_LONG_RANDOM_KEY
CAMOFOX_ADMIN_KEY=GENERATE_A_SECOND_RANDOM_KEY
CAMOFOX_PORT=9377

Generate the keys: ssh ... "openssl rand -hex 32" (run twice). Ensure LF line endings (.gitattributes enforces this — verify the file isn't CRLF before committing). Leave CAMOFOX_API_KEY unset (cookie-import endpoint stays disabled).

  • Step 4: Add the Caddy site block (internal-only)

Add to Caddyfile (place near other internal admin services). Since camofox enforces its own bearer auth and Hermes calls it machine-to-machine, gate by LAN only (no Authentik forward-auth, which would block the agent's API calls):

camofox.ginnoir.com {
	import internal_only
	reverse_proxy camofox:9377
}
  • Step 5: Create host config dirs, regenerate bookmarks, then deploy

Run:

ssh -o BatchMode=yes ginnoir@valhalla "sudo mkdir -p /config/camofox/cookies /config/camofox/profiles && sudo chown -R ginnoir:ginnoir /config/camofox"

Then regenerate bookmarks and push (Portainer must have the stacks/camofox git stack registered — see Step 6):

./scripts/gen-bookmarks.ps1
git add stacks/camofox/ Caddyfile bookmarks-domains.html bookmarks-ports.html
git commit -m "feat(camofox): stealth browser stack for the Hermes agent"
git push

Expected: commit + push; Gitea Actions reloads Caddy (Caddyfile changed); Portainer redeploys the camofox stack within 5 min.

  • Step 6: Register the stack in Portainer if new, and verify it runs

New stacks need one-time Portainer registration (see portainer-new-stack-registration memory). After deploy, verify:

ssh -o BatchMode=yes ginnoir@valhalla "docker ps --filter name=camofox --format '{{.Names}} {{.Status}}' && curl -fsS http://172.20.0.1:9377/health"

Expected: container Up (healthy); /health returns OK.

  • Step 7: Smoke-test the browser API end-to-end

Run (creates a tab, snapshots a page):

ssh -o BatchMode=yes ginnoir@valhalla "K=\$(grep CAMOFOX_ACCESS_KEY /config/portainer/compose/*/stacks/camofox/stack.env | cut -d= -f2); ID=\$(curl -fsS -H \"Authorization: Bearer \$K\" -H 'Content-Type: application/json' -d '{\"userId\":\"smoke\",\"sessionKey\":\"t1\",\"url\":\"https://example.com\"}' http://172.20.0.1:9377/tabs | python3 -c 'import sys,json;print(json.load(sys.stdin)[\"id\"])'); curl -fsS -H \"Authorization: Bearer \$K\" \"http://172.20.0.1:9377/tabs/\$ID/snapshot?userId=smoke\" | head -20"

Expected: a tab id comes back; the snapshot returns accessibility text containing "Example Domain". (Adjust the JSON id field name to match the real response from Step 1's README read.)

  • Step 8: Wire camofox into Hermes as a minimal tool surface (per §6.4 decision)

Default recommendation: a small Hermes skill (2 high-level tools — browse(url) and search(query)) that curls camofox, rather than exposing the full REST surface (respects the tool-budget that keeps gpt-oss-20b functional). Create ~/.hermes/skills/camofox-browse/SKILL.md documenting the two operations against http://172.20.0.1:9377 with the bearer key, restart the gateway, and smoke-test:

ssh -o BatchMode=yes ginnoir@valhalla "sudo systemctl restart hermes-gateway.service && sleep 5 && ~/.local/bin/hermes run 'browse https://example.com and tell me the page heading' 2>&1 | tail -20"

Expected: Hermes uses the camofox tool and reports "Example Domain". If §6.4 chose an MCP shim instead, build/register the MCP server and add it to mcp_servers: with a 2-tool tools.include allowlist (per the MCP-curation pattern in llm-stack-hermes).

  • Step 9: Checkpoint

Repo changes are committed (Step 5). Update memory/hermes-extensions.md + the vault note with the camofox stack + tool wiring and the bearer-key location.


Task 5b: Install hermes-web-search-plus (multi-provider search; pairs with camofox)

Mature (v2.6.1, MIT, stdlib-only). Complements camofox (spec §7.2): search-plus finds via cheap provider APIs, camofox browses/renders. Lighter and higher-frequency — good default reach-for.

Files:

  • Host: ~/.hermes/plugins/ (plugin), provider key(s) in ~/.hermes/config.yaml (or the plugin's config).

  • Step 1: Install the plugin

Run:

ssh -o BatchMode=yes ginnoir@valhalla "bash -lc '~/.local/bin/hermes plugins install robbyczgw-cla/hermes-web-search-plus --enable'"

Expected: plugin installs and enables.

  • Step 2: Configure at least one provider key (free tier)

All provider keys are optional but ≥1 is needed to function. Pick a free-tier provider (e.g. Tavily, Exa, or self-hosted SearXNG; Keenable has a keyless public tier). Add the key per the plugin's README (read /storage1/hermes/workspace/clones/... or the plugin docs for the exact env/config key), then:

ssh -o BatchMode=yes ginnoir@valhalla "sudo systemctl restart hermes-gateway.service && sleep 5 && systemctl is-active hermes-gateway.service"

Expected: gateway active. Mind the tool-budget rule — if it exposes both web_search_plus + extract, that's fine (2 tools); don't also enable redundant search MCPs.

  • Step 3: Smoke-test a search

Run:

ssh -o BatchMode=yes ginnoir@valhalla "~/.local/bin/hermes run 'search the web for the latest Hermes Agent release version and cite the source' 2>&1 | tail -20"

Expected: the agent calls the search tool, returns a current result with a source URL.

  • Step 4: Checkpoint

No repo commit (host-side). Document the chosen provider + key location in memory/hermes-extensions.md.


Task 6: Trial eagle-eye skill pre-filter (Class A, behind a switch)

Confirmed the chosen tool: eagle-eye is the only direct skill-router in the Hermes ecosystem (per awesome-hermes-agent / Hermes Atlas). It directly serves the goal of "many skills installed, few injected per turn." Complementary (not a substitute) and worth a later look on the tool side: llmtrim (compresses tool schemas + MCP output before each request). hermes-motif overlaps curator-evolver (trace→micro-skill), not this router.

Files:

  • Host: ~/.hermes/plugins/eagle-eye/ (or skills dir per its README), config toggle in ~/.hermes/config.yaml.

  • Step 1: Clone and read; confirm graceful-degradation and the jieba dependency

Run:

ssh -o BatchMode=yes ginnoir@valhalla "git -C /storage1/hermes/workspace/clones clone https://github.com/willingning-coder/eagle-eye && sed -n '1,200p' /storage1/hermes/workspace/clones/eagle-eye/README.md"

Expected: README prints. Confirm the install hook, the on/off switch, and that L2L5 (incl. dense embeddings) are optional. Plan to run with the dense layer disabled (CPU/keep off the P100) — lean on L1 (hard triggers) + L2 (BM25) only for the trial.

  • Step 2: Install with an easy off-switch and minimal deps

Install per the README (likely ~/.local/bin/hermes plugins install willingning-coder/eagle-eye --enable), then restart the gateway:

ssh -o BatchMode=yes ginnoir@valhalla "bash -lc '~/.local/bin/hermes plugins install willingning-coder/eagle-eye --enable' && ssh -o BatchMode=yes ginnoir@valhalla 'sudo systemctl restart hermes-gateway.service && systemctl is-active hermes-gateway.service'"

Expected: plugin enabled; gateway active. (If install fails on jieba, uv pip install --python ~/.hermes/hermes-agent/venv/bin/python jieba then retry — note the foreign-language dep for maintenance.)

  • Step 3: A/B test skill selection on representative prompts

Pick 5 prompts that should each map to a known skill and 2 that should map to none. Run each with eagle-eye enabled, then disable it (~/.local/bin/hermes plugins disable eagle-eye + gateway restart) and run the same 7. Record which skills each surfaced and whether the local model then picked the right one.

ssh -o BatchMode=yes ginnoir@valhalla "~/.local/bin/hermes run '<representative prompt>' 2>&1 | tail -25"

Expected: with eagle-eye on, the prompt's prompt-injected skill candidates are ≤5 and include the right one; the "no skill needed" prompts proceed without forced skill loading.

  • Step 4: Keep-or-cut decision

Keep only if skill selection measurably improved (right skill surfaced more often AND/OR fewer wrong skills loaded) without regressions. Otherwise disable and uninstall:

ssh -o BatchMode=yes ginnoir@valhalla "~/.local/bin/hermes plugins uninstall eagle-eye && sudo systemctl restart hermes-gateway.service"

Record the verdict + evidence in memory/hermes-extensions.md.

  • Step 5: Checkpoint

No repo commit (host-side). Document the A/B result and final state (kept/cut) in memory + vault.


PHASE 2b — Delegation fabric & context efficiency

Extends acp-skill (Task 2) from 3 targets to 4 external agents, and adds optional token-trimming.

Task 10: Wire Cursor + Antigravity into the delegation fabric

Files:

  • Host: Cursor + agy binaries; acp-skill config or a generic shell-agent skill in ~/.hermes/skills/.

  • Step 1: Install the Cursor CLI (official cursor.com)

Run:

ssh -o BatchMode=yes ginnoir@valhalla "bash -lc 'curl https://cursor.com/install -fsS | bash' && ssh -o BatchMode=yes ginnoir@valhalla 'bash -lc \"command -v cursor-agent && cursor-agent --version\"'"

Expected: cursor-agent installs and prints a version. ginnoir logs in later.

  • Step 2: Install the Antigravity CLI (agy) from the OFFICIAL Google source

Do not use blog-derived URLs. Get the exact installer from the official pages first: https://antigravity.google/download and https://antigravity.google/docs/gcli-migration. Then run the official one-line installer they document, e.g.:

ssh -o BatchMode=yes ginnoir@valhalla "bash -lc '<official agy installer from antigravity.google/docs>' && ssh -o BatchMode=yes ginnoir@valhalla 'bash -lc \"command -v agy && agy --version\"'"

Expected: agy (Go binary, ideal for headless SSH) installs and prints a version. Auth later via keyring/Google sign-in or ANTIGRAVITY_TOKEN.

  • Step 3: Confirm each agent answers in headless mode (after ginnoir logs in)

Run:

ssh -o BatchMode=yes ginnoir@valhalla "bash -lc 'claude -p \"say PONG\"; codex exec \"say PONG\"; cursor-agent -p --output-format json --trust \"say PONG\"; agy -p \"say PONG\"'"

Expected: each prints PONG-ish output. Cursor caveat: -p has a known hang bug — always pass --output-format json and wrap with a timeout (timeout 120 cursor-agent ...).

  • Step 4: Extend acp-skill routing (or add a generic shell-agent skill)

Read ~/.hermes/skills/hermes-acp-orchestrator/SKILL.md to see if agent= routing is extensible.

  • If extensible: add cursor and antigravity targets mapping to the Step 3 invocations (with the cursor timeout + json flags), honoring the delegation: external_timeout_seconds: 900 / external_max_output_chars: 24000 caps.
  • If not: add ~/.hermes/skills/shell-agent/SKILL.md exposing one tool delegate(agent, goal) that shells out to claude/codex/cursor/agy with the caps + cursor guard. One tool keeps the surface within the tool-budget. Then restart the gateway:
ssh -o BatchMode=yes ginnoir@valhalla "sudo systemctl restart hermes-gateway.service && sleep 5 && systemctl is-active hermes-gateway.service"
  • Step 5: End-to-end smoke-test each delegation target

Run one delegated task per agent (e.g. agent=cursor, agent=antigravity) and confirm output is captured under the cap. Record any that hang/auth-fail for follow-up.

  • Step 6: Checkpoint

No repo commit (host-side). Document the four-target fabric + cursor caveat in memory/hermes-extensions.md.


Task 11: (OPTIONAL) Context efficiency — llmtrim on the cloud-delegation path

Opt-in. Start where the win is unambiguous and local-risk-free: trimming the cloud delegation agents' traffic (Claude Code/Codex/Cursor → Anthropic/OpenAI). Defer the llama-swap-fronting idea until validated. rtk-hermes (shell-output trimming) is a separate lighter opt-in.

Files:

  • Host: llmtrim service + HTTPS_PROXY env for the delegation agents.

  • Step 1: Install llmtrim and run setup

Run:

ssh -o BatchMode=yes ginnoir@valhalla "bash -lc 'npm install -g @llmtrim/cli@latest && llmtrim setup' 2>&1 | tail -20"

Expected: installs; setup installs the name-constrained CA + background proxy. Review the MITM-CA trust implication first — it's name-constrained to LLM API domains, but it's still a CA on the host.

  • Step 2: Point the cloud delegation agents through it; measure

Ensure the delegation agents inherit HTTPS_PROXY (llmtrim sets this). Run a representative delegated task via Claude Code/Codex and compare token counts / cost before vs after (llmtrim reports savings). Keep only if the reduction is real with no quality loss.

  • Step 3: (Later, separate) Evaluate llama-swap fronting + rtk-hermes

Document — do not implement here — the two deferred ideas: (a) llmtrim in front of 172.20.0.1:8090 via LLMTRIM_EXTRA_HOSTS to shrink prompts and speed Pascal prefill (needs validation; unproven for local OpenAI-compatible backends); (b) rtk-hermes (pre_tool_call shell rewrite) if the rtk binary is available on Ubuntu.

  • Step 4: Checkpoint

No repo commit. Record the decision + measured savings (or rejection) in memory/hermes-extensions.md. llmtrim uninstall fully reverses the proxy + CA if rejected.


PHASE 3 — UI trial: stand up BOTH, keep the winner

Decision resolved (spec §6.1): trial hermes-ui (Task 7) and hermes-workspace (Task 8) in parallel, compare head-to-head against the bundled webui (Task 9), keep one and tear down the rest. mission-control is skipped.

Task 7: Trial hermes-ui (lightweight, no build)

Files:

  • Host: clone at /storage1/hermes/workspace/clones/hermes-ui; optional hermes-ui.service (host unit) or a tiny container; Caddy block if exposed.

  • Step 1: Clone and run the stdlib proxy against the live gateway

Run:

ssh -o BatchMode=yes ginnoir@valhalla "git -C /storage1/hermes/workspace/clones clone https://github.com/pyrate-llama/hermes-ui && cd /storage1/hermes/workspace/clones/hermes-ui && (~/.hermes/hermes-agent/venv/bin/python3 serve_lite.py >/tmp/hermes-ui.log 2>&1 &) && sleep 3 && curl -fsS http://127.0.0.1:3333/hermes-ui.html | head -5"

Expected: the proxy starts on :3333 (defaults to gateway 127.0.0.1:8642, which matches your deployment), and the HTML serves. If your gateway port differs, edit the HERMES variable at the top of serve_lite.py (no env var exists).

  • Step 2: Expose it on the LAN for evaluation (don't finalize yet)

Bind the proxy to the host IP so Caddy can reach it, add a temporary internal-only Caddy block, and keep it running for the Task 9 comparison:

hermes-ui.ginnoir.com {
	import internal_only
	reverse_proxy 172.20.0.1:3333
}

Run serve_lite.py bound appropriately (edit its bind host if it defaults to 127.0.0.1), regenerate bookmarks, push the Caddyfile change. Do not create the persistent hermes-ui.service yet — that happens in Task 9 only for the winner.

  • Step 3: Checkpoint

hermes-ui is reachable at https://hermes-ui.ginnoir.com (LAN) for the head-to-head. Leave the final keep/revert + boot-persistence to Task 9.


Task 8: Deploy hermes-workspace as a stack (Class B) — for evaluation

Files:

  • Create: stacks/hermes-workspace/docker-compose.yml, stacks/hermes-workspace/stack.env.

  • Modify: Caddyfile (Authentik-gated site block), bookmarks-domains.html + bookmarks-ports.html.

  • Host (image): build under /storage1/hermes/workspace/clones/hermes-workspace.

  • Step 1: Clone and read its deployment docs (get exact build, ports, env)

Run:

ssh -o BatchMode=yes ginnoir@valhalla "git -C /storage1/hermes/workspace/clones clone https://github.com/outsourc-e/hermes-workspace && sed -n '1,200p' /storage1/hermes/workspace/clones/hermes-workspace/README.md && ls /storage1/hermes/workspace/clones/hermes-workspace/{Dockerfile,docker-compose*.yml,.env*} 2>&1"

Expected: README + a Dockerfile/compose appear. Record the exact image build command, the served port, and the env var(s) that point the frontend at the gateway (:8642) and dashboard (:9119). Note the swarm caveat for Task 9: Swarm Mode (tmux worker pools) can't parallelize inference on one P100 — evaluate the workspace/observability features, not swarm.

  • Step 2: Resolve container→host-service reachability

hermes-workspace (a container) must reach the host's gateway :8642 and dashboard :9119. Check what interface those bind to:

ssh -o BatchMode=yes ginnoir@valhalla "ss -ltnp | grep -E ':8642|:9119'"

Expected: shows the bind address. If bound to 127.0.0.1, the container can't reach them — pick one: (a) add extra_hosts: ["host.docker.internal:host-gateway"] and target host.docker.internal, or (b) rebind the Hermes services to the docker-bridge host IP 172.20.0.1 (config change + gateway restart, with backup). Default recommendation: (a) (no Hermes config change; reversible).

  • Step 3: Build the image on-host

Run (use the build command discovered in Step 1; tag locally since there's no published image):

ssh -o BatchMode=yes ginnoir@valhalla "cd /storage1/hermes/workspace/clones/hermes-workspace && docker build -t hermes-workspace:local . 2>&1 | tail -20 && docker image ls hermes-workspace:local"

Expected: hermes-workspace:local is built and listed.

  • Step 4: Write the stack compose

Create stacks/hermes-workspace/docker-compose.yml (adjust the served port and gateway/dashboard env keys to Step 1's findings; this uses host.docker.internal per Step 2 option (a)):

# hermes-workspace stack — full web command center for the Hermes agent (trial).
# No published image: built on-host as hermes-workspace:local (see plan Task 8).
# Human-facing UI → Authentik-gated. Reaches host gateway :8642 + dashboard :9119
# via host.docker.internal.
services:
  hermes-workspace:
    image: hermes-workspace:local
    container_name: hermes-workspace
    restart: unless-stopped
    labels:
      - "com.centurylabs.watchtower.enable=false"
    env_file:
      - stack.env
    networks: [edge]
    extra_hosts:
      - "host.docker.internal:host-gateway"
    ports:
      - "172.20.0.1:8088:8088"

networks:
  edge:
    external: true
  • Step 5: Write stack.env (gateway/dashboard targets; LF endings)

Create stacks/hermes-workspace/stack.env using the real env keys from Step 1, e.g.:

HERMES_GATEWAY_URL=http://host.docker.internal:8642
HERMES_DASHBOARD_URL=http://host.docker.internal:9119
PORT=8088

Verify LF endings before committing.

  • Step 6: Add an Authentik-gated Caddy block

Unlike camofox (machine-to-machine), this is a human UI → gate with Authentik forward_auth (Pattern B):

workspace.ginnoir.com {
	import internal_only
	route {
		import authentik_outpost
		import authentik_forward_auth
		reverse_proxy hermes-workspace:8088
	}
}
  • Step 7: Deploy and verify

Run:

./scripts/gen-bookmarks.ps1
git add stacks/hermes-workspace/ Caddyfile bookmarks-domains.html bookmarks-ports.html
git commit -m "feat(hermes-workspace): trial command-center stack (eval vs hermes-ui)"
git push

Register the stack in Portainer if new, then:

ssh -o BatchMode=yes ginnoir@valhalla "docker ps --filter name=hermes-workspace --format '{{.Names}} {{.Status}}' && curl -fsS http://172.20.0.1:8088/ | head -5"

Expected: container Up; the workspace HTML serves; logging into https://workspace.ginnoir.com via Authentik shows live chat/memory/skills wired to your gateway.

  • Step 8: Checkpoint

Repo changes committed (Step 7). Leave the keep/tear-down decision to Task 9.


Task 9: Head-to-head UI decision — keep one, tear down the rest

Files:

  • Modify (on tear-down): Caddyfile, stacks/... (remove the loser), bookmarks; host unit for the winner.

  • Step 1: Compare bundled webui vs hermes-ui vs hermes-workspace

Use all three live for representative work (chat/streaming, tasks/kanban, files, terminal, skills, MCP browser, cron, memory, health). Score against: does it surface your curated tools cleanly, does it stay responsive against the P100's latency, and does it add real value over the bundled webui. Record the verdict in the vault.

  • Step 2: Make the winner permanent

  • If hermes-ui wins: create host unit hermes-ui.service (host-managed, like obsidian.service — NOT in this repo), After=hermes-gateway.service, Restart=on-failure; keep its Caddy block.

  • If hermes-workspace wins: keep its stack + Authentik block as-is.

  • If bundled webui wins: keep status quo.

  • Step 3: Tear down the losers (reversible, clean)

  • Remove the hermes-workspace stack if it lost: delete stacks/hermes-workspace/, its Caddy block, regenerate bookmarks, commit + push, then delete the stack in Portainer and docker rm -f hermes-workspace, docker image rm hermes-workspace:local.

  • Stop/remove hermes-ui if it lost: pkill -f 'serve_lite[.]py' (bracket trick), remove its Caddy block + clone, commit the Caddyfile change.

  • Step 4: Checkpoint

One UI kept and documented in memory + vault; losers fully removed; repo reflects the final state.


Self-Review (completed)

  • Spec coverage: Original 7 repos — acp-skill (T2), curator-evolver (T3), camofox (T5), eagle-eye (T6), hermes-ui (T7), hermes-workspace (T8 deploy) + keep-one decision (T9); mission-control (skipped per spec §2.5/§5, intentional). Ecosystem expansion (spec §7) — hermes-motif (T3b), hermes-web-search-plus (T5b), delegation fabric for cursor+antigravity (T10), optional llmtrim/rtk context efficiency (T11). Claude Code + Codex install is done (T1 Step 3). Phase ordering, single-P100 discipline, host-vs-repo boundary, provenance (official installers only — Antigravity URL verified to antigravity.google), reversibility, and the §6 decisions are all reflected.
  • Placeholders: None of the prohibited kinds. Where a third-party command form can't be verified remotely (e.g. exact hermes subcommand spelling, acp-skill install mechanism, response field names), the plan's first step is a concrete "clone + read the README/SKILL.md" command that resolves it before use — a real action with expected output, not a TBD.
  • Consistency: Paths and names are consistent throughout (~/.hermes/skills, ~/.hermes/plugins/curator-evolver, camofox-browser:local, port 9377, gateway 8642, 172.20.0.1 host-IP publish pattern, sudo systemctl restart hermes-gateway.service).
  • Decision gates: Phases 23 are clearly gated on spec §6 and must not start before ginnoir answers.