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.

5
Core Files
10
Reaction States
3s
Keepalive Interval
TypeScript
Language

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:

ScopeBehavior
allReact to every message
directReact only in DMs
group-allReact to all group messages
group-mentionsReact only when @mentioned in groups (default)
off / noneNever 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

StateEmojiMeaning
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

ParameterDefaultDescription
debounceMs700Delay before applying non-terminal emoji
stallSoftMs10,000Soft stall warning threshold
stallHardMs30,000Hard stall warning threshold
doneHoldMs1,500How long to hold done emoji
errorHoldMs2,500How long to hold error emoji

Typing Indicators

The typing system provides reliable typing indicator management with three safety layers:

  1. Start guard — Tracks consecutive failures, trips the keepalive loop after N failures (default 2)
  2. Keepalive loop — Fires start() every 3s to refresh the typing indicator
  3. 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 MatchEmoji
exec, bash, read, write, editcoding (👨‍💻)
web_search, web_fetch, browserweb (⚡)
any other tooltool (🔥)

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.