channel

package
v0.19.1 Latest Latest
Warning

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

Go to latest
Published: Apr 29, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package channel defines the public contract for channel plugins. Channel plugins are thin platform adapters (Telegram, QQ, Feishu, Weixin) that normalise incoming messages, delegate business logic to a MessageHandler, and render streamed responses back to the platform.

Index

Constants

View Source
const (
	PlatformTelegram = "telegram"
	PlatformQQ       = "qq"
	PlatformFeishu   = "feishu"
	PlatformWeixin   = "weixin"
	PlatformCLI      = "cli"
)

Platform identifiers for each messaging channel.

View Source
const WelcomeMessage = `` /* 1001-byte string literal not displayed */

WelcomeMessage is the shared welcome/help text for all channels. It explains both slash commands and natural-language shortcuts for channels without slash-command menus (like SMS or simple webhooks).

Variables

View Source
var ToolEmoji = map[string]string{
	"bash":    "⚡",
	"read":    "📖",
	"write":   "✏️",
	"edit":    "🔧",
	"search":  "🔍",
	"default": "🔧",
}

ToolEmoji maps known tool names to display emoji.

Functions

func CloneConfigMap

func CloneConfigMap(src map[string]any) map[string]any

func DecodePluginConfig

func DecodePluginConfig[T any](raw map[string]any, label string) (T, error)

func EmojiFor

func EmojiFor(tool string) string

EmojiFor returns the emoji for a tool name, falling back to the default.

func FileReceivedContent added in v0.17.0

func FileReceivedContent(fileName, assetsDir, savedPath string) []ai.ContentBlock

FileReceivedContent returns the standard content block telling the agent about a file that has been saved to disk, with a kreuzberg extraction hint. assetsDir is the host-side assets directory; savedPath is the host-side absolute path returned by SaveAsset. The hint uses a path relative to the user root (parent of assetsDir) so it resolves correctly inside the bwrap sandbox at /workspace.

func FormatAgentList

func FormatAgentList(agents []IndexedAgent, currentAgentID string) string

FormatAgentList builds a text-based agent list, marking the current agent.

func FormatDuration

func FormatDuration(d time.Duration) string

FormatDuration formats a duration as a human-friendly string.

func FormatModelList

func FormatModelList(models []IndexedModel, query string) string

FormatModelList builds a text-based model list with bullet points.

func FormatNumberedModelList

func FormatNumberedModelList(models []IndexedModel, query string) string

FormatNumberedModelList builds a text-based model list with numbered entries.

func HandleAgentCommand

func HandleAgentCommand(cfg AgentCommandHandler)

HandleAgentCommand runs the shared /agent flow for text-based channels.

func HandleModelCommand

func HandleModelCommand(cfg ModelCommandHandler)

HandleModelCommand runs the shared /model flow for text-based channels.

func NotificationAgentIDFromContext added in v0.10.0

func NotificationAgentIDFromContext(ctx context.Context) string

NotificationAgentIDFromContext returns the agent ID associated with a notification-producing tool call, if present.

func ParseCommandArgs

func ParseCommandArgs(text, cmd string) string

ParseCommandArgs extracts arguments after the command token. Example: ParseCommandArgs("/agent foo", "/agent") returns "foo".

func ParseModelArgs

func ParseModelArgs(args string) string

ParseModelArgs parses /model arguments as a query string. Returns empty string when no arguments are provided.

func ParseSlashCommand

func ParseSlashCommand(text string) (string, string)

ParseSlashCommand extracts a slash command and its arguments. The returned command is always lowercased.

func RenderToolRecord

func RenderToolRecord(rec ToolRecord) string

RenderToolRecord formats a single completed tool record.

func SplitMessage

func SplitMessage(text string, maxLen int) []string

SplitMessage splits text into chunks that fit within maxLen. It tries to split at newline boundaries and avoids cutting multi-byte UTF-8 characters.

func TextContent

func TextContent(text string) []ai.ContentBlock

TextContent wraps a plain string as a single-element []ai.ContentBlock.

func ToolLine

func ToolLine(t *ToolUseEvent) string

ToolLine returns a short status line for a tool-use event. Used by channels with simple tool display (no full tracker).

func Truncate

func Truncate(s string, maxLen int) string

Truncate shortens s to maxLen bytes, appending "..." if truncated. Cuts at a valid UTF-8 boundary to avoid producing invalid strings.

func WithNotificationAgentID added in v0.10.0

func WithNotificationAgentID(ctx context.Context, agentID string) context.Context

WithNotificationAgentID annotates tool execution contexts with the agent that produced a notification.

Types

type AgentCommandHandler

type AgentCommandHandler struct {
	Ctx         context.Context
	Incoming    IncomingMessage
	Args        string
	Reply       func(string)
	ListAgents  func(context.Context, IncomingMessage) ([]AgentInfo, string, error)
	SwitchAgent func(context.Context, IncomingMessage, string) error
	FormatList  func([]IndexedAgent, string) string
	OnSwitched  func(string)
}

AgentCommandHandler provides the callbacks needed to serve /agent in text UIs.

type AgentInfo

