The message router handles formatting messages for agents and routing outbound messages to the correct channel.
Core functions
escapeXml
Escapes special XML characters in strings.
function escapeXml(s: string): string
XML-escaped string with &, <, >, and " replaced with entities
Example:
import { escapeXml } from './router.js';
const escaped = escapeXml('Hello <world> & "friends"');
// 'Hello <world> & "friends"'
Formats an array of messages as XML for agent consumption.
function formatMessages(messages: NewMessage[], timezone: string): string
Array of messages to format
IANA timezone identifier (e.g., "America/New_York"). Used to format timestamps in local time and included in the output <context> header.
A <context> header followed by XML-formatted messages wrapped in <messages> tags
Output format:
<context timezone="America/New_York" />
<messages>
<message sender="John Doe" time="2:34 PM">Hello!</message>
<message sender="Jane Smith" time="2:35 PM">Hi there!</message>
</messages>
Example:
import { formatMessages } from './router.js';
const messages = [
{
id: '1',
chat_jid: '123@g.us',
sender: '456@s.whatsapp.net',
sender_name: 'John',
content: 'Hello!',
timestamp: '2026-02-28T12:34:56.789Z',
},
];
const formatted = formatMessages(messages, 'America/New_York');
Removes <internal>...</internal> blocks from agent output.
function stripInternalTags(text: string): string
Text containing potential <internal> tags
Text with all <internal> blocks removed and trimmed
Purpose:
Agents use <internal> tags for internal reasoning that should not be shown to users.
Example:
import { stripInternalTags } from './router.js';
const raw = `<internal>Analyzing request...</internal>Here's your answer!`;
const clean = stripInternalTags(raw);
// "Here's your answer!"
Prepares agent output for sending to users (strips internal tags).
function formatOutbound(rawText: string): string
Cleaned text ready for user display (empty string if no content after stripping)
Example:
import { formatOutbound } from './router.js';
const raw = `<internal>Debug info</internal>Response to user`;
const outbound = formatOutbound(raw);
// "Response to user"
routeOutbound
Routes a message to the appropriate channel.
function routeOutbound(
channels: Channel[],
jid: string,
text: string,
): Promise<void>
Array of available channels
Throws: Error if no connected channel owns the JID
Example:
import { routeOutbound } from './router.js';
await routeOutbound(channels, '123@g.us', 'Hello!');
findChannel
Finds the channel that owns a given JID.
function findChannel(
channels: Channel[],
jid: string,
): Channel | undefined
Array of available channels
Channel that owns the JID, or undefined if none found
Example:
import { findChannel } from './router.js';
const channel = findChannel(channels, '123@g.us');
if (channel) {
await channel.sendMessage('123@g.us', 'Hello!');
}
Types
NewMessage
interface NewMessage {
id: string; // Unique message ID
chat_jid: string; // Chat identifier (e.g., "123@g.us")
sender: string; // Sender JID (e.g., "456@s.whatsapp.net")
sender_name: string; // Display name
content: string; // Message text
timestamp: string; // ISO 8601 timestamp
is_from_me?: boolean; // Whether sent by the bot
is_bot_message?: boolean; // Whether this is a bot message
}
Channel
interface Channel {
name: string;
connect(): Promise<void>;
sendMessage(jid: string, text: string): Promise<void>;
isConnected(): boolean;
ownsJid(jid: string): boolean;
disconnect(): Promise<void>;
setTyping?(jid: string, isTyping: boolean): Promise<void>; // Optional
syncGroups?(force: boolean): Promise<void>; // Optional
}