Skip to main content
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
s
string
required
String to escape
return
string
XML-escaped string with &, <, >, and " replaced with entities
Example:
import { escapeXml } from './router.js';

const escaped = escapeXml('Hello <world> & "friends"');
// 'Hello &lt;world&gt; &amp; &quot;friends&quot;'

formatMessages

Formats an array of messages as XML for agent consumption.
function formatMessages(messages: NewMessage[], timezone: string): string
messages
NewMessage[]
required
Array of messages to format
timezone
string
required
IANA timezone identifier (e.g., "America/New_York"). Used to format timestamps in local time and included in the output <context> header.
return
string
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');

stripInternalTags

Removes <internal>...</internal> blocks from agent output.
function stripInternalTags(text: string): string
text
string
required
Text containing potential <internal> tags
return
string
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!"

formatOutbound

Prepares agent output for sending to users (strips internal tags).
function formatOutbound(rawText: string): string
rawText
string
required
Raw text from agent
return
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>
channels
Channel[]
required
Array of available channels
jid
string
required
Chat JID to send to
text
string
required
Text to send
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
channels
Channel[]
required
Array of available channels
jid
string
required
Chat JID to look up
return
Channel | undefined
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
}
Last modified on March 23, 2026