type AgentInfo struct {
	ID   string
	Name string
}

AgentInfo is agent metadata for display in channel UIs.

type AgentManager

type AgentManager interface {
	// ListAgents returns enabled agents the user can access and the current agent ID.
	ListAgents(ctx context.Context, msg IncomingMessage) ([]AgentInfo, string, error)

	// SwitchAgent switches the active agent for this chat context.
	SwitchAgent(ctx context.Context, msg IncomingMessage, agentSlug string) error
}

AgentManager provides agent listing and switching for channel plugins that support the /agent command.

type Channel

type Channel interface {
	// Name returns a unique identifier (e.g. "telegram", "qq").
	Name() string

	// Start begins listening for messages. Blocks until ctx is cancelled.
	Start(ctx context.Context) error

	// Stop gracefully shuts down the channel.
	Stop()

	// Notify sends a push notification to a target within this channel.
	Notify(ctx context.Context, n Notification) error
}

Channel is a messaging platform adapter.

type ChatStream

type ChatStream struct {
	Events    <-chan Event
	SessionID string
}

ChatStream holds the event channel and session metadata returned by HandleMessage.

type Event

type Event struct {
	Text    string
	Image   *ImageEvent
	File    *FileEvent
	ToolUse *ToolUseEvent
	Err     error
}

Event is a stream event from the agent, consumed by channel plugins to render responses on the platform.

type FeishuConfig

type FeishuConfig struct {
	InstanceID        string                 `json:"-"`
	AppID             string                 `json:"app_id"`
	AppSecret         string                 `json:"app_secret"`
	EncryptKey        string                 `json:"encrypt_key"`
	VerificationToken string                 `json:"verification_token"`
	GroupMode         string                 `json:"group_mode"`
	Groups            map[string]FeishuGroup `json:"groups"`
	EnableNotify      bool                   `json:"enable_notify"`
	TenantKey         string                 `json:"tenant_key"`
	AutoProvision     bool                   `json:"auto_provision"`
}

FeishuConfig is the persisted Feishu channel plugin configuration.

type FeishuGroup

type FeishuGroup struct {
	GroupMode    string   `json:"group_mode"`
	SystemPrompt string   `json:"system_prompt"`
	ToolAllow    []string `json:"tool_allow"`
	ToolDeny     []string `json:"tool_deny"`
}

FeishuGroup is a per-chat override in the persisted Feishu channel config.

type FileEvent added in v0.17.0

type FileEvent struct {
	Path string // absolute path on disk
	Name string // display filename (with extension)
}

FileEvent carries a local file path to send to the user.

type Handler

type Handler interface {
	MessageHandler
	ModelManager
	AgentManager
}

Handler combines message routing with model and agent management. Channel plugins typically need all three capabilities.

type ImageEvent

type ImageEvent struct {
	Data     string // base64 encoded
	MimeType string // e.g. "image/jpeg"
}

ImageEvent carries a base64-encoded image.

type IncomingMessage

type IncomingMessage struct {
	Platform   string   // "telegram", "qq", etc.
	ChannelID  string   // configured channel instance ID; defaults to Platform.
	SenderID   string   // preferred platform-specific user ID
	SenderIDs  []string // ordered candidate sender IDs, most stable first
	SenderName string   // display name
	ChatID     string   // group/channel ID (empty for DMs)
	IsGroup    bool
	Content    []ai.ContentBlock
}

IncomingMessage is the normalised input from any platform.

type IndexedAgent

type IndexedAgent struct {
	AgentInfo
	GlobalIdx int
}

IndexedAgent pairs an AgentInfo with its 1-based global index.

func IndexAgents

func IndexAgents(agents []AgentInfo) []IndexedAgent

IndexAgents wraps a full agent list with sequential 1-based indices.

type IndexedModel

type IndexedModel struct {
	ModelOption
	GlobalIdx int
}

IndexedModel pairs a ModelOption with its 1-based global index.

func FilterModels

func FilterModels(models []ModelOption, query string) []IndexedModel

FilterModels returns indexed models matching the query, preserving their 1-based global indices from the full list.

func IndexModels

func IndexModels(models []ModelOption) []IndexedModel

IndexModels wraps a full model list with sequential 1-based indices.

type MessageHandler

type MessageHandler interface {
	// HandleIncoming resolves the user once, tries command handling, and if the
	// command is not handled, streams a chat response.
	// Returns (commandResponse, handled, stream, err).
	// If handled is true, commandResponse contains the reply and stream is nil.
	// If handled is false, stream contains the chat response.
	HandleIncoming(ctx context.Context, msg IncomingMessage, command, args string) (string, bool, *ChatStream, error)
}

MessageHandler is the core coordinator interface injected into channel plugins. It owns user resolution, agent routing, session management, and command handling. The single HandleIncoming entry point resolves the user once, tries command handling, and falls through to chat streaming — eliminating double resolution.

type ModelCommandHandler

type ModelCommandHandler struct {
	Args                string
	Reply               func(string)
	ListModels          func() []ModelOption
	SwitchModel         func(provider, model string) error
	FormatList          func([]IndexedModel, string) string
	AllowIndexSelection bool
	OnSwitched          func(ModelOption)
}

