Channels/Messaging — Message Processing Pipeline
Status Reactions, Typing Indicators & Message Normalization
The Messaging sub-module handles the user-visible feedback loop: acknowledgement reactions that confirm message receipt, a status reaction state machine that shows agent progress via emoji, typing indicators with keepalive loops and TTL safety, and message normalization across all supported platforms.
Reactions Typing State Machine TTL Safety
📚 What is this & why does it matter?
When a user sends a message to an AI agent, silence is anxiety-inducing. The messaging sub-module ensures users always get visual feedback — an emoji reaction when the message is received, a changing status as the agent thinks and uses tools, and a typing indicator to show the agent is composing a response.
💡 Real-world analogy
Think of a restaurant kitchen with an open window. When a customer orders (message received), the waiter puts a ticket on the rail (“👀 queued” reaction). The chef picks it up (“🤔 thinking”), uses specialized equipment (“🔧 tool”), and plates the dish (“👍 done”). If the kitchen is backed up, a “⏳ please wait” stall indicator appears. The typing indicator is the waiter walking toward your table — you know food is coming.
Architecture
flowchart TD
subgraph AckReactions["Ack Reactions"]
SCOPE["AckReactionScope"]
GATE["shouldAckReaction()"]
WAGATE["shouldAckReactionForWhatsApp()"]
end
subgraph StatusReactions["Status Reactions"]
CTRL["StatusReactionController"]
EMOJIS["StatusReactionEmojis"]
TIMING["StatusReactionTiming"]
DEBOUNCE["Debounce Timer"]
STALL["Stall Timers"]
end
subgraph Typing["Typing Indicators"]
CALLBACKS["TypingCallbacks"]
GUARD["TypingStartGuard"]
KEEPALIVE["KeepaliveLoop"]
TTL["TTL Safety Timer"]
end
SCOPE --> GATE
GATE --> CTRL
EMOJIS --> CTRL
TIMING --> CTRL
CTRL --> DEBOUNCE
CTRL --> STALL
CALLBACKS --> GUARD
CALLBACKS --> KEEPALIVE
CALLBACKS --> TTL
Acknowledgement Reactions
Ack reactions confirm that the agent received a message. The scope determines when reactions appear:
| Scope | Behavior |
|---|---|
all | React to every message |
direct | React only in DMs |
group-all | React to all group messages |
group-mentions | React only when @mentioned in groups (default) |
off / none | Never react |
export function shouldAckReaction(params: AckReactionGateParams): boolean {
const scope = params.scope ?? "group-mentions";
if (scope === "off" || scope === "none") return false;
if (scope === "all") return true;
if (scope === "direct") return params.isDirect;
if (scope === "group-all") return params.isGroup;
if (scope === "group-mentions") {
if (!params.isMentionableGroup) return false;
if (!params.requireMention) return false;
if (!params.canDetectMention) return false;
return params.effectiveWasMentioned || params.shouldBypassMention === true;
}
return false;
}
Status Reaction State Machine
The status reaction controller provides visual agent state via emoji reactions. Each state transition is debounced, stall timers detect long-running tasks, and terminal states fire immediately.
Default Emoji Map
| State | Emoji | Meaning |
|---|---|---|
| queued | 👀 | Message received, waiting to process |
| thinking | 🤔 | Agent is reasoning |
| tool | 🔥 | Executing a tool |
| coding | 👨💻 | Running code/exec tool |
| web | ⚡ | Web search/fetch in progress |
| done | 👍 | Response complete |
| error | 😱 | Processing failed |
| stallSoft | ⏳ | Taking longer than expected (10s) |
| stallHard | ⚠️ | Significantly delayed (30s) |
| compacting | ✍ | Context compaction in progress |
stateDiagram-v2
[*] --> queued
queued --> thinking : Agent starts processing
thinking --> tool : Tool invoked
tool --> thinking : Tool returns
thinking --> coding : Code exec tool
coding --> thinking : Code complete
thinking --> web : Web tool
web --> thinking : Web returns
thinking --> compacting : Context overflow
compacting --> thinking : Compaction done
thinking --> done : Reply ready
tool --> done : Reply ready
thinking --> error : Failure
tool --> error : Failure
thinking --> stallSoft : 10s elapsed
stallSoft --> stallHard : 30s elapsed
stallSoft --> done : Reply ready
stallHard --> done : Reply ready
done --> [*]
error --> [*]
Timing Defaults
| Parameter | Default | Description |
|---|---|---|
debounceMs | 700 | Delay before applying non-terminal emoji |
stallSoftMs | 10,000 | Soft stall warning threshold |
stallHardMs | 30,000 | Hard stall warning threshold |
doneHoldMs | 1,500 | How long to hold done emoji |
errorHoldMs | 2,500 | How long to hold error emoji |
Typing Indicators
The typing system provides reliable typing indicator management with three safety layers:
- Start guard — Tracks consecutive failures, trips the keepalive loop after N failures (default 2)
- Keepalive loop — Fires
start()every 3s to refresh the typing indicator - TTL safety — Auto-stops typing after 60s maximum duration
export function createTypingCallbacks(params: {
start: () => Promise<void>;
stop?: () => Promise<void>;
onStartError: (err: unknown) => void;
keepaliveIntervalMs?: number; // Default: 3000
maxConsecutiveFailures?: number; // Default: 2
maxDurationMs?: number; // Default: 60000
}): TypingCallbacks
Tool-Specific Emoji Resolution
When a tool is invoked, the controller resolves the appropriate emoji based on tool name tokens:
| Token Match | Emoji |
|---|---|
exec, bash, read, write, edit | coding (👨💻) |
web_search, web_fetch, browser | web (⚡) |
| any other tool | tool (🔥) |
Focus Deep-Dives
👌 Ack Reactions
Reaction scope gating logic for DM, group, and mention-based contexts with WhatsApp-specific handling.
🎥 Media Handling
Media attachment processing, size/type constraints, and channel-specific format conversion.
🔄 Message Normalization
Platform-specific message format normalization, markdown stripping, and context extraction.