Messaging

Chat platform integration

Messaging Guide

Prompt-first procedure: Describe the outcome you want in your agent conversation. The agent should select and load the appropriate AIWG assets, explain material changes, request any needed approval, and report verification evidence. Exact commands and flags appear only in the CLI reference.

AIWG messaging integration connects your project to Slack, Discord, and Telegram for real-time notifications, interactive commands, and 2-way AI chat. The messaging subsystem runs within the daemon process.

Overview

The messaging system provides:

  • Event notifications — Receive alerts for agent loop completions, security issues, build failures, and HITL gates
  • Interactive commands — Query status, approve gates, and manage workflows from chat
  • 2-way AI chat — Ask questions about your project directly from messaging platforms
  • Multi-platform — Same event stream delivered to all connected platforms simultaneously

Quick Start

1. Set environment variables

Each platform requires a bot token via environment variable:

# Slack
export AIWG_SLACK_TOKEN="xoxb-your-bot-token"
export AIWG_SLACK_CHANNEL="#aiwg-notifications"

# Discord
export AIWG_DISCORD_TOKEN="your-discord-bot-token"
export AIWG_DISCORD_CHANNEL="channel-id"

# Telegram
export AIWG_TELEGRAM_TOKEN="123456:ABCdefGHIjklMNOpqrsTUVwxyz"
export AIWG_TELEGRAM_CHAT_ID="-1001234567890"

2. Start the daemon

Use AIWG to complete this documented outcome: 2. Start the daemon
Have it inspect the current state, explain the plan, ask before material
changes, and report the result with verification evidence.

The messaging hub auto-discovers enabled adapters from environment variables. No configuration file changes needed.

3. Verify

The daemon log confirms adapter loading:

[messaging] Hub started with 2 adapter(s): slack, telegram
[messaging] 2-way AI chat enabled

Platform Setup

Slack

1. Create a Slack app at https://api.slack.com/apps 2. Add Bot Token Scopes: `chat:write`, `commands`, `app_mentions:read` 3. Install to workspace and copy the Bot User OAuth Token 4. Set environment variables:

export AIWG_SLACK_TOKEN="xoxb-..."
export AIWG_SLACK_CHANNEL="#aiwg-notifications"  # Default channel for notifications

Discord

1. Create a Discord application at https://discord.com/developers/applications 2. Add a Bot under the application 3. Enable Message Content Intent in Bot settings 4. Generate an invite URL with `Send Messages` and `Read Message History` permissions 5. Invite the bot to your server 6. Set environment variables:

export AIWG_DISCORD_TOKEN="your-bot-token"
export AIWG_DISCORD_CHANNEL_ID="channel-id"  # Right-click channel → Copy ID

To get a channel ID: enable Developer Mode in Discord (Settings → Advanced), then right-click any channel → Copy Channel ID.

Multi-channel configuration

Route different event types to separate Discord channels using `config.rooms` in your daemon configuration:

{
  "messaging": {
    "discord": {
      "botToken": "your-bot-token",
      "rooms": [
        {
          "channel_id": "1234567890",
          "label": "dev-notifications",
          "is_default": true,
          "purpose": "interactive"
        },
        {
          "channel_id": "9876543210",
          "label": "security-alerts",
          "is_default": true,
          "purpose": "notifications"
        },
        {
          "channel_id": "1122334455",
          "label": "debug-logs",
          "is_default": false,
          "purpose": "logs"
        }
      ]
    }
  }
}
FieldDescription
`channel_id`Discord channel ID (also accepts `channelId`)
`label`Human-readable name for this channel
`is_default`When `true`, the channel receives all broadcast messages (e.g., agent loop completions, health alerts). When `false`, the channel only receives messages sent to it explicitly. Also accepts `isDefault`.
`purpose`Informational. `interactive` (commands), `notifications` (one-way events), `logs` (verbose output)

Rooms with `is_default: true` receive `broadcastToRooms()` messages. Multiple default rooms can exist — all receive the same broadcast. Rooms with `is_default: false` only receive messages sent via `sendToRoom(message, channelId)`.

Single-channel fallback: If `rooms` is omitted, the adapter uses `AIWG_DISCORD_CHANNEL_ID` or `defaultChannelId` as the sole channel with `is_default: true`.

Telegram

1. Message @BotFather on Telegram 2. Send `/newbot` and follow the prompts 3. Copy the bot token 4. Add the bot to your group/channel 5. Get the chat ID (send a message, then check `https://api.telegram.org/bot<token>/getUpdates`) 6. Set environment variables:

export AIWG_TELEGRAM_TOKEN="123456:ABCdef..."
export AIWG_TELEGRAM_CHAT_ID="-1001234567890"

