channels

package
v0.32.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 5, 2026 License: MIT Imports: 14 Imported by: 0

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

View Source
const (
	KindImage = "image"
	KindAudio = "audio"
	KindPDF   = "pdf"
	KindFile  = "file"
)

Attachment kinds — a coarse content class, mapped by MIME type.

View Source
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

func Chunk(s string, max int) []string

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

func HostAllowed(host string, suffixes ...string) bool

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

func Jitter(d time.Duration) time.Duration

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

func KindForMime(mimeType, name string) string

KindForMime maps a MIME type (and filename, as a fallback) to a coarse attachment kind.

func ResolveSpoolID added in v0.15.0

func ResolveSpoolID(dir, id string) (string, error)

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

func SafeHTTPClient(timeout time.Duration) *http.Client

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

func SaveToSpool(dir string, r io.Reader, mimeType, name string) (Attachment, error)

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

func NewBackoff(floor, max time.Duration) *Backoff

NewBackoff returns a ladder starting at floor and doubling up to max.

func (*Backoff) Reset added in v0.27.0

func (b *Backoff) Reset()

Reset drops the ladder back to its floor — call it once the connection proves healthy (a frame/update actually arrived), not merely when a dial succeeded.

func (*Backoff) Sleep added in v0.27.0

func (b *Backoff) Sleep(ctx context.Context) error

Sleep waits the current (jittered) delay — or until ctx is cancelled, returning its error — then doubles the delay toward the cap.

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

type Sink interface {
	Deliver(ctx context.Context, inb Inbound) error
}

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL