| Without Caspian |
With Caspian |
|
```python
# slack_bolt app + socket handler
# discord.py client + intents + reconnect
# python-telegram-bot + webhook server
# smtplib/imap polling + threading logic
# 4 auth flows, 4 payload shapes,
# 4 retry/backoff paths, 4 dedup caches,
# per-channel identity bugs...
# ~1,500 lines before your agent
# says a single word
```
|
```python
cx.channels.add("email", via="self-host", ...)
cx.channels.add("telegram", via="self-host", bot_token=TG, webhook_url=URL)
cx.channels.add("slack", via="self-host", bot_token=SLACK, ...)
@cx.on_message({"overlap": "queue"})
def handle(thread, msg, ctx):
thread.post(agent(msg.text))
cx.run() # hosted
# or cx.listen("slack") / cx.handle(channel, body, headers)
```
|
> **Using a coding agent?** Point it at [`SKILL.md`](https://api.trycaspianai.com/SKILL.md) — it can do the entire integration for you.
## The problem
Every agent team ends up rebuilding the same four things — and none of them make the agent smarter.
**1. You own infrastructure you never wanted.** Writing the Slack bot is a weekend; owning it is forever. Session/auth desync, reconnect loops, silent connection failures, payload changes on every platform version bump. The pain isn't `send()` — sending is a solved call. The pain is the **lifecycle**. The largest OSS agent frameworks each maintain 25+ channel adapters in-tree and still spend 8–15% of their issue trackers on channel plumbing. (We measured 42 open-source agent projects before writing a line of this code.)
**2. Communication isn't part of your agent's decision-making.** With one-off, per-channel integrations, a developer decided at build time where and how the agent talks. The agent itself can't reason *"this deserves a quick Telegram ping now and an email summary afterwards"* — each channel is a separate bot with separate code and a separate identity. Communication stays hardcoded plumbing instead of becoming a capability the model can actually decide with.
**3. You maintain N identities for every one person.** The same human DMs your agent on Instagram today and emails it tomorrow. Now *your* database needs its own concept of "this is one person, one relationship, one running conversation" — who said what on which channel, and what should happen next in the flow. Every team rebuilds that continuity layer from scratch, per app, and it never stops needing care.
**4. A single-channel agent is a competitive disadvantage.** If a competing agent is reachable on five channels and yours on one, users go where they get answered. The open-source numbers show it: the agents people actually rely on are exactly the ones deployed across dozens of human channels — and that reach is exactly where their engineering time goes.
## Caspian's answer
**Channels are transports, not identities.** The agent is one program (`cx.app.rules` is inspectable data); every channel binds through the same adapter interface, and your handler code works against a normalized `Thread` / `Message` model. Messages arrive as kernel events regardless of transport, overlap policies (`queue` / `debounce` / `drop` / `parallel`) serialize concurrent chats, and `thread.post()` / `thread.reply()` always answer in the right place.
```mermaid
flowchart LR
S[Slack] --> A
D[Discord] --> A
T[Telegram] --> A
E[Email] --> A
W[WhatsApp · Messenger] --> A
X[X] --> A
A["channel adapters
**🧵 Declarative rules, one program**
`@cx.on_message({"channel": "telegram", "command": "help"})` — filters for channel, chat kind, command, overlap, and instant ack. Your bot is data: `cx.app.rules` is inspectable and testable offline.
|
**🔐 Webhook verification, always**
Slack signing secret, Meta `X-Hub-Signature-256`, Telegram secret header, X CRC, and signed email webhooks. Mismatches rejected.
|
**☁️ Hosted or self-host**
Gateway polling with `cx.run()`, or bring your own tokens and webhooks/sockets with `via="self-host"`. Same handler rules either way.
|
**🧪 Offline fakes for every channel**
Adapters consume each platform's *real* payload shapes — 650+ tests across Python + TypeScript, zero network in CI.
|
**⌨️ Typing, streaming, rich sends**
`thread.typing()`, `thread.stream()` (post once, edit as it writes), `thread.send_media()`, `thread.send_blocks()`, reactions, pins, forwards, and cold DMs.
|
**🤖 Model tools from the same surface**
`cx.tools(thread)` exposes the Command catalog (post, react, send-photo, …) with schemas derived from the kernel — same API your handlers use.
|
**🔌 Per-channel packs (TypeScript)**
Import `caspian-sdk/telegram`, `caspian-sdk/discord`, `caspian-sdk/slack`, and the rest for parse/plan/execute without pulling the whole facade.
|
**📡 Socket inbound (Discord, Slack)**
No public URL required — `cx.listen("discord")` or `cx.listen("slack")` over a held-open websocket (optional extras).
|
## Channels
Self-host adapters ship in the SDK for the channels below. Hosted mode covers any channel the gateway supports (including Bluesky, Instagram, and channels with no local adapter).
| Channel | Self-host (`via="self-host"`) | Hosted (`via="hosted"`) |
|---|:---:|:---:|
|