| index 421909a..1416f65 100644 |
| --- a/agent/runtime.mjs |
| +++ b/agent/runtime.mjs |
| @@ -50,12 +50,27 @@ export function runBot({ name, session, builtinPersona = "", builtinDocs = "" }) |
| |
| const keys = new Map(); // threadId -> { key: CryptoKey, kind: "dm"|"group"|"self" } |
| const cursors = new Map(); // threadId -> last message id we've processed |
| + let agentNames = new Set(); // roster labels of ALL agents — never reply to one |
| |
| const get = (path) => |
| fetch(`${BASE}${path}${path.includes("?") ? "&" : "?"}token=${encodeURIComponent(session.token)}`).then((r) => |
| r.json() |
| ); |
| |
| + // Bots never talk to bots: a bot's mention trigger matches its bare name, and |
| + // bot-authored text naturally names other bots (Eleven recommends Claude, the |
| + // Announcer's notes mention Eleven) — without this, two bots in one group |
| + // ping-pong forever. Name-based (payloads aren't sender-signed yet), refreshed |
| + // on every (re)connect so newly added bots are picked up within a cycle. |
| + async function loadAgents() { |
| + try { |
| + const { roster = [] } = await get("/api/roster"); |
| + agentNames = new Set(roster.filter((e) => e.is_agent).map((e) => e.label)); |
| + } catch (e) { |
| + tag(`loadAgents: ${e.message}`); |
| + } |
| + } |
| + |
| const maxId = (messages) => messages.reduce((m, x) => Math.max(m, x.id || 0), 0); |
| |
| // Fetch our threads and cache the (unwrapped) key for any we don't hold yet. A |
| @@ -178,6 +193,7 @@ export function runBot({ name, session, builtinPersona = "", builtinDocs = "" }) |
| } catch {} |
| }, 30000); |
| await loadConfig(); |
| + await loadAgents(); |
| await loadThreads(); |
| await catchUp(); |
| for (const d of queue.splice(0)) ws.send(d); |
| @@ -204,6 +220,7 @@ export function runBot({ name, session, builtinPersona = "", builtinDocs = "" }) |
| return; |
| } |
| if (p.name === name) return; // our own message, echoed back |
| + if (agentNames.has(p.name)) return; // bots never talk to bots (see loadAgents) |
| if (!wantsReply(entry.kind, p.text)) return; |
| await reply(threadId, entry); |
| } |