Documentation
¶
Index ¶
- Constants
- Variables
- func BuildMediaScope(channel, chatID, messageID string) string
- func ClassifyNetError(err error) error
- func ClassifySendError(statusCode int, rawErr error) error
- func GetRegisteredFactoryNames() []string
- func InitialAnimatedToolFeedbackContent(baseContent string) string
- func MaxToolFeedbackAnimationFrameLength() int
- func RegisterFactory(name string, f ChannelFactory)
- func RegisterSafeFactory[S any](channelType string, ...)
- func SplitByMarker(content string) []string
- func SplitMessage(content string, maxLen int) []string
- type BaseChannel
- func (c *BaseChannel) GetMediaStore() media.MediaStore
- func (c *BaseChannel) GetPlaceholderRecorder() PlaceholderRecorder
- func (c *BaseChannel) HandleInboundContext(ctx context.Context, deliveryChatID, content string, media []string, ...)
- func (c *BaseChannel) HandleMessageWithContext(ctx context.Context, deliveryChatID, content string, media []string, ...)
- func (c *BaseChannel) IsAllowed(senderID string) bool
- func (c *BaseChannel) IsAllowedSender(sender bus.SenderInfo) bool
- func (c *BaseChannel) IsRunning() bool
- func (c *BaseChannel) MaxMessageLength() int
- func (c *BaseChannel) Name() string
- func (c *BaseChannel) ReasoningChannelID() string
- func (c *BaseChannel) SetMediaStore(s media.MediaStore)
- func (c *BaseChannel) SetName(name string)
- func (c *BaseChannel) SetOwner(ch Channel)
- func (c *BaseChannel) SetPlaceholderRecorder(r PlaceholderRecorder)
- func (c *BaseChannel) SetRunning(running bool)
- func (c *BaseChannel) ShouldRespondInGroup(isMentioned bool, content string) (bool, string)
- type BaseChannelOption
- type Channel
- type ChannelFactory
- type ChannelLifecyclePayload
- type ChannelOutboundPayload
- type CommandRegistrarCapable
- type HealthChecker
- type Manager
- func (m *Manager) DismissToolFeedback(ctx context.Context, channelName, chatID string, ...)
- func (m *Manager) GetChannel(name string) (Channel, bool)
- func (m *Manager) GetEnabledChannels() []string
- func (m *Manager) GetStatus() map[string]any
- func (m *Manager) GetStreamer(ctx context.Context, channelName, chatID, sessionKey string) (bus.Streamer, bool)
- func (m *Manager) InvokeTypingStop(channel, chatID string)
- func (m *Manager) RecordPlaceholder(channel, chatID, placeholderID string)
- func (m *Manager) RecordReactionUndo(channel, chatID string, undo func())
- func (m *Manager) RecordTypingStop(channel, chatID string, stop func())
- func (m *Manager) RegisterChannel(name string, channel Channel)
- func (m *Manager) Reload(ctx context.Context, cfg *config.Config) error
- func (m *Manager) SendMedia(ctx context.Context, msg bus.OutboundMediaMessage) error
- func (m *Manager) SendMessage(ctx context.Context, msg bus.OutboundMessage) error
- func (m *Manager) SendPlaceholder(ctx context.Context, channel, chatID string) bool
- func (m *Manager) SendToChannel(ctx context.Context, channelName, chatID, content string) error
- func (m *Manager) SetMediaStore(store media.MediaStore)
- func (m *Manager) SetupHTTPServer(addr string, healthServer *health.Server)
- func (m *Manager) SetupHTTPServerListeners(listeners []net.Listener, addr string, healthServer *health.Server)
- func (m *Manager) StartAll(ctx context.Context) error
- func (m *Manager) StopAll(ctx context.Context) error
- func (m *Manager) UnregisterChannel(name string)
- type ManagerOption
- type MediaSender
- type MessageDeleter
- type MessageEditor
- type MessageEditorWithPayload
- type MessageLengthProvider
- type PlaceholderCapable
- type PlaceholderRecorder
- type ReactionCapable
- type Streamer
- type StreamingCapable
- type ToolFeedbackAnimator
- func (a *ToolFeedbackAnimator) Clear(chatID string)
- func (a *ToolFeedbackAnimator) Current(chatID string) (string, bool)
- func (a *ToolFeedbackAnimator) Record(chatID, messageID, content string)
- func (a *ToolFeedbackAnimator) StopAll()
- func (a *ToolFeedbackAnimator) Take(chatID string) (string, string, bool)
- func (a *ToolFeedbackAnimator) Update(ctx context.Context, chatID, content string) (string, bool, error)
- type TypingCapable
- type VoiceCapabilities
- type VoiceCapabilityProvider
- type WebhookHandler
Constants ¶
const MessageSplitMarker = "<|[SPLIT]|>"
MessageSplitMarker is the delimiter used to split a message into multiple outbound messages. When SplitOnMarker is enabled in config, the Manager will split messages on this marker and send each part as a separate message.
Variables ¶
var ( // ErrNotRunning indicates the channel is not running. // Manager will not retry. ErrNotRunning = errors.New("channel not running") // ErrRateLimit indicates the platform returned a rate-limit response (e.g. HTTP 429). // Manager will wait a fixed delay and retry. ErrRateLimit = errors.New("rate limited") // ErrTemporary indicates a transient failure (e.g. network timeout, 5xx). // Manager will use exponential backoff and retry. ErrTemporary = errors.New("temporary failure") // ErrSendFailed indicates a permanent failure (e.g. invalid chat ID, 4xx non-429). // Manager will not retry. ErrSendFailed = errors.New("send failed") )
Functions ¶
func BuildMediaScope ¶ added in v0.2.0
BuildMediaScope constructs a scope key for media lifecycle tracking.
func ClassifyNetError ¶ added in v0.2.0
ClassifyNetError wraps a network/timeout error as ErrTemporary.
func ClassifySendError ¶ added in v0.2.0
ClassifySendError wraps a raw error with the appropriate sentinel based on an HTTP status code. Channels that perform HTTP API calls should use this in their Send path.
func GetRegisteredFactoryNames ¶ added in v0.2.7
func GetRegisteredFactoryNames() []string
GetRegisteredFactoryNames returns a slice of all registered channel factory names.
func InitialAnimatedToolFeedbackContent ¶ added in v0.2.8
func MaxToolFeedbackAnimationFrameLength ¶ added in v0.2.8
func MaxToolFeedbackAnimationFrameLength() int
MaxToolFeedbackAnimationFrameLength returns the largest frame suffix length so callers can reserve room before sending messages to length-limited APIs.
func RegisterFactory ¶ added in v0.2.0
func RegisterFactory(name string, f ChannelFactory)
RegisterFactory registers a named channel factory. Called from subpackage init() functions.
func RegisterSafeFactory ¶ added in v0.2.7
func RegisterSafeFactory[S any]( channelType string, ctor func(bc *config.Channel, settings *S, bus *bus.MessageBus) (Channel, error), )
RegisterSafeFactory is a convenience wrapper that handles GetDecoded() error checking and type assertion, reducing boilerplate in channel init() functions.
Usage:
func init() {
channels.RegisterSafeFactory(config.ChannelTelegram,
func(bc *config.Channel, c *config.TelegramSettings, b *bus.MessageBus) (channels.Channel, error) {
return NewTelegramChannel(bc, c, b)
})
}
func SplitByMarker ¶ added in v0.2.5
SplitByMarker splits a message by the MessageSplitMarker and returns the parts. Empty parts (including from consecutive markers) are filtered out. If no marker is found, returns a single-element slice containing the original content.
func SplitMessage ¶ added in v0.2.0
SplitMessage splits long messages into chunks, preserving code block integrity. The maxLen parameter is measured in runes (Unicode characters), not bytes. The function reserves a buffer (10% of maxLen, min 50) to leave room for closing code blocks, but may extend to maxLen when needed. Call SplitMessage with the full text content and the maximum allowed length of a single message; it returns a slice of message chunks that each respect maxLen and avoid splitting fenced code blocks.
Types ¶
type BaseChannel ¶
type BaseChannel struct {
// contains filtered or unexported fields
}
func NewBaseChannel ¶
func NewBaseChannel( name string, config any, bus *bus.MessageBus, allowList []string, opts ...BaseChannelOption, ) *BaseChannel
func (*BaseChannel) GetMediaStore ¶ added in v0.2.0
func (c *BaseChannel) GetMediaStore() media.MediaStore
GetMediaStore returns the injected MediaStore (may be nil).
func (*BaseChannel) GetPlaceholderRecorder ¶ added in v0.2.0
func (c *BaseChannel) GetPlaceholderRecorder() PlaceholderRecorder
GetPlaceholderRecorder returns the injected PlaceholderRecorder (may be nil).
func (*BaseChannel) HandleInboundContext ¶ added in v0.2.7
func (c *BaseChannel) HandleInboundContext( ctx context.Context, deliveryChatID, content string, media []string, inboundCtx bus.InboundContext, senderOpts ...bus.SenderInfo, )
HandleInboundContext publishes a normalized inbound message using only the structured context.
func (*BaseChannel) HandleMessageWithContext ¶ added in v0.2.7
func (c *BaseChannel) HandleMessageWithContext( ctx context.Context, deliveryChatID, content string, media []string, inboundCtx bus.InboundContext, senderOpts ...bus.SenderInfo, )
func (*BaseChannel) IsAllowed ¶
func (c *BaseChannel) IsAllowed(senderID string) bool
func (*BaseChannel) IsAllowedSender ¶ added in v0.2.0
func (c *BaseChannel) IsAllowedSender(sender bus.SenderInfo) bool
IsAllowedSender checks whether a structured SenderInfo is permitted by the allow-list. It delegates to identity.MatchAllowed for each entry, providing unified matching across all legacy formats and the new canonical "platform:id" format.
func (*BaseChannel) IsRunning ¶
func (c *BaseChannel) IsRunning() bool
func (*BaseChannel) MaxMessageLength ¶ added in v0.2.0
func (c *BaseChannel) MaxMessageLength() int
MaxMessageLength returns the maximum message length (in runes) for this channel. A value of 0 means no limit.
func (*BaseChannel) Name ¶
func (c *BaseChannel) Name() string
func (*BaseChannel) ReasoningChannelID ¶ added in v0.2.0
func (c *BaseChannel) ReasoningChannelID() string
func (*BaseChannel) SetMediaStore ¶ added in v0.2.0
func (c *BaseChannel) SetMediaStore(s media.MediaStore)
SetMediaStore injects a MediaStore into the channel.
func (*BaseChannel) SetName ¶ added in v0.2.7
func (c *BaseChannel) SetName(name string)
SetName updates the channel name. Used by the manager after channel creation to ensure the name matches the config key (which may differ from the type).
func (*BaseChannel) SetOwner ¶ added in v0.2.0
func (c *BaseChannel) SetOwner(ch Channel)
SetOwner injects the concrete channel that embeds this BaseChannel. This allows HandleMessage to auto-trigger TypingCapable / ReactionCapable / PlaceholderCapable.
func (*BaseChannel) SetPlaceholderRecorder ¶ added in v0.2.0
func (c *BaseChannel) SetPlaceholderRecorder(r PlaceholderRecorder)
SetPlaceholderRecorder injects a PlaceholderRecorder into the channel.
func (*BaseChannel) SetRunning ¶ added in v0.2.0
func (c *BaseChannel) SetRunning(running bool)
func (*BaseChannel) ShouldRespondInGroup ¶ added in v0.2.0
func (c *BaseChannel) ShouldRespondInGroup(isMentioned bool, content string) (bool, string)
ShouldRespondInGroup determines whether the bot should respond in a group chat. Each channel is responsible for:
- Detecting isMentioned (platform-specific)
- Stripping bot mention from content (platform-specific)
- Calling this method to get the group response decision
Logic:
- If isMentioned → always respond
- If mention_only configured and not mentioned → ignore
- If prefixes configured → respond if content starts with any prefix (strip it)
- If prefixes configured but no match and not mentioned → ignore
- Otherwise (no group_trigger configured) → respond to all (permissive default)
type BaseChannelOption ¶ added in v0.2.0
type BaseChannelOption func(*BaseChannel)
BaseChannelOption is a functional option for configuring a BaseChannel.
func WithGroupTrigger ¶ added in v0.2.0
func WithGroupTrigger(gt config.GroupTriggerConfig) BaseChannelOption
WithGroupTrigger sets the group trigger configuration for a channel.
func WithMaxMessageLength ¶ added in v0.2.0
func WithMaxMessageLength(n int) BaseChannelOption
WithMaxMessageLength sets the maximum message length (in runes) for a channel. Messages exceeding this limit will be automatically split by the Manager. A value of 0 means no limit.
func WithReasoningChannelID ¶ added in v0.2.0
func WithReasoningChannelID(id string) BaseChannelOption
WithReasoningChannelID sets the reasoning channel ID where thoughts should be sent.
type ChannelFactory ¶ added in v0.2.0
type ChannelFactory func(channelName, channelType string, cfg *config.Config, bus *bus.MessageBus) (Channel, error)
ChannelFactory is a constructor function that creates a Channel from config and message bus. Each channel subpackage registers one or more factories via init(). channelName is the config map key for this channel instance (may differ from the channel type). channelType is the channel type string used to look up the Channel config.
type ChannelLifecyclePayload ¶ added in v0.2.9
type ChannelLifecyclePayload struct {
Type string `json:"type,omitempty"`
Error string `json:"error,omitempty"`
}
ChannelLifecyclePayload describes channel lifecycle runtime events.
type ChannelOutboundPayload ¶ added in v0.2.9
type ChannelOutboundPayload struct {
Media bool `json:"media,omitempty"`
ContentLen int `json:"content_len,omitempty"`
MessageIDs []string `json:"message_ids,omitempty"`
ReplyToMessageID string `json:"reply_to_message_id,omitempty"`
Error string `json:"error,omitempty"`
Retries int `json:"retries,omitempty"`
}
ChannelOutboundPayload describes channel outbound message runtime events.
type CommandRegistrarCapable ¶ added in v0.2.1
type CommandRegistrarCapable interface {
RegisterCommands(ctx context.Context, defs []commands.Definition) error
}
CommandRegistrarCapable is implemented by channels that can register command menus with their upstream platform (e.g. Telegram BotCommand). Channels that do not support platform-level command menus can ignore it.
type HealthChecker ¶ added in v0.2.0
type HealthChecker interface {
HealthPath() string
HealthHandler(w http.ResponseWriter, r *http.Request)
}
HealthChecker is an optional interface for channels that expose a health check endpoint on the shared HTTP server.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
func NewManager ¶
func NewManager( cfg *config.Config, messageBus *bus.MessageBus, store media.MediaStore, opts ...ManagerOption, ) (*Manager, error)
func (*Manager) DismissToolFeedback ¶ added in v0.2.8
func (m *Manager) DismissToolFeedback( ctx context.Context, channelName, chatID string, outboundCtx *bus.InboundContext, )
DismissToolFeedback clears any tracked tool feedback animation for the given channel/chat. This is called when a turn ends without a final response (e.g., ResponseHandled tools) to stop orphaned animation goroutines. outboundCtx carries topic/thread info for channels that use scoped tracker keys (e.g., Telegram forum topics); may be nil for non-topic channels.
func (*Manager) GetEnabledChannels ¶
func (*Manager) GetStreamer ¶ added in v0.2.4
func (m *Manager) GetStreamer(ctx context.Context, channelName, chatID, sessionKey string) (bus.Streamer, bool)
GetStreamer implements bus.StreamDelegate. It checks if the named channel supports streaming and returns a Streamer.
func (*Manager) InvokeTypingStop ¶ added in v0.2.4
InvokeTypingStop invokes the registered typing stop function for the given channel and chatID. It is safe to call even when no typing indicator is active (no-op). Used by the agent loop to stop typing when processing completes (success, error, or panic), regardless of whether an outbound message is published.
func (*Manager) RecordPlaceholder ¶ added in v0.2.0
RecordPlaceholder registers a placeholder message for later editing. Implements PlaceholderRecorder.
func (*Manager) RecordReactionUndo ¶ added in v0.2.0
RecordReactionUndo registers a reaction undo function for later invocation. Implements PlaceholderRecorder.
func (*Manager) RecordTypingStop ¶ added in v0.2.0
RecordTypingStop registers a typing stop function for later invocation. Implements PlaceholderRecorder.
func (*Manager) RegisterChannel ¶
func (*Manager) Reload ¶ added in v0.2.4
Reload updates the config reference without restarting channels. This is used when channel config hasn't changed but other parts of the config have.
func (*Manager) SendMedia ¶ added in v0.2.4
SendMedia sends outbound media synchronously through the channel worker's rate limiter and retry logic. It blocks until the media is delivered (or all retries are exhausted), which preserves ordering when later agent behavior depends on actual media delivery.
func (*Manager) SendMessage ¶ added in v0.2.2
SendMessage sends an outbound message synchronously through the channel worker's rate limiter and retry logic. It blocks until the message is delivered (or all retries are exhausted), which preserves ordering when a subsequent operation depends on the message having been sent.
func (*Manager) SendPlaceholder ¶ added in v0.2.2
SendPlaceholder sends a "Thinking…" placeholder for the given channel/chatID and records it for later editing. Returns true if a placeholder was sent.
func (*Manager) SendToChannel ¶
func (*Manager) SetMediaStore ¶ added in v0.2.9
func (m *Manager) SetMediaStore(store media.MediaStore)
SetMediaStore updates the store used by the manager and every channel that accepts media store injection. Gateway reload creates a fresh store, so keeping existing channels on the same store as the agent is required for inbound media refs to remain resolvable after reload.
func (*Manager) SetupHTTPServer ¶ added in v0.2.0
SetupHTTPServer creates a shared HTTP server with the given listen address. It registers health endpoints from the health server and discovers channels that implement WebhookHandler and/or HealthChecker to register their handlers.
func (*Manager) SetupHTTPServerListeners ¶ added in v0.2.7
func (m *Manager) SetupHTTPServerListeners(listeners []net.Listener, addr string, healthServer *health.Server)
SetupHTTPServerListeners creates a shared HTTP server on pre-opened listeners. When listeners is empty it falls back to Addr-based ListenAndServe behavior.
func (*Manager) UnregisterChannel ¶
type ManagerOption ¶ added in v0.2.9
type ManagerOption func(*Manager)
ManagerOption configures a channel Manager.
func WithRuntimeEvents ¶ added in v0.2.9
func WithRuntimeEvents(eventBus runtimeevents.Bus) ManagerOption
WithRuntimeEvents injects the runtime event bus used for channel observations.
type MediaSender ¶ added in v0.2.0
type MediaSender interface {
SendMedia(ctx context.Context, msg bus.OutboundMediaMessage) ([]string, error)
}
MediaSender is an optional interface for channels that can send media attachments (images, files, audio, video). Manager discovers channels implementing this interface via type assertion and routes OutboundMediaMessage to them.
type MessageDeleter ¶ added in v0.2.4
type MessageDeleter interface {
DeleteMessage(ctx context.Context, chatID string, messageID string) error
}
MessageDeleter — channels that can delete a message by ID.
type MessageEditor ¶ added in v0.2.0
type MessageEditor interface {
EditMessage(ctx context.Context, chatID string, messageID string, content string) error
}
MessageEditor — channels that can edit an existing message. messageID is always string; channels convert platform-specific types internally.
type MessageEditorWithPayload ¶ added in v0.2.9
type MessageEditorWithPayload interface {
EditMessageWithPayload(
ctx context.Context,
chatID string,
messageID string,
payload map[string]any,
) error
}
MessageEditorWithPayload extends MessageEditor for channels that can update structured message metadata in addition to plain text content.
type MessageLengthProvider ¶ added in v0.2.0
type MessageLengthProvider interface {
MaxMessageLength() int
}
MessageLengthProvider is an opt-in interface that channels implement to advertise their maximum message length. The Manager uses this via type assertion to decide whether to split outbound messages.
type PlaceholderCapable ¶ added in v0.2.0
type PlaceholderCapable interface {
SendPlaceholder(ctx context.Context, chatID string) (messageID string, err error)
}
PlaceholderCapable — channels that can send a placeholder message (e.g. "Thinking... 💭") that will later be edited to the actual response. The channel MUST also implement MessageEditor for the placeholder to be useful. SendPlaceholder returns the platform message ID of the placeholder so that Manager.preSend can later edit it via MessageEditor.EditMessage.
type PlaceholderRecorder ¶ added in v0.2.0
type PlaceholderRecorder interface {
RecordPlaceholder(channel, chatID, placeholderID string)
RecordTypingStop(channel, chatID string, stop func())
RecordReactionUndo(channel, chatID string, undo func())
}
PlaceholderRecorder is injected into channels by Manager. Channels call these methods on inbound to register typing/placeholder state. Manager uses the registered state on outbound to stop typing and edit placeholders.
type ReactionCapable ¶ added in v0.2.0
type ReactionCapable interface {
ReactToMessage(ctx context.Context, chatID, messageID string) (undo func(), err error)
}
ReactionCapable — channels that can add a reaction (e.g. 👀) to an inbound message. ReactToMessage adds a reaction and returns an undo function to remove it. The undo function MUST be idempotent and safe to call multiple times.
type Streamer ¶ added in v0.2.4
Streamer is defined in pkg/bus to avoid circular imports. This alias keeps channel implementations using channels.Streamer unchanged.
type StreamingCapable ¶ added in v0.2.4
type StreamingCapable interface {
BeginStream(ctx context.Context, chatID string) (Streamer, error)
}
StreamingCapable — channels that can show partial LLM output in real-time. The channel SHOULD gracefully degrade if the platform rejects streaming (e.g. Telegram bot without forum mode). In that case, Update becomes a no-op and Finalize still delivers the final message.
type ToolFeedbackAnimator ¶ added in v0.2.8
type ToolFeedbackAnimator struct {
// contains filtered or unexported fields
}
func NewToolFeedbackAnimator ¶ added in v0.2.8
func NewToolFeedbackAnimator( editFn func(ctx context.Context, chatID, messageID, content string) error, ) *ToolFeedbackAnimator
func (*ToolFeedbackAnimator) Clear ¶ added in v0.2.8
func (a *ToolFeedbackAnimator) Clear(chatID string)
func (*ToolFeedbackAnimator) Current ¶ added in v0.2.8
func (a *ToolFeedbackAnimator) Current(chatID string) (string, bool)
func (*ToolFeedbackAnimator) Record ¶ added in v0.2.8
func (a *ToolFeedbackAnimator) Record(chatID, messageID, content string)
func (*ToolFeedbackAnimator) StopAll ¶ added in v0.2.8
func (a *ToolFeedbackAnimator) StopAll()
func (*ToolFeedbackAnimator) Take ¶ added in v0.2.8
func (a *ToolFeedbackAnimator) Take(chatID string) (string, string, bool)
func (*ToolFeedbackAnimator) Update ¶ added in v0.2.8
func (a *ToolFeedbackAnimator) Update(ctx context.Context, chatID, content string) (string, bool, error)
Update edits an existing tracked feedback message. If the edit fails, the previous feedback state is restored so callers can retry without orphaning the old progress message.
type TypingCapable ¶ added in v0.2.0
type TypingCapable interface {
StartTyping(ctx context.Context, chatID string) (stop func(), err error)
}
TypingCapable — channels that can show a typing/thinking indicator. StartTyping begins the indicator and returns a stop function. The stop function MUST be idempotent and safe to call multiple times.
type VoiceCapabilities ¶ added in v0.2.5
VoiceCapabilities describes whether ASR (speech-to-text) and TTS (text-to-speech) are available for a channel under the current configuration.
func DetectVoiceCapabilities ¶ added in v0.2.5
func DetectVoiceCapabilities(channelName string, ch Channel, asrAvailable bool, ttsAvailable bool) VoiceCapabilities
DetectVoiceCapabilities returns ASR/TTS availability for a channel, gated by whether providers are configured.
type VoiceCapabilityProvider ¶ added in v0.2.5
type VoiceCapabilityProvider interface {
VoiceCapabilities() VoiceCapabilities
}
VoiceCapabilityProvider is an optional interface for channels that want to explicitly declare their ASR/TTS support.
type WebhookHandler ¶ added in v0.2.0
type WebhookHandler interface {
// WebhookPath returns the path to mount this handler on the shared server.
// Examples: "/webhook/line", "/webhook/wecom"
WebhookPath() string
http.Handler // ServeHTTP(w http.ResponseWriter, r *http.Request)
}
WebhookHandler is an optional interface for channels that receive messages via HTTP webhooks. Manager discovers channels implementing this interface and registers them on the shared HTTP server.