Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2c00f6f8b |
@@ -30,6 +30,10 @@ MATRIX_CONTROL_ROOM_ID=
|
|||||||
# Required: without it the bot can't tell its own messages apart from real ones and would
|
# Required: without it the bot can't tell its own messages apart from real ones and would
|
||||||
# reply to itself in a loop, so it refuses to start.
|
# reply to itself in a loop, so it refuses to start.
|
||||||
MATRIX_BOT_USER_ID=
|
MATRIX_BOT_USER_ID=
|
||||||
|
# Comma-separated Matrix IDs of OTHER agents sharing the control room (currently just
|
||||||
|
# @hermes:...) — without this, claude-bot treats every message another bot posts as
|
||||||
|
# fresh chat input and replies to it, which that bot may then react to in turn.
|
||||||
|
OTHER_AGENT_USER_IDS=@hermes:matrix.apps.williamturner.eu
|
||||||
# Comma-separated "owner/repo" list the chat router is allowed to open code-change PRs
|
# Comma-separated "owner/repo" list the chat router is allowed to open code-change PRs
|
||||||
# against. A plain chat message mentioning a repo NOT in this list is treated as chat,
|
# against. A plain chat message mentioning a repo NOT in this list is treated as chat,
|
||||||
# never as a code task — the router only matches confidently against known repos.
|
# never as a code task — the router only matches confidently against known repos.
|
||||||
|
|||||||
@@ -11,6 +11,15 @@ const GITEA_URL = process.env.GITEA_URL;
|
|||||||
// to the endpoint itself. This is also the only reliable way to filter the bot's own
|
// to the endpoint itself. This is also the only reliable way to filter the bot's own
|
||||||
// messages now that there's no command prefix to naturally exclude them by.
|
// messages now that there's no command prefix to naturally exclude them by.
|
||||||
const BOT_USER_ID = process.env.MATRIX_BOT_USER_ID;
|
const BOT_USER_ID = process.env.MATRIX_BOT_USER_ID;
|
||||||
|
// Other agents sharing this room (currently just Hermes) — their own messages must be
|
||||||
|
// ignored the same way claude-bot ignores its own, or claude-bot's classifier treats
|
||||||
|
// every message another bot posts as fresh chat input and replies to it, which that
|
||||||
|
// bot may then react to in turn. Found the hard way: a single @hermes mention cascaded
|
||||||
|
// into claude-bot replying to Hermes's own thread messages ("Hermes says: ...").
|
||||||
|
const OTHER_AGENT_USER_IDS = (process.env.OTHER_AGENT_USER_IDS || "")
|
||||||
|
.split(",")
|
||||||
|
.map((id) => id.trim())
|
||||||
|
.filter(Boolean);
|
||||||
const KNOWN_REPOS = (process.env.KNOWN_REPOS || "")
|
const KNOWN_REPOS = (process.env.KNOWN_REPOS || "")
|
||||||
.split(",")
|
.split(",")
|
||||||
.map((r) => r.trim())
|
.map((r) => r.trim())
|
||||||
@@ -47,6 +56,7 @@ export async function startMatrixBot() {
|
|||||||
client.on("room.message", async (roomId, event) => {
|
client.on("room.message", async (roomId, event) => {
|
||||||
if (roomId !== CONTROL_ROOM_ID) return;
|
if (roomId !== CONTROL_ROOM_ID) return;
|
||||||
if (event.sender === BOT_USER_ID) return;
|
if (event.sender === BOT_USER_ID) return;
|
||||||
|
if (OTHER_AGENT_USER_IDS.includes(event.sender)) return;
|
||||||
const body = event.content?.body;
|
const body = event.content?.body;
|
||||||
if (!body) return;
|
if (!body) return;
|
||||||
// Messages explicitly addressed to another agent in this room (currently just
|
// Messages explicitly addressed to another agent in this room (currently just
|
||||||
@@ -67,6 +77,7 @@ export async function startMatrixBot() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const reply = await chatReply(body);
|
const reply = await chatReply(body);
|
||||||
|
console.log("chat reply sent, length:", reply.length);
|
||||||
await client.sendText(roomId, truncate(reply));
|
await client.sendText(roomId, truncate(reply));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("message handling failed", err);
|
console.error("message handling failed", err);
|
||||||
|
|||||||
+12
-4
@@ -77,7 +77,15 @@ services:
|
|||||||
# rooms (DMs to it would respond unprompted, per Hermes's own default behavior).
|
# rooms (DMs to it would respond unprompted, per Hermes's own default behavior).
|
||||||
MATRIX_ALLOWED_USERS: ${MATRIX_HUMAN_USER_ID}
|
MATRIX_ALLOWED_USERS: ${MATRIX_HUMAN_USER_ID}
|
||||||
MATRIX_REQUIRE_MENTION: "true"
|
MATRIX_REQUIRE_MENTION: "true"
|
||||||
OPENROUTER_API_KEY: ${OPENROUTER_API_KEY}
|
# Routed through the local litellm gateway, not OpenRouter directly — same pattern
|
||||||
|
# as claude-agent's chat path, one place to hold the OpenRouter credential and swap
|
||||||
|
# models. Hermes's "main"/custom-endpoint provider is any OpenAI-compatible API
|
||||||
|
# reachable via OPENAI_BASE_URL + OPENAI_API_KEY. Note: this does NOT give Hermes
|
||||||
|
# access to the Claude Pro/Max subscription — that's blocked by Anthropic itself for
|
||||||
|
# any caller other than the real Claude Code CLI, proven earlier in this session
|
||||||
|
# (reproduced with plain curl straight to api.anthropic.com, LiteLLM or not).
|
||||||
|
OPENAI_BASE_URL: http://litellm:4000/v1
|
||||||
|
OPENAI_API_KEY: ${LITELLM_MASTER_KEY}
|
||||||
# Left disabled: Hermes itself warns that a network-reachable API server combined
|
# Left disabled: Hermes itself warns that a network-reachable API server combined
|
||||||
# with the default unsandboxed ('local') terminal backend gives any caller full
|
# with the default unsandboxed ('local') terminal backend gives any caller full
|
||||||
# terminal/file access within the container. Matrix is the actual interface in use;
|
# terminal/file access within the container. Matrix is the actual interface in use;
|
||||||
@@ -113,10 +121,10 @@ services:
|
|||||||
MATRIX_BOT_TOKEN: ${MATRIX_BOT_TOKEN}
|
MATRIX_BOT_TOKEN: ${MATRIX_BOT_TOKEN}
|
||||||
MATRIX_CONTROL_ROOM_ID: ${MATRIX_CONTROL_ROOM_ID}
|
MATRIX_CONTROL_ROOM_ID: ${MATRIX_CONTROL_ROOM_ID}
|
||||||
MATRIX_BOT_USER_ID: ${MATRIX_BOT_USER_ID}
|
MATRIX_BOT_USER_ID: ${MATRIX_BOT_USER_ID}
|
||||||
|
OTHER_AGENT_USER_IDS: ${OTHER_AGENT_USER_IDS}
|
||||||
KNOWN_REPOS: ${KNOWN_REPOS}
|
KNOWN_REPOS: ${KNOWN_REPOS}
|
||||||
# All model calls now go through the local litellm service, not OpenRouter directly —
|
# Chat replies go through the local litellm service (OpenRouter's models, incl. its
|
||||||
# one gateway for OpenRouter's models (incl. its auto-router) and, for the
|
# auto-router), not OpenRouter directly.
|
||||||
# claude-subscription route, Anthropic itself via the forwarded OAuth token above.
|
|
||||||
LITELLM_BASE_URL: http://litellm:4000
|
LITELLM_BASE_URL: http://litellm:4000
|
||||||
LITELLM_MASTER_KEY: ${LITELLM_MASTER_KEY}
|
LITELLM_MASTER_KEY: ${LITELLM_MASTER_KEY}
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
Reference in New Issue
Block a user