apps/monad/test/integration/channels/channel-conformance.test.ts (+ telegram normalization is a pure, tested
function normalizeTelegramMessage).
Shared contracts
Contracts this pass added: command lowercasing (
/NEW == /new), outbound chunking at
maxMessageChars, the group require-mention gate, and
the per-channel agent hint. We also grew from one channel (Telegram) to seventeen adapters
spanning every inbound style — long-poll (Telegram), dial-out WebSocket (Discord Gateway, Slack
Socket Mode, QQ, WhatsApp Web), raw TCP (IRC), inbound webhook (LINE, WhatsApp Business, Twilio, Feishu, WeCom, Teams,
Google Chat, iMessage, generic), IMAP polling (email), and child process (Signal).
Design decisions (where platform conventions differ, we pick a stance)
Deliberately NOT matched (out of scope / by design)
- Markdown: our Telegram adapter sends plain text (
markdown:false) to avoid MarkdownV2 parse failures. (We chose robustness over rich formatting for v1.) (Discord/Slack adapters set their ownmarkdowncapability.) - Progressive-edit throttle constants: we throttle edits at ~1.2s. Same shape, different constants from any given platform’s batch delays.
- Media passthrough:
media[]is captured-but-empty; attachments aren’t forwarded yet.
Per-platform review (16 adapters)
A review of every adapter confirmed the inbound-normalization, mention-gating (entity/ mentioned-array, not substring), reply-to-bot-as-implicit-mention, token caching, and signature schemes are aligned with each platform’s documented behavior. Dedup is intentionally core-side (ChannelService.seen), not per-adapter. Four issues were found and fixed:
- WeCom decrypt (correctness bug): WeCom pads PKCS7 to a 32-byte block (pad can exceed 16), which
Web Crypto’s AES-CBC rejects. Switched
decryptWecomtonode:crypto+setAutoPadding(false)with manual strip, and added thereceive_id === corpIdanti-spoof check. - IRC outbound injection (security): agent output is hostile —
sanitizeIrcTextstrips CR/LF + control chars andsanitizeIrcTargetvalidates the target so a reply can’t smuggle a raw IRC command. - Teams serviceUrl SSRF (security): without inbound JWT validation a forged activity could point
serviceUrlat an attacker host and leak the AAD token;isAllowedTeamsServiceUrlallowlists Bot Framework hosts before a serviceUrl is remembered. - Feishu signature (security): verify
SHA256(timestamp+nonce+encryptKey+body)on the raw body when an encrypt key is configured, and reject AES-encryptpayloads (disable encryption in the console) rather than mis-parsing them.
text_mention entity unhandled; Slack
thread-reply not treated as implicit mention; outbound length uses hard platform caps (the core
splitForLimit already splits at boundaries) rather than headroom constants.
WhatsApp linked-device self-chat is an intentional A3 exception: the adapter assigns every Monad
outbound a tracked native message ID and drops only those echoes. A fromMe message authored on
another linked device is accepted only when its chat JID matches the connected account, which makes
the account’s self-chat usable without treating messages sent to other contacts as inbound turns.
How to extend the conformance suite
- Derive the rule from the platform’s documented behavior, and express it as an INPUT → expected-OUTPUT pair.
- If it’s adapter-level (platform payload →
ChannelInbound), testnormalizeTelegramMessage(or your adapter’s pure normalizer) directly. - If it’s core-level (routing/keying/auth/render), drive
ChannelServicewith a mock adapter (seecoreHarnessin the test) orcreateRendererwith a capturing adapter.