Files
agent/docs/channels.md
highperfocused 089bd7bd48
All checks were successful
Build and Push Docker Image / build (push) Successful in 1m15s
Add HTTP gateway with streaming chat and multi-client conversation mapping (#1)
Reviewed-on: MoA/agent#1
Co-authored-by: highperfocused <highperfocused@pm.me>
Co-committed-by: highperfocused <highperfocused@pm.me>
2026-03-12 14:47:45 +01:00

6.1 KiB

Channels: Web UI, Slack, Matrix, and custom adapters

This document explains how channels work in the gateway and how to integrate transports like Web UI, Slack, Matrix, or your own chat source.


What is a "channel" in this project?

In this gateway, a channel is not a separate server object you provision.

A channel is simply an upstream conversation context (for example: a Slack thread, a Matrix room thread, or a browser chat tab) that maps to one stable conversationId.

The gateway uses that conversationId to keep one long-lived agent session.


How channels are represented internally

You have two integration options:

  1. Direct chat endpoints

    • POST /v1/chat
    • POST /v1/chat/stream
    • You provide conversationId directly.
  2. Adapter endpoints (recommended for Slack/Matrix/etc.)

    • POST /v1/adapters/chat
    • POST /v1/adapters/chat/stream
    • You provide channel/thread fields; the gateway derives conversationId for you.

Adapter ID format:

  • conversationId = source:workspaceId:channelId:threadId
  • adapterKey = source:workspaceId:channelId:threadId:userId

Field behavior on adapter routes:

  • source (optional, default: generic)
  • workspaceId (optional, default: default)
  • channelId (required)
  • threadId (optional, default: root)
  • userId (optional, default: anonymous)

Important constraints:

  • segment values must be strings
  • channelId must not be empty
  • segment values must not contain :

If your upstream IDs contain : (common in Matrix), sanitize/encode them before sending to adapter routes.


Do I need to "create" a channel first?

Usually: no.

A conversation/session is auto-created on first message. You can optionally pre-create one via:

  • POST /v1/conversations

But for most channel integrations, you just start sending messages to chat endpoints with stable IDs.


Requirements checklist (all channels)

  1. Gateway is running (RUN_MODE=gateway)
  2. Model/provider credentials are configured (PROVIDER, MODEL, API_KEY or provider-specific key)
  3. Your adapter/client can reach the gateway URL
  4. If GATEWAY_AUTH_TOKEN is set, send Authorization: Bearer <token>
  5. For browser apps on another origin, set GATEWAY_CORS_ORIGIN
  6. If you want restart-safe sessions, set SESSION_PERSIST=true

Web UI channel

Built-in UI

The built-in UI is available at GET / when:

  • GATEWAY_ENABLE_WEB_UI=true

How it works:

  • sends messages to /v1/chat/stream
  • stores conversationId in browser local storage
  • "New session" clears local conversationId and starts a new thread

Custom web app

If you build your own web frontend, call /v1/chat or /v1/chat/stream and choose your own ID format, e.g.:

  • web:<userId>:<chatId>

Example:

{
  "conversationId": "web:user-42:chat-main",
  "message": "Summarize the last response"
}

Slack channel integration

There is no built-in Slack bot in this repository; you run a small Slack adapter service.

Typical adapter needs:

  • Slack bot token + app configuration (events/interactions)
  • webhook/event receiver in your adapter
  • code that forwards messages to gateway and posts replies back to Slack

Recommended mapping from Slack event payload:

  • source: "slack"
  • workspaceId: Slack team/workspace ID (e.g. T123)
  • channelId: Slack channel ID (e.g. C456) required
  • threadId: Slack thread_ts (or fallback strategy)
  • userId: Slack user ID (e.g. U789)

Example request to gateway:

curl -N -X POST http://localhost:8787/v1/adapters/chat/stream \
  -H 'content-type: application/json' \
  -d '{
    "source": "slack",
    "workspaceId": "T123",
    "channelId": "C456",
    "threadId": "1712233.991",
    "userId": "U789",
    "message": "Please summarize this thread"
  }'

Threading strategy tip:

  • use a stable threadId per Slack thread to keep context isolated per thread
  • if you omit threadId, gateway uses root (all messages for that channel collapse into one thread context)

Matrix channel integration

There is no built-in Matrix bot in this repository; use a Matrix bot/bridge process as adapter.

Typical adapter needs:

  • Matrix bot account + access token
  • event listener for room messages
  • logic to send assistant output back into room/thread

Recommended mapping:

  • source: "matrix"
  • workspaceId: homeserver or deployment identifier (optional but useful)
  • channelId: Matrix room_id (required, encoded/sanitized for adapter route)
  • threadId: Matrix thread root event ID (encoded/sanitized, or omit for root)
  • userId: Matrix sender ID (encoded/sanitized)

Because adapter segments cannot contain :, Matrix IDs should be encoded in the adapter, e.g.:

const encodeSegment = (value: string) => encodeURIComponent(value);

Example payload (encoded values):

{
  "source": "matrix",
  "workspaceId": "matrix.org",
  "channelId": "%21roomid%3Amatrix.org",
  "threadId": "%24rootEventId%3Amatrix.org",
  "userId": "%40alice%3Amatrix.org",
  "message": "What did we decide in this thread?"
}

Alternative: call /v1/chat directly and provide your own conversationId format if you do not want per-segment adapter normalization.


Custom channel template (Discord, Telegram, email, etc.)

For any source, map your upstream identifiers into adapter fields and keep that mapping stable.

Minimal payload:

{
  "channelId": "your-channel-id",
  "message": "Hello"
}

Full payload template:

{
  "source": "custom",
  "workspaceId": "tenant-a",
  "channelId": "channel-123",
  "threadId": "thread-456",
  "userId": "user-789",
  "message": "Hello"
}

Best practices

  • Keep threadId stable per thread (do not generate random values per message)
  • Include userId for audit/telemetry (adapterKey) even though it does not change conversationId
  • Avoid : in any adapter segment value
  • Use streaming endpoints for better UX (/stream)
  • Persist sessions in production (SESSION_PERSIST=true)
  • Add retry/backoff logic in your adapter for network failures

  • Gateway internals/API: docs/gateway.md
  • Built-in browser UI: docs/web-ui.md