Multi-room configuration

Route different event types to separate Telegram chats or groups using `config.rooms`:

{
  "messaging": {
    "telegram": {
      "botToken": "123456:ABCdef...",
      "rooms": [
        {
          "chat_id": "-1001234567890",
          "label": "dev-team",
          "is_default": true,
          "purpose": "interactive"
        },
        {
          "chat_id": "-1009876543210",
          "label": "security-channel",
          "is_default": true,
          "purpose": "notifications"
        }
      ]
    }
  }
}
FieldDescription
`chat_id`Telegram chat/group/channel ID (also accepts `chatId`)
`label`Human-readable name for this chat
`is_default`When `true`, receives broadcast messages. Also accepts `isDefault`.
`purpose`Informational. `interactive`, `notifications`, or `logs`

Single-room fallback: If `rooms` is omitted, the adapter uses `AIWG_TELEGRAM_CHAT_ID` or `defaultChatId` as the sole room with `is_default: true`.

Commands

All platforms support the same command set. Prefix commands with `/` in chat:

CommandPermissionDescription
`/help`readList available commands
`/status`readShow project status and daemon health
`/ralph-status`readShow active agent loop status
`/health`readRun health check on all subsystems
`/ask <question>`readAsk the AI a question about your project
`/approve <gate-id>`writeApprove a pending HITL gate
`/reject <gate-id> [reason]`writeReject a pending HITL gate with optional reason

Permissions

Commands have two permission levels:

  • read — Available to everyone in the channel
  • write — Requires explicit permission grant

Grant write permissions via the `writeUsers` option:

const hub = await createMessagingHub({
  writeUsers: ['U12345', '[email protected]'],
});

Or in daemon configuration, set `AIWG_WRITE_USERS` environment variable (comma-separated user IDs).

Command Examples

Check project status:

You: /status
Bot: Project: my-project
     Phase: Construction
     Daemon uptime: 3600s
     Health: healthy

Approve a HITL gate:

You: /approve gate-e2c-001
Bot: Gate gate-e2c-001 approved

Ask a question:

You: /ask what is our test coverage?
Bot: Based on the project configuration, the test suite runs via
     `npx vitest run` and currently has approximately 2,619 tests...

2-Way AI Chat

Beyond slash commands, you can send free-text messages to the bot for AI-powered responses. The bot uses `claude -p` with your project's full context (CLAUDE.md, codebase, etc.).

How It Works

1. Send a message to the bot (DM or mention in a channel) 2. The messaging hub forwards it to the ChatHandler 3. ChatHandler spawns a `claude -p` process with conversation context 4. The AI response is sent back to the originating platform

Multi-Turn Conversations

The chat system maintains conversation history per chat channel. Each conversation tracks up to 10 message pairs (configurable), so the AI remembers context from recent messages:

You: What testing framework do we use?
Bot: The project uses Vitest for testing...

You: How do I add a new test file?
Bot: Based on the existing test structure, create a new file in
     test/unit/ following the pattern...

Configuration

Chat behavior is configured via `createMessagingHub()` options or environment variables:

SettingDefaultDescription
`maxConcurrent`3Maximum simultaneous AI processes
`maxContextMessages`10Conversation history depth (message pairs)
`timeoutMs`120000AI response timeout (milliseconds)
`maxResponseLength`4000Maximum response characters

Concurrency Limits

To prevent resource exhaustion, the chat system limits concurrent AI processes:

  • Global limit: Maximum 3 simultaneous `claude -p` processes (configurable)
  • Per-chat dedup: Only one AI process per chat at a time
  • Busy response: When limits are reached, users receive a "please wait" message

Disabling Chat

To run messaging with notifications and commands only (no AI chat):

const hub = await createMessagingHub({
  chatHandler: false,
});

Event Topics

The messaging system forwards events from the internal event bus to all connected platforms. Events are formatted into human-readable messages with severity indicators.

Event Categories

CategoryTopicsDescription
Al`ralph.started`, `ralph.iteration`, `ralph.completed`, `ralph.failed`, `ralph.aborted`Agent loop lifecycle events
HITL Gates`gate.pending`, `gate.approved`, `gate.rejected`, `gate.timeout`Human-in-the-loop gate events
Security`security.critical`, `security.warning`, `security.scan_done`Security scan results
Health`health.check`, `health.degraded`, `health.recovered`System health transitions
Build`build.failed`, `build.passed`Build/test results
Daemon`daemon.started`, `daemon.stopping`Daemon lifecycle
Chat`chat.message`, `chat.response`, `chat.error`Chat session events

Event Severity

Events carry a severity level that affects formatting:

