feat: add broadcast groups for multi-agent responses

Enables multiple agents to process the same message simultaneously,
allowing teams of specialized agents with atomic tasks to work together
in the same group using one phone number.

Key features:
- Configure multiple agents per WhatsApp group/DM via routing.broadcast
- Parallel (default) or sequential processing strategies
- Full session isolation (separate history, workspace, tools per agent)
- Minimal code changes (~50 lines in auto-reply.ts)
- Backward compatible with existing routing

Use cases:
- Specialized agent teams (code reviewer + security scanner + docs)
- Multi-language support (EN + DE + ES agents)
- Quality assurance workflows (support + QA agents)
- Task automation (tracker + logger + reporter)

Example config:
{
  "routing": {
    "broadcast": {
      "strategy": "parallel",
      "120363403215116621@g.us": ["alfred", "baerbel", "assistant3"]
    }
  }
}

This enables scaling to hundreds of focused micro-agents on a single
phone number, each handling specific atomic tasks.
This commit is contained in:
sheeek
2026-01-09 03:09:25 +01:00
committed by Peter Steinberger
parent 2469384643
commit 09769d127f
2 changed files with 470 additions and 0 deletions

View File

@@ -49,6 +49,13 @@ import { registerUnhandledRejectionHandler } from "../infra/unhandled-rejections
import { createSubsystemLogger, getChildLogger } from "../logging.js";
import { toLocationContext } from "../providers/location.js";
import { resolveAgentRoute } from "../routing/resolve-route.js";
import {
buildAgentMainSessionKey,
buildAgentPeerSessionKey,
DEFAULT_MAIN_KEY,
normalizeAgentId,
normalizeId,
} from "../routing/session-key.js";
import { defaultRuntime, type RuntimeEnv } from "../runtime.js";
import { isSelfChatMode, jidToE164, normalizeE164 } from "../utils.js";
import { resolveWhatsAppAccount } from "./accounts.js";
@@ -1453,6 +1460,55 @@ export async function monitorWebProvider(
}
}
// Check for broadcast groups
const broadcastAgents = cfg.routing?.broadcast?.[peerId];
if (
broadcastAgents &&
Array.isArray(broadcastAgents) &&
broadcastAgents.length > 0
) {
const strategy = cfg.routing?.broadcast?.strategy || "parallel";
whatsappInboundLog.info(
`Broadcasting message to ${broadcastAgents.length} agents (${strategy})`,
);
const processForAgent = (agentId: string) => {
const normalizedAgentId = normalizeAgentId(agentId);
const agentRoute = {
...route,
agentId: normalizedAgentId,
sessionKey: buildAgentPeerSessionKey({
agentId: normalizedAgentId,
mainKey: DEFAULT_MAIN_KEY,
provider: "whatsapp",
peerKind: msg.chatType === "group" ? "group" : "dm",
peerId: normalizeId(peerId),
}),
mainSessionKey: buildAgentMainSessionKey({
agentId: normalizedAgentId,
mainKey: DEFAULT_MAIN_KEY,
}),
};
return processMessage(msg, agentRoute).catch((err) => {
whatsappInboundLog.error(
`Broadcast agent ${agentId} failed: ${formatError(err)}`,
);
});
};
if (strategy === "sequential") {
for (const agentId of broadcastAgents) {
await processForAgent(agentId);
}
} else {
// Parallel processing (default)
await Promise.allSettled(broadcastAgents.map(processForAgent));
}
return;
}
return processMessage(msg, route);
},
});