Critical bug: claude-bot only ignored its OWN messages and text explicitly
addressed to @hermes — it did not ignore Hermes's own replies appearing in
the room. A single @hermes mention cascaded into claude-bot replying to
Hermes's thread messages ('Hermes says: ...'), which could itself cascade
further. Fixed with an OTHER_AGENT_USER_IDS allowlist of sender IDs to
always ignore, not just a text-prefix check.
Also point Hermes's model provider at the local litellm gateway
(OPENAI_BASE_URL/OPENAI_API_KEY) instead of OpenRouter directly, matching
claude-agent's own chat path — one place to hold the OpenRouter credential.
This does NOT grant Hermes access to the Claude subscription; that's an
Anthropic-side restriction unrelated to which proxy sits in front of it,
already proven earlier in this session.
Separately (not a code fix): the 'Billing or credits exhausted: HTTP 402'
error Hermes hit is real — the OpenRouter account currently has 0 credits.
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.
- New litellm service (pinned v1.98.0 — litellm 1.82.7/1.82.8 on PyPI were
compromised with credential-stealing malware in March 2026; internal-only,
no Traefik route, no reason to expose an LLM gateway with a master key
publicly).
- Replaces !claude/!ai command prefixes with automatic routing: every plain
message in the control room goes through a classifier (router.js) that
decides chat vs code_task. Chat replies use OpenRouter's own auto-router
(openrouter/auto) via LiteLLM; code_task requests go through the existing
runChatTask() flow (Claude Code CLI, unchanged, still using the
subscription token directly).
- Investigated routing Claude itself through LiteLLM via OAuth token
forwarding (general_settings.forward_client_headers_to_llm_api) so the
Pro/Max subscription could be one of the auto-routable options. Confirmed
non-functional: Anthropic returns a generic rate_limit_error for any
direct API call using this token type outside the real Claude Code CLI,
reproduced with plain curl straight to api.anthropic.com. Not included.
- MATRIX_BOT_USER_ID now required and set explicitly (self-message filtering
can no longer rely on a command-prefix mismatch once there isn't one).
Same root cause as the earlier whoami fix: the mixin's initial
/joined_rooms scan 404s against Continuwuity even though the endpoint
works fine when called directly. Only auto-join-on-invite was actually
needed, so a plain room.invite listener replaces the whole mixin.
Not actually needed: self-message filtering was only used to avoid the bot
reacting to its own replies, but those never match the !claude command
pattern anyway, so parseCommand() already filters them out for free.