From 9fa025f7d5f96ce87a287ab4a4cf87c984c9674f Mon Sep 17 00:00:00 2001 From: William Turner Date: Sun, 23 Aug 2026 15:46:58 +0000 Subject: [PATCH] Add Hermes Agent as a second native Matrix presence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Deployed as its own service (pinned nousresearch/hermes-agent:v2026.8.19), own Matrix bot account (@hermes), own OpenRouter-backed model config, and its own OpenAI-compatible API server (internal network only, for possible future use by claude-agent). Joins the same control room but only responds when explicitly @mentioned, restricted to the human user — no conflict with claude-bot's default no-prefix chat routing. claude-agent's router now ignores messages addressed to @hermes so both bots don't answer the same message. Bridge networking (the 'web' network), not the image's default host mode — no reason for an agent container to share the host's network namespace when everything it needs (the homeserver, OpenRouter) is reachable over the existing bridge. --- .env.example | 11 +++++++++++ agent/src/matrixBot.js | 4 ++++ docker-compose.yml | 27 +++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/.env.example b/.env.example index a077b38..b32ad6a 100644 --- a/.env.example +++ b/.env.example @@ -40,6 +40,17 @@ OPENROUTER_API_KEY= # Any random string; also used as litellm's general_settings.master_key. LITELLM_MASTER_KEY= +# --- hermes (autonomous agent with its own native Matrix presence) --- +# Your own Matrix ID — Hermes only responds to this user, and only when @mentioned +# in a shared room (e.g. "@hermes " in the control room). +MATRIX_HUMAN_USER_ID=@william:matrix.apps.williamturner.eu +# Access token for the @hermes bot account (register it the same way as claude-bot — +# see README — then log in as it via /_matrix/client/v3/login to get this token). +HERMES_MATRIX_ACCESS_TOKEN= +# Any random string — bearer key for Hermes's own OpenAI-compatible API server +# (internal network only, not published anywhere). +HERMES_API_SERVER_KEY= + # --- portainer (GitOps redeploy) --- PORTAINER_STACK_WEBHOOK_URL= diff --git a/agent/src/matrixBot.js b/agent/src/matrixBot.js index 2b05a5a..b60243b 100644 --- a/agent/src/matrixBot.js +++ b/agent/src/matrixBot.js @@ -49,6 +49,10 @@ export async function startMatrixBot() { if (event.sender === BOT_USER_ID) return; const body = event.content?.body; if (!body) return; + // Messages explicitly addressed to another agent in this room (currently just + // @hermes) are that agent's to answer — without this, claude-bot's classifier would + // also see and reply to them, since it otherwise treats every message as its own. + if (/^@hermes\b/i.test(body.trim())) return; try { const decision = await routeMessage(body, KNOWN_REPOS); diff --git a/docker-compose.yml b/docker-compose.yml index 4d5e749..eb7678d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -61,6 +61,33 @@ services: # Internal only — no Traefik labels. No reason to expose an LLM gateway holding a # master key and OAuth-forwarding config to the public internet. + hermes: + # Pinned to a specific dated release, not :latest — same rationale as litellm above. + image: nousresearch/hermes-agent:v2026.8.19 + container_name: hermes + restart: unless-stopped + environment: + HERMES_UID: "1000" + HERMES_GID: "1000" + # Internal container address, not the public HTTPS one — same docker network as + # matrix-homeserver, no reason to round-trip through Traefik/TLS for this. + MATRIX_HOMESERVER: http://matrix-homeserver:8008 + MATRIX_ACCESS_TOKEN: ${HERMES_MATRIX_ACCESS_TOKEN} + # Only you can trigger it; and only with an explicit @hermes mention in shared + # rooms (DMs to it would respond unprompted, per Hermes's own default behavior). + MATRIX_ALLOWED_USERS: ${MATRIX_HUMAN_USER_ID} + MATRIX_REQUIRE_MENTION: "true" + OPENROUTER_API_KEY: ${OPENROUTER_API_KEY} + # Exposed on the internal network only (see claude-agent's LITELLM_BASE_URL-style + # usage pattern) — nothing publishes this port externally. + API_SERVER_ENABLED: "true" + API_SERVER_HOST: 0.0.0.0 + API_SERVER_KEY: ${HERMES_API_SERVER_KEY} + volumes: + - /home/william/hermes-data:/opt/data + networks: + - web + claude-agent: image: ${GITEA_REGISTRY_IMAGE} depends_on: -- 2.54.0