ModelCommandHandler provides the callbacks needed to serve /model in text UIs.

type ModelManager

type ModelManager interface {
	// ListModels returns available models.
	ListModels() []ModelOption

	// SwitchModel switches the active model.
	SwitchModel(provider, model string) error
}

ModelManager provides model listing and switching for channel plugins that support the /model command.

type ModelOption

type ModelOption struct {
	Provider string
	Model    string
}

ModelOption represents a selectable provider/model combination.

func FindModelByName

func FindModelByName(models []ModelOption, name string) (ModelOption, bool)

FindModelByName looks up a model by "provider/model" name (case-insensitive). Returns the model and true if found, zero value and false otherwise.

type Notification

type Notification struct {
	Channel string // optional: route to a specific backend
	ChatID  string // target chat/channel within the backend
	AgentID string // optional: agent that produced the notification
	Text    string // markdown content
	Silent  bool   // send without notification sound
}

Notification is a push message to send to a chat.

type ProvisionRequest added in v0.14.0

type ProvisionRequest struct {
	Platform   string
	ExternalID string
	Name       string
	EmailHint  string
}

ProvisionRequest carries the information needed to auto-provision a channel user.

type Provisioner added in v0.14.0

type Provisioner interface {
	ProvisionUser(ctx context.Context, req ProvisionRequest) error
}

Provisioner is an optional capability that a Handler may implement. Channel plugins assert for this interface when they want to auto-provision users on first contact, without adding the method to every channel's Handler.

type QQConfig

type QQConfig struct {
	InstanceID   string `json:"-"`
	AppID        string `json:"app_id"`
	AppSecret    string `json:"app_secret"`
	GroupMode    string `json:"group_mode"`
	EnableNotify bool   `json:"enable_notify"`
}

QQConfig is the persisted QQ channel plugin configuration.

type TelegramConfig

type TelegramConfig struct {
	InstanceID   string `json:"-"`
	Token        string `json:"token"`
	ChannelID    string `json:"channel_id"`
	GroupMode    string `json:"group_mode"`
	EnableNotify bool   `json:"enable_notify"`
}

TelegramConfig is the persisted Telegram channel plugin configuration.

type ToolRecord

type ToolRecord struct {
	Tool     string
	Input    string
	Status   string // "done" or "error"
	Detail   string
	Duration time.Duration
}

ToolRecord holds a completed tool invocation for summary display.

type ToolTracker

type ToolTracker struct {
	History      []ToolRecord
	ActiveTool   string
	ActiveInput  string
	ActiveStart  time.Time
	DisplayUntil time.Time // minimum time to keep showing the last-finished tool

	// MinDisplayDuration controls how long a finished tool stays visible.
	// Zero means no minimum.
	MinDisplayDuration time.Duration
}

ToolTracker tracks active and completed tool invocations during streaming.

func (*ToolTracker) Finish

func (tt *ToolTracker) Finish(t *ToolUseEvent)

Finish records the active tool as completed.

func (*ToolTracker) Handle

func (tt *ToolTracker) Handle(t *ToolUseEvent) bool

Handle processes a tool event, returning true if a display refresh is needed.

func (*ToolTracker) HasHistory

func (tt *ToolTracker) HasHistory() bool

HasHistory returns true if any tools were tracked.

func (*ToolTracker) IsDisplaying

func (tt *ToolTracker) IsDisplaying() bool

IsDisplaying returns true if there is an active tool or the minimum display duration for the last finished tool has not yet elapsed.

func (*ToolTracker) Render

func (tt *ToolTracker) Render() string

Render builds the tool section for real-time streaming display, showing completed tools and the active tool with elapsed time.

func (*ToolTracker) RenderFinal

func (tt *ToolTracker) RenderFinal() string

RenderFinal builds a compact tool summary for the final message. Shows a one-liner with tool counts and total time, plus individual lines for any failed tool calls.

func (*ToolTracker) Start

func (tt *ToolTracker) Start(t *ToolUseEvent)

Start registers a new tool as running.

type ToolUseEvent

type ToolUseEvent struct {
	Tool   string // tool name, e.g. "bash", "read"
	Status string // "running", "done", "error"
	Input  string // short summary of the tool input
	Detail string // error detail or result summary
}

ToolUseEvent describes a tool invocation in progress or completed.

type UserRootResolver added in v0.17.0

type UserRootResolver interface {
	ResolveUserRoot(ctx context.Context, msg IncomingMessage) (string, error)
}

UserRootResolver is an optional capability that a Handler may implement. Channel plugins assert for this interface when they need the per-user writable root path (e.g. to store uploaded files) before calling HandleIncoming.

type WeixinConfig

type WeixinConfig struct {
	InstanceID   string `json:"-"`
	BotToken     string `json:"bot_token"`
	BaseURL      string `json:"base_url"`
	BotID        string `json:"bot_id"`
	UserID       string `json:"user_id"`
	EnableNotify bool   `json:"enable_notify"`
}

WeixinConfig is the persisted Weixin channel plugin configuration.

Jump to

Keyboard shortcuts

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