Documentation
¶
Overview ¶
Package channels provides message channel implementations for the Agents subsystem. Each channel (Slack, future: Telegram, Discord, etc.) satisfies the Channel interface and handles the full lifecycle:
- Listening for incoming messages
- Access control (channel-specific)
- Dispatching to the agent pool via a SendFunc
- Delivering responses back (reactions, threaded replies, SSE, etc.)
Wiring is done by *Registry — server constructs channels with cfg only, hands them to the registry, and the registry attaches dependencies via optional setter interfaces and fans out events.
Index ¶
- func EnsureChannel(db *gorm.DB, channelType string) error
- func GetChannelConfigMap(db *gorm.DB, channelType string) (map[string]string, error)
- func ProjectOverride(ctx context.Context) string
- func SetChannelConfigKey(db *gorm.DB, channelType, key, value string) error
- func SwitchProvider(layout agentconfig.Layout, pool SwitchPool, ...) error
- func WithProjectOverride(ctx context.Context, projectID string) context.Context
- type AgentEventReceiver
- type ApprovalReceiver
- type ApproveFn
- type ApproveFnSetter
- type Channel
- type ChannelEnsurer
- type ConfigSource
- type DBStore
- type HTTPHandlerProvider
- type HealthCheck
- type HealthChecker
- type IncomingMessage
- type LookupItem
- type LookupProvider
- type MetaResult
- type MultiHTTPHandlerProvider
- type OutgoingMessage
- type ProviderSwitchResult
- type PublicURLSetter
- type Registry
- func (r *Registry) Add(c Channel, src ConfigSource)
- func (r *Registry) ChannelByName(name string) Channel
- func (r *Registry) Channels() []Channel
- func (r *Registry) DispatchAgentEvent(sessionID string, ev event.AgentEvent)
- func (r *Registry) DispatchApprovalRequest(sessionID string, req gate.ApprovalRequest)
- func (r *Registry) DispatchApprovalResolved(sessionID, requestID, decision string)
- func (r *Registry) HTTPHandlers() map[string]http.Handler
- func (r *Registry) StartAll(ctx context.Context)
- func (r *Registry) StopAll()
- func (r *Registry) WatchConfigs(ctx context.Context, interval time.Duration)
- func (r *Registry) WithApproveFn(fn RegistryApproveFn) *Registry
- func (r *Registry) WithPublicURL(u string) *Registry
- func (r *Registry) WithSendFunc(fn SendFunc) *Registry
- func (r *Registry) WithSessionChecker(c SessionChecker) *Registry
- func (r *Registry) WithSessionStartHook(h SessionStartHook) *Registry
- type RegistryApproveFn
- type RestConfigStore
- type SendFunc
- type SendFuncSetter
- type SessionChecker
- type SessionCheckerSetter
- type SessionStartHook
- type SessionStartHookSetter
- type SlackConfigStore
- type SwitchPool
- type TelegramConfigStore
- type WorkflowActionProvider
- type WorkflowActionSpec
- type WorkflowSessionOriginator
- type WorkflowTriggerProvider
- type WorkflowTriggerSpec
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func EnsureChannel ¶
EnsureChannel creates a default agent_channels row for channelType if none exists. Safe to call at boot — idempotent.
func GetChannelConfigMap ¶
GetChannelConfigMap loads the JSON config for a channel type into a map. Returns an empty map when no row exists (not an error).
func ProjectOverride ¶ added in v0.14.21
ProjectOverride returns the per-request project id set via WithProjectOverride, or "" when none.
func SetChannelConfigKey ¶
SetChannelConfigKey updates a single key inside the channel's JSON config. Creates the row first if it doesn't exist.
func SwitchProvider ¶ added in v0.13.4
func SwitchProvider(layout agentconfig.Layout, pool SwitchPool, sessionID, agentName, tag, source string) error
SwitchProvider updates agents.json, records a system turn in conversation.jsonl, and kills the running agent so the next message spawns with the new provider. source is the transport label ("ui", "slack", etc.) written into conversation.jsonl.
Types ¶
type AgentEventReceiver ¶ added in v0.9.4
type AgentEventReceiver interface {
OnAgentEvent(sessionID string, ev event.AgentEvent)
}
AgentEventReceiver is fanned-out per agent event (TextDelta, Done, …).
type ApprovalReceiver ¶ added in v0.9.4
type ApprovalReceiver interface {
OnApprovalRequest(sessionID string, req gate.ApprovalRequest)
OnApprovalResolved(sessionID, requestID, decision string)
}
ApprovalReceiver is fanned-out for gate approval lifecycle.
type ApproveFn ¶
ApproveFn resolves a gate approval request originating from a channel. sessionID is the wick session, requestID is the gate request UUID, decision is one of the gate.Decision* constants. channelName is the originating channel name ("slack", "telegram", …) — passed for audit logging only. The registry wraps the user-supplied ApproveFn at Add time so each channel sees a 4-arg setter that already binds its name.
type ApproveFnSetter ¶ added in v0.9.4
type ApproveFnSetter interface{ SetApproveFn(ApproveFn) }
ApproveFnSetter receives the gate approval resolver.
type Channel ¶
type Channel interface {
Name() string
Start(ctx context.Context) error
Stop()
IsConfigured() bool
}
Channel is the minimal contract every transport must satisfy. The registry routes everything else (event fan-out, reload, http handlers) through the optional interfaces below — implementing those is opt-in.
type ChannelEnsurer ¶ added in v0.9.4
ChannelEnsurer guarantees a default agent_channels row exists for the given channel type. setup composers call it before loading config so first-boot operators see the channel listed in the UI even when the row is empty.
type ConfigSource ¶ added in v0.9.4
ConfigSource is per-channel hot-reload glue. Hash returns a stable fingerprint of the currently-applied config; the registry watcher compares against the previous hash on each tick and calls Reload when it changes. Implementations decide where the config lives — see ConfigStore for the abstraction the bundled sources read from.
type DBStore ¶ added in v0.9.4
DBStore satisfies SlackConfigStore + TelegramConfigStore by delegating to configMap so callers always receive decrypted plaintext values. Server wires one of these at boot so per-channel ConfigSource implementations don't need to import gorm directly. Configs is optional — when set, wick_cenc_ tokens in the JSON config are decrypted before being returned to callers.
func NewDBStore ¶ added in v0.9.4
NewDBStore returns a DBStore bound to db.
func (DBStore) EnsureChannel ¶ added in v0.9.4
EnsureChannel satisfies ChannelEnsurer.
func (DBStore) LoadRest ¶ added in v0.10.0
func (s DBStore) LoadRest() (agentconfig.RestChannelConfig, error)
LoadRest satisfies RestConfigStore.
func (DBStore) LoadSlack ¶ added in v0.9.4
func (s DBStore) LoadSlack() (agentconfig.SlackChannelConfig, string, error)
LoadSlack satisfies SlackConfigStore.
func (DBStore) LoadTelegram ¶ added in v0.9.4
func (s DBStore) LoadTelegram() (agentconfig.TelegramChannelConfig, error)
LoadTelegram satisfies TelegramConfigStore.
type HTTPHandlerProvider ¶ added in v0.9.4
HTTPHandlerProvider exposes a webhook handler the registry mounts on the public mux. Slack's HTTP-mode events use this; Telegram (long polling) does not.
type HealthCheck ¶ added in v0.10.0
type HealthCheck struct {
Name string `json:"name"`
OK bool `json:"ok"`
Error string `json:"error,omitempty"`
Detail string `json:"detail,omitempty"`
}
HealthCheck is one row of an integration self-test (e.g. "auth.test ok", "users.list missing scope"). OK=true means the upstream call succeeded with the result the operator expects; Detail is a short human-readable note (scope hint, count, etc.).
type HealthChecker ¶ added in v0.10.0
type HealthChecker interface {
HealthCheck() []HealthCheck
}
HealthChecker lets a channel expose a "Test Integration" probe that runs from the admin UI. Implementations should cover the API calls the channel relies on (auth, listing, search, write) so missing scopes surface before runtime.
type IncomingMessage ¶
type IncomingMessage struct {
SessionKey string // routing key
UserID string // sender identifier
GroupIDs []string // user groups (Slack only — access control)
Text string
Source string // "slack" | "ui" | "api"
Raw any // original payload from the channel
}
IncomingMessage is the canonical inbound shape produced by each channel. SessionKey is the routing key (thread_ts for Slack, UUID for UI/API).
type LookupItem ¶ added in v0.10.0
LookupItem is one row returned by a picker lookup. ID is the stable identifier stored in the config; Name is the human label shown to the operator.
type LookupProvider ¶ added in v0.10.0
type LookupProvider interface {
Lookup(source, query string) ([]LookupItem, error)
}
LookupProvider lets a channel back picker fields with a live search against its upstream. Source is the registered key from the wick tag (e.g. "slack.users"). Implementations should cap results and skip deleted/bot entries.
type MetaResult ¶
type MetaResult struct {
// IsMeta is true when the text is a wick meta-command and should NOT
// be forwarded to the agent subprocess.
IsMeta bool
// Cmd is the canonical command name (e.g. "dashboard", "reset").
Cmd string
// Arg is the optional argument following the command (e.g. agent name
// after "agent <name>").
Arg string
}
MetaResult is what ParseMeta returns.
func ParseMeta ¶
func ParseMeta(text string) MetaResult
ParseMeta checks whether text is one of the wick meta-commands intercepted before the pool. Commands are case-insensitive and may be prefixed with / or !. Returns IsMeta=false for regular user messages.
Supported commands (agents-design.md §10):
/agent <name> — switch active agent /reset — clear session context (next send starts fresh) /status — reply with current session/agent state /dashboard — reply with dashboard URL for this session /link — alias for /dashboard /log — reply with last N command-gate log lines
type MultiHTTPHandlerProvider ¶ added in v0.13.0
MultiHTTPHandlerProvider extends HTTPHandlerProvider for channels that need to register more than one HTTP route (e.g. Slack registers both the inbound event webhook and a local send-message proxy).
type OutgoingMessage ¶
type OutgoingMessage struct {
SessionKey string
Text string
// State carries the reaction lifecycle marker for Slack:
// "queued" | "running" | "done" | "blocked" | "error"
State string
}
OutgoingMessage is what the channel delivers back to the user after an agent turn completes. Slack uses Text + State for reaction lifecycle; UI uses SSE which bypasses this struct entirely.
type ProviderSwitchResult ¶ added in v0.13.4
type ProviderSwitchResult struct {
Tag string // provider type parsed from #tag, empty if no tag
Rest string // message text after the tag (may be empty)
HasTag bool
}
ProviderSwitchResult is returned by ParseProviderTag.
func ParseProviderTag ¶ added in v0.13.4
func ParseProviderTag(text string) ProviderSwitchResult
ParseProviderTag checks if text starts with #<provider> and splits it. Returns HasTag=false if text does not start with '#'.
type PublicURLSetter ¶ added in v0.9.4
type PublicURLSetter interface{ SetPublicURL(string) }
PublicURLSetter receives the public base URL for dashboard links. Slack uses this for /dashboard meta-command replies; Telegram doesn't.
type Registry ¶ added in v0.9.4
type Registry struct {
// contains filtered or unexported fields
}
Registry holds the shared dependency set every channel may need plus the channel list itself. Construct via NewRegistry, attach deps via the With* setters, then Add channels and Start.
func NewRegistry ¶ added in v0.9.4
func NewRegistry() *Registry
NewRegistry returns an empty registry. Use the With* methods to attach shared dependencies before calling Add.
func (*Registry) Add ¶ added in v0.9.4
func (r *Registry) Add(c Channel, src ConfigSource)
Add registers a channel and auto-wires every shared dependency the channel implements via its setter interface. Idempotent across dependencies — if a setter is missing the channel just skips that wire.
Pass an optional ConfigSource to enable hot-reload for the channel. Nil source = channel never reloaded by WatchConfigs.
func (*Registry) ChannelByName ¶ added in v0.10.0
ChannelByName returns the registered channel matching name, or nil.
func (*Registry) DispatchAgentEvent ¶ added in v0.9.4
func (r *Registry) DispatchAgentEvent(sessionID string, ev event.AgentEvent)
DispatchAgentEvent fans out one agent event to every channel that implements AgentEventReceiver. Channels filter by sessionID internally (events for sessions they didn't originate are ignored).
func (*Registry) DispatchApprovalRequest ¶ added in v0.9.4
func (r *Registry) DispatchApprovalRequest(sessionID string, req gate.ApprovalRequest)
DispatchApprovalRequest fans out an approval request to every channel that implements ApprovalReceiver. The channel decides whether the request belongs to one of its sessions (by checking its session table).
func (*Registry) DispatchApprovalResolved ¶ added in v0.9.4
DispatchApprovalResolved fans out an approval-resolved notification.
func (*Registry) HTTPHandlers ¶ added in v0.9.4
HTTPHandlers returns the webhook handlers exposed by channels that implement HTTPHandlerProvider. Caller mounts them on the public mux.
func (*Registry) StartAll ¶ added in v0.9.4
StartAll starts every configured channel in its own goroutine. Unconfigured channels are skipped with an info log so operators see why their channel isn't live. Errors from Start are logged but non-fatal — one bad channel doesn't take down the rest.
func (*Registry) StopAll ¶ added in v0.9.4
func (r *Registry) StopAll()
StopAll signals every channel to shut down. Safe to call before any Start has returned — channels' Stop is expected to be idempotent.
func (*Registry) WatchConfigs ¶ added in v0.9.4
WatchConfigs polls every registered ConfigSource at the given interval and triggers Reload when its Hash changes. Blocks until ctx is done. Run in its own goroutine.
func (*Registry) WithApproveFn ¶ added in v0.9.4
func (r *Registry) WithApproveFn(fn RegistryApproveFn) *Registry
WithApproveFn attaches the gate approval resolver. The signature includes channelName so the manager can record which transport posted the decision; Add binds each channel's name into a 4-arg ApproveFn before handing it to the channel's setter.
func (*Registry) WithPublicURL ¶ added in v0.9.4
WithPublicURL attaches the public base URL for dashboard links.
func (*Registry) WithSendFunc ¶ added in v0.9.4
WithSendFunc attaches the pool dispatch closure. Called once at boot.
func (*Registry) WithSessionChecker ¶ added in v0.9.4
func (r *Registry) WithSessionChecker(c SessionChecker) *Registry
WithSessionChecker attaches the session-exists probe.
func (*Registry) WithSessionStartHook ¶ added in v0.9.4
func (r *Registry) WithSessionStartHook(h SessionStartHook) *Registry
WithSessionStartHook attaches the new-session notifier.
type RegistryApproveFn ¶ added in v0.9.4
RegistryApproveFn is the multi-source variant the registry holds. It is wrapped per-channel into ApproveFn during Add so each channel keeps a 4-arg signature.
type RestConfigStore ¶ added in v0.10.0
type RestConfigStore interface {
LoadRest() (cfg agentconfig.RestChannelConfig, err error)
}
RestConfigStore mirrors SlackConfigStore for the OpenAI-compatible REST channel.
type SendFunc ¶
SendFunc is the signature the pool exposes for sending a user message into a session. Channels call this after passing access control and meta-command checks.
func WrapSendFunc ¶ added in v0.13.4
func WrapSendFunc(fn SendFunc, layout agentconfig.Layout, pool SwitchPool, replyFn func(sessionID, agentName, source, text string)) SendFunc
WrapSendFunc wraps a SendFunc to intercept #<provider> prefix. On switch-only message (no body), the send is skipped after confirmation. On switch+message, provider is switched then message is forwarded. On unknown provider, an error is surfaced via replyFn. replyFn, if non-nil, is called with the confirmation text so the channel (Slack, REST) can deliver it without forwarding to the provider.
type SendFuncSetter ¶ added in v0.9.4
type SendFuncSetter interface{ SetSendFunc(SendFunc) }
SendFuncSetter receives the pool dispatch closure.
type SessionChecker ¶ added in v0.9.4
SessionChecker reports whether a sessionID already exists. Implemented by *pool.Pool. Channels use it to decide whether the next inbound message starts a brand-new session — if so they prepend a one-time system turn (workspace/chat/user/link) so the agent's first reply is grounded.
type SessionCheckerSetter ¶ added in v0.9.4
type SessionCheckerSetter interface{ SetSessionChecker(SessionChecker) }
SessionCheckerSetter receives the session-exists probe.
type SessionStartHook ¶ added in v0.9.4
type SessionStartHook func(sessionID, source, ctxText string)
SessionStartHook fires once when a channel sees a brand-new session (no on-disk state yet). Optional — channels that don't track session origin (e.g. UI, API) never fire it. ctxText is the human-readable origin metadata composed for the agent's first turn.
type SessionStartHookSetter ¶ added in v0.9.4
type SessionStartHookSetter interface{ SetSessionStartHook(SessionStartHook) }
SessionStartHookSetter receives the new-session callback.
type SlackConfigStore ¶ added in v0.9.4
type SlackConfigStore interface {
LoadSlack() (cfg agentconfig.SlackChannelConfig, pubURL string, err error)
}
SlackConfigStore is the storage abstraction SlackConfigSource reads from. It hides the backend (DB, file, in-memory test fake) so the channels package can stay free of gorm imports inside the source implementations. Server wires a DB-backed implementor at boot.
type SwitchPool ¶ added in v0.13.4
type SwitchPool interface {
Kill(sessionID, agentName string) error
Send(ctx context.Context, sessionID, agentName, source, role, text string) error
}
SwitchPool is the subset of pool.Pool needed for provider switching.
type TelegramConfigStore ¶ added in v0.9.4
type TelegramConfigStore interface {
LoadTelegram() (cfg agentconfig.TelegramChannelConfig, err error)
}
TelegramConfigStore mirrors SlackConfigStore for Telegram.
type WorkflowActionProvider ¶ added in v0.13.0
type WorkflowActionProvider interface {
WorkflowActionSpecs() []WorkflowActionSpec
WorkflowSend(ctx context.Context, op string, args map[string]any) (any, error)
}
WorkflowActionProvider is implemented by channels that expose outbound operations (Send, react, open_modal, …) to workflow action nodes.
type WorkflowActionSpec ¶ added in v0.13.0
type WorkflowActionSpec struct {
ID string `json:"id"`
Description string `json:"description"`
Destructive bool `json:"destructive,omitempty"`
InputSchema map[string]any `json:"input_schema"`
OutputSchema map[string]any `json:"output_schema,omitempty"`
}
WorkflowActionSpec describes one outbound op a workflow channel-action node can invoke. Mirrors the input/output schema convention used by connector ops so the editor can render a typed args form.
type WorkflowSessionOriginator ¶ added in v0.13.0
type WorkflowSessionOriginator interface {
SupportsSession() bool
}
WorkflowSessionOriginator reports whether this channel can be the origin of a multi-turn agent session. UI/Slack/Telegram return true; stateless transports (REST, one-shot webhook) return false. Workflow validator rejects channel triggers that need a reply path on channels that don't support sessions.
type WorkflowTriggerProvider ¶ added in v0.13.0
type WorkflowTriggerProvider interface {
WorkflowTriggerSpecs() []WorkflowTriggerSpec
}
WorkflowTriggerProvider is implemented by channels that can fire workflow triggers (Slack, Telegram, …). Channels that only accept outbound calls (REST one-shot) skip this.
type WorkflowTriggerSpec ¶ added in v0.13.0
type WorkflowTriggerSpec struct {
Type string `json:"type"` // always "channel"
Events []string `json:"events"`
Description string `json:"description"`
MatchSchema map[string]any `json:"match_schema,omitempty"`
PayloadSchema map[string]any `json:"payload_schema,omitempty"`
}
WorkflowTriggerSpec describes one inbound event class the channel can fire as a workflow trigger. Surfaced via MCP for AI introspection + the editor's trigger-channel dropdown.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package rest implements an OpenAI Chat Completions compatible HTTP channel for the agents pool.
|
Package rest implements an OpenAI Chat Completions compatible HTTP channel for the agents pool. |
|
Package setup composes channel implementations into a registry.
|
Package setup composes channel implementations into a registry. |
|
Package slack — send.go: local agent-proxy send handler + reply helpers.
|
Package slack — send.go: local agent-proxy send handler + reply helpers. |
|
workflow
Slack picker resolvers — feed workflow_picker_resolve so AI authors editing a trigger match (channel_id whitelist, user whitelist) can get real IDs instead of guessing C123/U456.
|
Slack picker resolvers — feed workflow_picker_resolve so AI authors editing a trigger match (channel_id whitelist, user whitelist) can get real IDs instead of guessing C123/U456. |
|
Package telegram implements the Telegram transport for the agents channel registry.
|
Package telegram implements the Telegram transport for the agents channel registry. |