Documentation
¶
Overview ¶
Package channels defines the gateway's channel-adapter contract: a normalized inbound message and the interface each external surface (Telegram, Discord, Slack, …) implements. Adapters own their own connection to their platform; the gateway router (internal/gateway/server) maps inbound messages to agent work and posts replies back through Send. Keeping the contract this thin is what lets a new surface be "one more adapter" rather than a new subsystem.
Index ¶
- Constants
- func Chunk(s string, max int) []string
- func HostAllowed(host string, suffixes ...string) bool
- func Jitter(d time.Duration) time.Duration
- func KindForMime(mimeType, name string) string
- func ResolveSpoolID(dir, id string) (string, error)
- func SafeHTTPClient(timeout time.Duration) *http.Client
- type Attachment
- type Backoff
- type Channel
- type Inbound
- type Outbound
- type Sink
Constants ¶
const ( KindImage = "image" KindAudio = "audio" KindPDF = "pdf" KindFile = "file" )
Attachment kinds — a coarse content class, mapped by MIME type.
const MaxAttachmentBytes = 25 << 20 // 25 MiB
MaxAttachmentBytes caps a single downloaded attachment. Keeps a hostile or oversized upload from filling the disk; also comfortably above every transcription provider's audio cap.
Variables ¶
This section is empty.
Functions ¶
func Chunk ¶
Chunk splits s into pieces of at most max runes, preferring to break at a newline near the limit so code blocks and paragraphs aren't cut mid-line. It is the ONE splitter every adapter shares: Hermes and OpenClaw both grew message-too-long bugs precisely where a side path bypassed the shared chunker (or a second, divergent splitter stripped indentation differently), so all outbound text goes through here.
An empty string yields a single empty piece, and the split is loss-free: the concatenation of the result always equals the input (only the exact newline we break on moves to the end of a piece, never dropped).
func HostAllowed ¶ added in v0.15.0
HostAllowed reports whether host (or a subdomain of it) is in suffixes — for gating a privileged credential to first-party hosts only. Suffixes are matched on a dot boundary, so "evil-botframework.com" does not match ".botframework.com".
func Jitter ¶ added in v0.27.0
Jitter scales d by a random factor in [0.75, 1.25) so reconnecting gateways don't retry in lockstep against a recovering server, and no fixed period resonates with a server-side session TTL.
func KindForMime ¶ added in v0.15.0
KindForMime maps a MIME type (and filename, as a fallback) to a coarse attachment kind.
func ResolveSpoolID ¶ added in v0.15.0
ResolveSpoolID resolves a spool ID back to a path STRICTLY inside dir — the spool is the trust boundary, so an ID carrying separators, "..", or anything but a bare filename is rejected rather than resolved.
func SafeHTTPClient ¶ added in v0.15.0
SafeHTTPClient returns an HTTP client whose every connection (including those made following redirects) is refused if it would reach a non-public IP. Use it for ALL outbound fetches of URLs derived from inbound messages.
Types ¶
type Attachment ¶ added in v0.15.0
type Attachment struct {
Path string // absolute path inside the media spool
Kind string // image | audio | pdf | file
Mime string // as reported by the platform (best-effort)
Name string // original filename, display only
}
Attachment is one piece of inbound media, stored in the gateway media spool.
func SaveToSpool ¶ added in v0.15.0
SaveToSpool streams r into the media spool as <sha256>.<ext> (content-addressed: the same bytes land once) and returns the Attachment. The write is capped at MaxAttachmentBytes; an over-cap stream is an error, never a truncated file.
func (Attachment) ID ¶ added in v0.15.0
func (a Attachment) ID() string
ID returns the attachment's spool ID — the bare spool filename. IDs, not paths, are what cross process boundaries (durable inbox, job context); the consumer re-resolves an ID strictly inside the spool directory.
type Backoff ¶ added in v0.27.0
type Backoff struct {
// contains filtered or unexported fields
}
Backoff is a jittered exponential reconnect ladder. The zero value is not usable; construct with NewBackoff.
func NewBackoff ¶ added in v0.27.0
NewBackoff returns a ladder starting at floor and doubling up to max.
type Channel ¶
type Channel interface {
// Name is the adapter's stable identifier (matches Inbound.Channel).
Name() string
// Start owns the connection and hands each inbound message to the sink until
// ctx is cancelled, returning ctx.Err() on clean shutdown. It must NOT return
// on transient network errors — reconnect/back off instead, so a flaky
// platform never takes the gateway down. It acknowledges the provider only
// after Deliver returns nil.
Start(ctx context.Context, sink Sink) error
// Send posts a reply to the given conversation. Safe to call while Start runs.
Send(ctx context.Context, conversation string, msg Outbound) error
}
Channel is a bidirectional chat surface.
type Inbound ¶
type Inbound struct {
Channel string // adapter name, matches Channel.Name() ("telegram", …)
Conversation string // opaque per-channel chat/thread id the reply routes back to
Principal string // who sent it (id or @handle) — for authz + audit
Text string // the message body: the task handed to the agent
// MessageID is the platform's stable, unique id for this delivery (Telegram
// update_id, Discord message id, Slack event ts, GitHub delivery, WhatsApp
// wamid). The router dedups on (Channel, MessageID) so a redelivery — after a
// restart, reconnect, or provider retry — never re-runs as a fresh agent turn.
// Empty means the adapter couldn't supply one; the router then can't dedup it.
MessageID string
// Trusted marks an inbound whose SENDER is already cryptographically
// authenticated by the transport (a signature-verified webhook), so the
// router's per-channel allow-list doesn't apply. Chat messages leave this
// false and are gated by the allow-list; a signed GitHub delivery sets it.
Trusted bool
// Agent optionally forces the agent for this task. Honored only on Trusted
// inbounds (schedules, verified webhooks) — a chat sender picks agents via
// /agent, never through a message field.
Agent string
// IsDirect is true for a 1:1 direct message. A DM always triggers the agent;
// a message in a group/channel triggers only when the bot is addressed (see
// Mentioned) or the channel is configured to respond to all.
IsDirect bool
// Mentioned is true when the bot was explicitly addressed — @mentioned, or
// replied-to — so a group message meant for it triggers even without
// respond_to_all. Detected structurally by each adapter, never by substring.
Mentioned bool
// Attachments are media the sender included, already downloaded into the
// gateway's media spool by the adapter (see SaveToSpool). The spool is the
// trust boundary: downstream code addresses an attachment by its spool ID,
// never by an arbitrary path.
Attachments []Attachment
}
Inbound is a normalized message arriving from a channel.
type Outbound ¶
type Outbound struct {
Text string
// VoicePath optionally points at a synthesized speech rendition of Text
// (OGG/Opus in the media spool). Adapters that can send voice notes send it
// alongside/instead of the text; adapters that can't simply ignore it — Text
// is always present as the fallback.
VoicePath string
}
Outbound is a reply to post back to a conversation.
type Sink ¶
Sink receives inbound messages from an adapter. Deliver applies the gateway's gating and authorization and, for a message that should run, durably records it for processing. A nil return means the adapter may acknowledge the provider (the message was recorded, was a duplicate, or was intentionally dropped); a non-nil error means it was NOT durably recorded, so the adapter must NOT ack — the provider will redeliver. Acking only after a nil return is what makes delivery durable: a crash before the record simply causes a redelivery.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package discord is the gateway's Discord channel adapter.
|
Package discord is the gateway's Discord channel adapter. |
|
Package email is the gateway's email channel: a mailbox the agent answers.
|
Package email is the gateway's email channel: a mailbox the agent answers. |
|
Package matrix is the gateway's Matrix channel adapter.
|
Package matrix is the gateway's Matrix channel adapter. |
|
Package mattermost is the gateway's Mattermost channel adapter, aimed at self-hosted servers.
|
Package mattermost is the gateway's Mattermost channel adapter, aimed at self-hosted servers. |
|
Package msteams is the gateway's Microsoft Teams adapter over the Bot Framework: an Azure Bot registration POSTs activities to our webhook, and replies go back to the activity's serviceUrl as REST calls authenticated with an Azure AD client-credentials token.
|
Package msteams is the gateway's Microsoft Teams adapter over the Bot Framework: an Azure Bot registration POSTs activities to our webhook, and replies go back to the activity's serviceUrl as REST calls authenticated with an Azure AD client-credentials token. |
|
Package signal is the gateway's Signal channel adapter.
|
Package signal is the gateway's Signal channel adapter. |
|
Package slack is the gateway's Slack channel adapter.
|
Package slack is the gateway's Slack channel adapter. |
|
Package telegram is the gateway's Telegram channel adapter.
|
Package telegram is the gateway's Telegram channel adapter. |