SeverityDisplayUse Case
`info`Normal textStatus updates, completions
`warning`HighlightedDegraded health, gate timeouts
`critical`Alert/urgentSecurity issues, failures

Publishing Custom Events

From your own integrations, publish events to the messaging hub:

hub.publish({
  topic: 'custom.event',
  source: 'my-integration',
  severity: 'info',
  summary: 'Deployment completed successfully',
  details: { version: '1.2.3', environment: 'staging' },
  timestamp: new Date().toISOString(),
});

Custom Adapters

To add support for a new messaging platform, extend `BaseAdapter`:

import { BaseAdapter } from '../adapters/base.mjs';

export class MyPlatformAdapter extends BaseAdapter {
  constructor(config) {
    super('myplatform');
    this.config = config;
  }

  async initialize() {
    // Connect to platform API
    this._setConnected();
  }

  async send(message, channel) {
    // Format and send message to platform
    this._recordSend();
    return { messageId: '...', channelId: channel, success: true };
  }

  async update(messageId, message) {
    // Update an existing message
  }

  async shutdown() {
    this._setDisconnected();
  }
}

Required Methods

MethodDescription
`initialize()`Connect to platform, set up event listeners
`send(message, channel)`Send a formatted message
`update(messageId, message)`Update an existing message
`shutdown()`Disconnect and clean up

Protected Methods (from BaseAdapter)

MethodDescription
`_setConnected()`Mark adapter as connected
`_setDisconnected()`Mark adapter as disconnected
`_recordSend()`Increment sent message counter
`_recordReceive()`Increment received message counter
`_recordError(error)`Record an error
`_dispatchCommand(command, args, context)`Forward command to registered handlers
`_dispatchMessage(text, context)`Forward free-text message to handlers

Registering Inbound Handlers

For platforms that support inbound messages, call the dispatch methods from your adapter's event listeners:

// In your adapter's initialize():
this.client.on('message', (msg) => {
  if (msg.text.startsWith('/')) {
    const [command, ...args] = msg.text.slice(1).split(' ');
    this._dispatchCommand(command, args, {
      chatId: msg.chatId,
      from: msg.author,
    });
  } else {
    this._dispatchMessage(msg.text, {
      chatId: msg.chatId,
      from: msg.author,
    });
  }
});

Architecture

┌─────────────────────────────────────────────────┐
│                  Daemon Process                  │
│                                                  │
│  ┌──────────┐    ┌──────────────────┐           │
│  │ EventBus │───→│ MessageFormatter │           │
│  └──────────┘    └────────┬─────────┘           │
│       ↑                   ↓                      │
│  Events from         Formatted messages          │
│  Al, Health,      ┌─────────┬─────────┐      │
│  Security, etc.      ↓         ↓         ↓      │
│                  ┌───────┐ ┌───────┐ ┌────────┐ │
│                  │ Slack │ │Discord│ │Telegram│  │
│                  └───┬───┘ └───┬───┘ └────┬───┘ │
│                      ↓         ↓          ↓      │
│  Inbound:     ┌──────────────────────────────┐  │
│  /commands    │      CommandRouter            │  │
│  free text    │      ChatHandler              │  │
│               └──────────────────────────────┘  │
└─────────────────────────────────────────────────┘

Troubleshooting

No adapters loaded

Verify environment variables are set:

echo $AIWG_SLACK_TOKEN
echo $AIWG_DISCORD_TOKEN
echo $AIWG_TELEGRAM_TOKEN

At least one token must be set for messaging to activate.

Bot not responding to commands

  • Verify the bot has appropriate permissions in the channel
  • Check daemon logs: `tail -f .aiwg/daemon/daemon.log`
  • Ensure commands start with `/` (e.g., `/status`, not `status`)

AI chat responses are slow

Each response spawns a `claude -p` process with full project context. For large projects, this can take 30-60 seconds. To speed up:

  • Reduce project context (optimize CLAUDE.md)
  • Increase `maxConcurrent` if you have resources
  • Use `/ask` for quick questions (routes through command system)

Messages truncated

Response length is capped at `maxResponseLength` (default 4000 chars) to respect platform limits. Truncated responses end with `[...truncated]`. Adjust the limit in chat handler configuration.

Rate limiting

If a platform rate-limits the bot, errors appear in daemon logs. The adapters handle rate limits gracefully with backoff, but sustained high-volume notifications may require message batching configuration.

Cross-References

  • Daemon Guide — Daemon setup and management
  • Al Guide — Agent loops with messaging notifications
  • `.aiwg/architecture/adrs/ADR-messaging-bot-mode.md` — Architecture decision
  • `.aiwg/architecture/adrs/ADR-2way-chat.md` — Chat architecture
  • `tools/messaging/README.md` — Developer documentation