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.
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
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"aiwg daemon startThe messaging hub auto-discovers enabled adapters from environment variables. No configuration file changes needed.
The daemon log confirms adapter loading:
[messaging] Hub started with 2 adapter(s): slack, telegram
[messaging] 2-way AI chat enabled
- Create a Slack app at https://api.slack.com/apps
- Add Bot Token Scopes:
chat:write,commands,app_mentions:read - Install to workspace and copy the Bot User OAuth Token
- Set environment variables:
export AIWG_SLACK_TOKEN="xoxb-..."
export AIWG_SLACK_CHANNEL="#aiwg-notifications" # Default channel for notifications- Create a Discord application at https://discord.com/developers/applications
- Add a Bot under the application
- Enable Message Content Intent in Bot settings
- Generate an invite URL with
Send MessagesandRead Message Historypermissions - Invite the bot to your server
- Set environment variables:
export AIWG_DISCORD_TOKEN="your-bot-token"
export AIWG_DISCORD_CHANNEL_ID="channel-id" # Right-click channel → Copy IDTo get a channel ID: enable Developer Mode in Discord (Settings → Advanced), then right-click any channel → Copy Channel ID.
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"
}
]
}
}
}| Field | Description |
|---|---|
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.
- Message @BotFather on Telegram
- Send
/newbotand follow the prompts - Copy the bot token
- Add the bot to your group/channel
- Get the chat ID (send a message, then check
https://api.telegram.org/bot<token>/getUpdates) - Set environment variables:
export AIWG_TELEGRAM_TOKEN="123456:ABCdef..."
export AIWG_TELEGRAM_CHAT_ID="-1001234567890"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"
}
]
}
}
}| Field | Description |
|---|---|
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.
All platforms support the same command set. Prefix commands with / in chat:
| Command | Permission | Description |
|---|---|---|
/help |
read | List available commands |
/status |
read | Show project status and daemon health |
/ralph-status |
read | Show active agent loop status |
/health |
read | Run health check on all subsystems |
/ask <question> |
read | Ask the AI a question about your project |
/approve <gate-id> |
write | Approve a pending HITL gate |
/reject <gate-id> [reason] |
write | Reject a pending HITL gate with optional reason |
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', 'user@example.com'],
});Or in daemon configuration, set AIWG_WRITE_USERS environment variable (comma-separated user IDs).
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...
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.).
- Send a message to the bot (DM or mention in a channel)
- The messaging hub forwards it to the ChatHandler
- ChatHandler spawns a
claude -pprocess with conversation context - The AI response is sent back to the originating platform
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...
Chat behavior is configured via createMessagingHub() options or environment variables:
| Setting | Default | Description |
|---|---|---|
maxConcurrent |
3 | Maximum simultaneous AI processes |
maxContextMessages |
10 | Conversation history depth (message pairs) |
timeoutMs |
120000 | AI response timeout (milliseconds) |
maxResponseLength |
4000 | Maximum response characters |
To prevent resource exhaustion, the chat system limits concurrent AI processes:
- Global limit: Maximum 3 simultaneous
claude -pprocesses (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
To run messaging with notifications and commands only (no AI chat):
const hub = await createMessagingHub({
chatHandler: false,
});The messaging system forwards events from the internal event bus to all connected platforms. Events are formatted into human-readable messages with severity indicators.
| Category | Topics | Description |
|---|---|---|
| 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 |
Events carry a severity level that affects formatting:
| Severity | Display | Use Case |
|---|---|---|
info |
Normal text | Status updates, completions |
warning |
Highlighted | Degraded health, gate timeouts |
critical |
Alert/urgent | Security issues, failures |
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(),
});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();
}
}| Method | Description |
|---|---|
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 |
| Method | Description |
|---|---|
_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 |
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,
});
}
});┌─────────────────────────────────────────────────┐
│ Daemon Process │
│ │
│ ┌──────────┐ ┌──────────────────┐ │
│ │ EventBus │───→│ MessageFormatter │ │
│ └──────────┘ └────────┬─────────┘ │
│ ↑ ↓ │
│ Events from Formatted messages │
│ Al, Health, ┌─────────┬─────────┐ │
│ Security, etc. ↓ ↓ ↓ │
│ ┌───────┐ ┌───────┐ ┌────────┐ │
│ │ Slack │ │Discord│ │Telegram│ │
│ └───┬───┘ └───┬───┘ └────┬───┘ │
│ ↓ ↓ ↓ │
│ Inbound: ┌──────────────────────────────┐ │
│ /commands │ CommandRouter │ │
│ free text │ ChatHandler │ │
│ └──────────────────────────────┘ │
└─────────────────────────────────────────────────┘
Verify environment variables are set:
echo $AIWG_SLACK_TOKEN
echo $AIWG_DISCORD_TOKEN
echo $AIWG_TELEGRAM_TOKENAt least one token must be set for messaging to activate.
- 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, notstatus)
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
maxConcurrentif you have resources - Use
/askfor quick questions (routes through command system)
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.
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.
- 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 architecturetools/messaging/README.md— Developer documentation