Cogitator
Channels

Gateway

Route messages from messaging platforms to AI agents with sessions, middleware, and streaming.

Overview

The Gateway is the central class of @cogitator-ai/channels. It connects messaging channels (Telegram, Discord, Slack, WhatsApp, WebChat) to your AI agents, handling sessions, middleware, streaming, and message formatting.

pnpm add @cogitator-ai/channels

Quick Start

import { Cogitator, Agent } from '@cogitator-ai/core';
import { Gateway, telegramChannel } from '@cogitator-ai/channels';

const agent = new Agent({
  name: 'assistant',
  model: 'anthropic/claude-sonnet-4-20250514',
  instructions: 'You are a helpful personal assistant.',
});

const gateway = new Gateway({
  agent,
  cogitator: new Cogitator({
    llm: {
      providers: { anthropic: { apiKey: process.env.ANTHROPIC_API_KEY } },
    },
  }),
  channels: [
    telegramChannel({ token: process.env.TG_TOKEN! }),
  ],
});

await gateway.start();

GatewayConfig

interface GatewayConfig {
  agent: Agent | ((user: ChannelUser) => Agent | Promise<Agent>);
  channels: Channel[];
  cogitator: Cogitator;
  memory?: MemoryAdapter;
  middleware?: GatewayMiddleware[];

  session?: {
    threadKey?: (msg: ChannelMessage) => string;
    compaction?: CompactionConfig;
  };

  stream?: StreamConfig;
  reactions?: StatusReactionConfig;
  debounce?: DebounceConfig;
  envelope?: EnvelopeConfig;
  queueMode?: 'parallel' | 'sequential' | 'interrupt' | 'collect';

  onError?: (error: Error, msg: ChannelMessage) => void;
}

Per-user Agents

Pass a factory function to create different agents per user:

const gateway = new Gateway({
  agent: (user) => {
    if (user.id === 'admin') {
      return adminAgent;
    }
    return defaultAgent;
  },
  // ...
});

Message Flow

User sends message
  → Channel converts to ChannelMessage
  → Debouncer (if enabled): buffer rapid messages, merge into one
  → MessageQueue (if configured): sequential/interrupt/collect
  → Middleware pipeline (pairing → rate-limit → ...)
  → Envelope formatting (if enabled): wrap with [channel user +elapsed timestamp]
  → Session lookup/create
  → Status reaction: 👀 → 🤔 (if enabled)
  → cogitator.run(agent, { input, threadId, stream })
  → Status reaction: 👍 or 😱
  → StreamBuffer flushes progressively via editText
  → Platform-specific markdown adaptation
  → Response delivered

Status Reactions

Show processing progress with emoji reactions on the user's message. Users see immediate feedback instead of waiting 10-30 seconds in silence.

const gateway = new Gateway({
  // ...
  reactions: {
    enabled: true,
    emojis: {
      queued: '👀',    // message received
      thinking: '🤔',  // LLM is generating
      tool: '🔥',      // tool execution
      done: '👍',      // response sent
      error: '😱',     // something failed
    },
    debounceMs: 700,    // skip rapid phase changes
    stallSoftMs: 10000, // show 🥱 after 10s on same phase
    stallHardMs: 30000, // show 😨 after 30s
  },
});

Supported channels: Telegram (setMessageReaction), Discord (react). Channels without setReaction silently skip reactions.

Inbound Debouncing

When a user sends 3 messages in 1 second, without debouncing you get 3 parallel LLM calls. With debouncing, they merge into one.

const gateway = new Gateway({
  // ...
  debounce: {
    enabled: true,
    delayMs: 1500,                  // wait 1.5s of silence before processing
    byChannel: { discord: 2000 },   // per-channel override
  },
});

Messages are grouped by channelType:channelId:userId. Texts are joined with newlines, attachments are merged.

Envelope Formatting

Give the LLM context about who is talking, from where, and when — without polluting agent instructions.

const gateway = new Gateway({
  // ...
  envelope: {
    enabled: true,
    includeTimestamp: true,
    includeElapsed: true,
    timezone: 'utc',         // 'utc' | 'local' | IANA timezone string
  },
});

The user's message Hello world becomes:

[telegram Alice +2m30s Feb 28, 20:15] Hello world

The elapsed time shows how long since the last message in this thread. The LLM naturally picks up on timing and context without explicit instructions.

Queue Modes

Control how concurrent messages from the same user/thread are handled.

const gateway = new Gateway({
  // ...
  queueMode: 'sequential',
});
ModeBehavior
parallelDefault. All messages processed immediately.
sequentialFIFO per thread. Next message waits for current to finish.
interruptAbort current processing, start new message immediately.
collectBuffer messages during processing, merge all when idle.

parallel is the default and matches the behavior before Phase 4. For personal assistants, sequential or collect usually give the best UX.

Multi-channel Setup

import {
  Gateway,
  telegramChannel,
  discordChannel,
  slackChannel,
  webchatChannel,
} from '@cogitator-ai/channels';

const gateway = new Gateway({
  agent,
  cogitator,
  channels: [
    telegramChannel({ token: process.env.TG_TOKEN! }),
    discordChannel({ token: process.env.DISCORD_TOKEN! }),
    slackChannel({
      token: process.env.SLACK_BOT_TOKEN!,
      signingSecret: process.env.SLACK_SIGNING_SECRET!,
      appToken: process.env.SLACK_APP_TOKEN!,
    }),
    webchatChannel({ port: 18789 }),
  ],
});

Platform Comparison

FeatureTelegramDiscordSlackWhatsAppWebChat
Streaming (edit)
Reactions
Typing indicator⚠️
Media (vision)
Voice (STT)
Max message length40962000~4000~65536unlimited
Public URL neededwebhook onlynoHTTP mode onlynono
Auth modelBot tokenBot token3 tokensQR codecustom

Session Management

Gateway integrates with @cogitator-ai/memory for persistent conversations:

import { SqliteMemory } from '@cogitator-ai/memory';

const gateway = new Gateway({
  // ...
  memory: new SqliteMemory({ path: './sessions.db' }),
  session: {
    threadKey: (msg) => `${msg.channelType}:${msg.userId}`,
    compaction: {
      strategy: 'summary',
      threshold: 100,
      keepRecent: 20,
    },
  },
});

The threadKey function determines how sessions are grouped. By default, each user gets their own session per channel.

Stats

const stats = gateway.stats;
// {
//   uptime: 7200000,
//   activeSessions: 3,
//   totalSessions: 12,
//   messagesToday: 847,
//   connectedChannels: ['telegram', 'discord'],
// }

Environment Variables

A typical .env for a multi-channel bot:

# LLM
GOOGLE_API_KEY=...

# Telegram
TG_TOKEN=7204891735:AAHr...

# Discord
DISCORD_TOKEN=MTI...

# Slack
SLACK_BOT_TOKEN=xoxb-...
SLACK_SIGNING_SECRET=...
SLACK_APP_TOKEN=xapp-...

# WebChat
WEBCHAT_SECRET=your-secret-token

Graceful Shutdown

process.on('SIGTERM', async () => {
  await gateway.stop();  // flushes debouncer, disposes queue, stops channels
  process.exit(0);
});

On this page