channel

package
v0.49.3 Latest Latest
Warning

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

Go to latest
Published: Jun 17, 2026 License: AGPL-3.0 Imports: 23 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrAgentAccessDenied = errors.New("you don't have access to this agent, contact an admin")
View Source
var ParseCommandArgs = pkgchannel.ParseCommandArgs

Functions

func EncodeGroupOutboxEnvelope added in v0.43.0

func EncodeGroupOutboxEnvelope(mentions []pkgchannel.Mention) (string, error)

func FormatAgentList

func FormatAgentList(agents []config.Agent, currentAgentID string) string

func HandleAgentCommand

func HandleAgentCommand(ctx context.Context, ac *AgentCommander, rc *ResolvedChat, args string, reply func(string))

func HandleCommand

func HandleCommand(ctx context.Context, rc *ResolvedChat, text, senderID string) (string, bool)

HandleCommand processes common bot commands shared across all channels. /model and /agent are left to each channel because they need platform-specific UI.

func IntentToCommand

func IntentToCommand(intent Intent) string

func LoadConfig

func LoadConfig[T any](store config.Store, channelID string) *T

func ResolveAgent

func ResolveAgent(ctx context.Context, store config.Store, authStore channelAuthStore, engine *auth.PolicyEngine, identity ResolvedIdentity, chat ChatContext) (string, error)

func TryLinkCode

func TryLinkCode(ctx context.Context, store channelAuthStore, linkCodes *auth.LinkCodeStore, text, platform, senderID, senderName string) (string, bool)

func TryLinkCodeWithCandidates

func TryLinkCodeWithCandidates(ctx context.Context, store channelAuthStore, linkCodes *auth.LinkCodeStore, text, platform, senderID string, senderIDs []string, senderName string) (string, bool)

func ValidateGroupMembership added in v0.42.0

func ValidateGroupMembership(ctx context.Context, store config.Store, agentID, replyChannelID string) error

ValidateGroupMembership checks that a reply_channel_id belongs to the expected agent — prevents an agent from replying through another bot.

Types

type AgentCommander

type AgentCommander struct {
	// contains filtered or unexported fields
}

func NewAgentCommander

func NewAgentCommander(store config.Store, users auth.UserStore) *AgentCommander

func (*AgentCommander) List

func (ac *AgentCommander) List(ctx context.Context) ([]config.Agent, error)

func (*AgentCommander) ListForChat

func (ac *AgentCommander) ListForChat(ctx context.Context, chat ChatContext) ([]config.Agent, error)

func (*AgentCommander) Switch

func (ac *AgentCommander) Switch(ctx context.Context, user auth.User, chat ChatContext, agentSlug string) error

type Arbiter added in v0.42.0

type Arbiter struct {
	// contains filtered or unexported fields
}

Arbiter decides which agents should respond to a group message. v1 is purely rule-based (no LLM calls):

  • @mentioned agent → always respond (bypass rules)
  • no mention → channel default agent responds
  • agent-origin messages → never trigger other agents (except explicit @handoff)

func NewArbiter added in v0.42.0

func NewArbiter(cfg ArbiterConfig) *Arbiter

NewArbiter creates a rule-based arbiter.

func (*Arbiter) Decide added in v0.42.0

func (a *Arbiter) Decide(_ context.Context, groupID string, mentions []pkgchannel.Mention, groupMembers []GroupMember, channelAgentID string, opts ...DecideOptions) ArbiterDecision

Decide determines which agents should respond to a human message in a group. mentionedAgents are the agent IDs resolved from @mentions. groupMembers are all agents currently in the group. channelAgentID is the channel's default agent (fallback when no mention).

type ArbiterConfig added in v0.42.0

type ArbiterConfig struct {
	// MaxRepliesPerTrigger caps the number of agent replies a single human
	// message can produce. 0 means use the default (1).
	MaxRepliesPerTrigger int

	// DebounceWindow is the minimum time between two dispatch triggers for
	// the same group. Messages arriving within the window are silently skipped.
	// 0 means no debounce.
	DebounceWindow time.Duration
}

ArbiterConfig controls the group arbiter's behavior.

type ArbiterDecision added in v0.42.0

type ArbiterDecision struct {
	RespondingAgents []string // agent IDs that should respond (in order)
	Debounced        bool     // true = message was suppressed by debounce window
}

ArbiterDecision is the outcome of a Decide call.

type BotIdentityRegistry added in v0.42.0

type BotIdentityRegistry struct {
	// contains filtered or unexported fields
}

BotIdentityRegistry is an in-memory map from a bot's platform identity (e.g., Telegram username) to the channel instance that owns that bot. Adapters populate it at startup; the group dispatcher reads it to resolve @-mentions to Stella agents.

func NewBotIdentityRegistry added in v0.42.0

func NewBotIdentityRegistry() *BotIdentityRegistry

NewBotIdentityRegistry returns an empty registry.

func (*BotIdentityRegistry) ChannelIDForBot added in v0.42.0

func (r *BotIdentityRegistry) ChannelIDForBot(platform, platformBotID string) (string, bool)

ChannelIDForBot returns the channel that owns the bot identified by (platform, platformBotID). Returns ("", false) if unknown.

func (*BotIdentityRegistry) Register added in v0.42.0

func (r *BotIdentityRegistry) Register(platform, platformBotID, channelID string)

Register records that the given platform bot identity belongs to channelID.

type ChatContext

type ChatContext struct {
	Platform  string
	ChannelID string
	ChatID    string
	IsGroup   bool
}

type Coordinator

type Coordinator struct {
	// contains filtered or unexported fields
}

func NewCoordinator

func NewCoordinator(
	pm interface {
		agent.ServiceManager
		userInvalidator
	},
	store config.Store,
	listFn func() []pkgchannel.ModelOption,
	switchFn func(provider, model string) error,
	opts ...CoordinatorOption,
) *Coordinator

NewCoordinator creates a Coordinator that satisfies pkgchannel.Handler. pm must implement both agent.ServiceManager (for routing) and userInvalidator (for /config secret updates). *agent.PoolManager satisfies both.

func (*Coordinator) EnsurePlatformGroupMember added in v0.43.1

func (c *Coordinator) EnsurePlatformGroupMember(ctx context.Context, platform, platformGroupID, channelID string) error

EnsurePlatformGroupMember resolves the internal group ID for a platform group and registers the channel's agent as a member. Safe to call repeatedly.

func (*Coordinator) HandleIncoming

func (c *Coordinator) HandleIncoming(ctx context.Context, msg pkgchannel.IncomingMessage, command, args string) (string, bool, *pkgchannel.ChatStream, error)

HandleIncoming resolves the user once, tries command handling, and if the command is not handled, streams a chat response. This avoids double resolution when a plugin needs to try commands before messaging.

func (*Coordinator) ListAgents

ListAgents returns enabled agents the user can access and the current agent ID.

func (*Coordinator) ListModels

func (c *Coordinator) ListModels() []pkgchannel.ModelOption

ListModels returns available models.

func (*Coordinator) ProvisionUser

func (c *Coordinator) ProvisionUser(ctx context.Context, req pkgchannel.ProvisionRequest) error

ProvisionUser checks whether a channel identity exists for the sender. Returns an error if the identity is not found — the user must first log in via OIDC and link their channel account.

func (*Coordinator) RegisterBotIdentity added in v0.42.0

func (c *Coordinator) RegisterBotIdentity(platform, platformBotID, channelID string)

RegisterBotIdentity records a bot's platform identity for mention resolution. Implements pkgchannel.BotRegistrar.

func (*Coordinator) RegisterGroupPublisher added in v0.43.0

func (c *Coordinator) RegisterGroupPublisher(channelID string, publisher GroupPublisher)

func (*Coordinator) RemovePlatformGroupMember added in v0.43.1

func (c *Coordinator) RemovePlatformGroupMember(ctx context.Context, platform, platformGroupID, channelID string) error

RemovePlatformGroupMember removes the channel's agent from a platform group.

func (*Coordinator) ResolveUserRoot

func (c *Coordinator) ResolveUserRoot(ctx context.Context, msg pkgchannel.IncomingMessage) (string, error)

ResolveUserRoot resolves the per-user writable root for the sender in msg. It performs the same user+agent resolution as HandleIncoming but stops before starting a session, so it is cheap and safe to call before file downloads. For group sessions, returns the group workspace instead of a per-user one.

func (*Coordinator) SetGroupDispatcher added in v0.43.0

func (c *Coordinator) SetGroupDispatcher(dispatcher *GroupDispatcher)

func (*Coordinator) SwitchAgent

func (c *Coordinator) SwitchAgent(ctx context.Context, msg pkgchannel.IncomingMessage, agentSlug string) error

SwitchAgent switches the active agent for the chat context.

func (*Coordinator) SwitchModel

func (c *Coordinator) SwitchModel(provider, model string) error

SwitchModel switches the active model.

type CoordinatorOption

type CoordinatorOption func(*Coordinator)

CoordinatorOption configures the Coordinator.

func WithArbiter added in v0.42.0

func WithArbiter(a *Arbiter) CoordinatorOption

WithArbiter configures the group arbiter for deciding which agents respond.

func WithBotRegistry added in v0.42.0

func WithBotRegistry(reg *BotIdentityRegistry) CoordinatorOption

WithBotRegistry enables bot identity resolution for @mention → agent routing.

func WithCoordinatorAuth

func WithCoordinatorAuth(store channelAuthStore, engine *auth.PolicyEngine, linkCodes *auth.LinkCodeStore) CoordinatorOption

WithCoordinatorAuth configures the coordinator with auth support.

func WithDB added in v0.43.1

func WithDB(db *sql.DB) CoordinatorOption

WithDB gives the coordinator direct DB access for group member management.

func WithEventLog added in v0.42.0

func WithEventLog(el *eventlog.Store) CoordinatorOption

WithEventLog enables group event log append (dedup + canonical ordering).

func WithGroupDispatcher added in v0.43.0

func WithGroupDispatcher(dispatcher *GroupDispatcher) CoordinatorOption

WithGroupDispatcher configures the durable group dispatcher wake path.

func WithGroupMemberLister added in v0.42.0

func WithGroupMemberLister(lister GroupMemberLister) CoordinatorOption

WithGroupMemberLister enables group membership queries for mention resolution.

func WithGroupResolver added in v0.42.0

func WithGroupResolver(gr GroupResolver) CoordinatorOption

WithGroupResolver enables group session identity resolution (D0/D9).

func WithIntentClassifier

func WithIntentClassifier(classifier IntentClassifier) CoordinatorOption

func WithPublisherRegistry added in v0.43.0

func WithPublisherRegistry(reg *PublisherRegistry) CoordinatorOption

WithPublisherRegistry configures cross-channel group response publishers.

func WithSemanticGroupArbiter added in v0.43.0

func WithSemanticGroupArbiter(a SemanticGroupArbiter) CoordinatorOption

WithSemanticGroupArbiter configures semantic routing for no-mention group messages. When set, no-mention messages are classified instead of using the all-members fallback; when unset, no-mention behavior is unchanged.

func WithVaultRecipient

func WithVaultRecipient(r *age.X25519Recipient) CoordinatorOption

WithVaultRecipient sets the master age recipient so channel-provisioned users get age keys at creation time.

func WithVaultService

func WithVaultService(svc *vault.Service) CoordinatorOption

WithVaultService configures the coordinator with vault secret management.

type DecideOptions added in v0.43.0

type DecideOptions struct {
	// AllMembersFallback: when true, if there is no @mention and no
	// channelAgentID, return all group members instead of empty.
	AllMembersFallback bool
	// DisableDebounce skips the in-memory fallback debounce for durable dispatch
	// replays. Mention routing already bypasses debounce.
	DisableDebounce bool
}

DecideOptions controls per-call arbiter behavior.

type FuncGroupMemberLister added in v0.42.0

type FuncGroupMemberLister func(ctx context.Context, groupID string) ([]GroupMember, error)

FuncGroupMemberLister adapts a function to GroupMemberLister.

func (FuncGroupMemberLister) ListGroupMembers added in v0.42.0

func (f FuncGroupMemberLister) ListGroupMembers(ctx context.Context, groupID string) ([]GroupMember, error)

type GroupDispatcher added in v0.43.0

type GroupDispatcher struct {
	// contains filtered or unexported fields
}

GroupDispatcher materializes durable group response decisions and executes one selected-agent dispatch at a time. Ingest owns facts; this owns work.

func NewGroupDispatcher added in v0.43.0

func NewGroupDispatcher(db *sql.DB, coord *Coordinator, publishers *PublisherRegistry) *GroupDispatcher

func (*GroupDispatcher) DispatchSync added in v0.43.0

func (d *GroupDispatcher) DispatchSync(ctx context.Context, outbox sqlc.CtxGroupOutbox, publisherOverride GroupPublisher) error

func (*GroupDispatcher) ExecuteDispatch added in v0.43.0

func (d *GroupDispatcher) ExecuteDispatch(ctx context.Context, row sqlc.CtxGroupDispatch, publisherOverride GroupPublisher) error

func (*GroupDispatcher) ProcessOutbox added in v0.43.0

func (d *GroupDispatcher) ProcessOutbox(ctx context.Context, outbox sqlc.CtxGroupOutbox) error

func (*GroupDispatcher) Run added in v0.43.0

func (d *GroupDispatcher) Run(ctx context.Context) error

func (*GroupDispatcher) Wake added in v0.43.0

func (d *GroupDispatcher) Wake()

type GroupMember added in v0.42.0

type GroupMember struct {
	AgentID        string
	ReplyChannelID string
}

GroupMember represents a bot's membership in a group chat.

type GroupMemberLister added in v0.42.0

type GroupMemberLister interface {
	ListGroupMembers(ctx context.Context, groupID string) ([]GroupMember, error)
}

GroupMemberLister returns the agents that belong to a group.

type GroupOutboxEnvelope added in v0.43.0

type GroupOutboxEnvelope struct {
	Mentions []pkgchannel.Mention `json:"mentions,omitempty"`
}

GroupOutboxEnvelope stores dispatch metadata that must be decided at ingest time, while the group_id and membership view are available in the append transaction.

func DecodeGroupOutboxEnvelope added in v0.43.0

func DecodeGroupOutboxEnvelope(raw string) (GroupOutboxEnvelope, error)

type GroupPublishRequest added in v0.43.0

type GroupPublishRequest struct {
	GroupID          string
	AgentID          string
	AgentName        string
	ReplyChannelID   string
	Platform         string
	PlatformGroupID  string
	PlatformThreadID string
	ReplyTo          string
	Stream           *pkgchannel.ChatStream
}

type GroupPublisher added in v0.43.0

type GroupPublisher interface {
	Publish(ctx context.Context, req GroupPublishRequest) error
}

GroupPublisher renders and sends an agent response stream to one concrete group egress. It must not resolve sessions, call agents, or write event-log rows; the dispatcher owns those cross-platform concerns. Returning nil means the platform API confirmed delivery for platform publishers, or the stream was fully consumed for Web publishers (client write errors do not make Web publish fail; the event log is the durable delivery channel).

func NoopGroupPublisher added in v0.43.0

func NoopGroupPublisher() GroupPublisher

type GroupResolver added in v0.42.0

type GroupResolver interface {
	ResolveGroupID(ctx context.Context, platform, platformGroupID, platformThreadID string) (string, error)
}

GroupResolver resolves the canonical group_id for a physical group identity.

type IndexedAgent

type IndexedAgent struct {
	config.Agent
	GlobalIdx int
}

func IndexAgents

func IndexAgents(agents []config.Agent) []IndexedAgent

type Intent

type Intent string
const (
	IntentHelp    Intent = "help"
	IntentNew     Intent = "new"
	IntentAbort   Intent = "abort"
	IntentCompact Intent = "compact"
	IntentNone    Intent = "none"
)

type IntentClassifier

type IntentClassifier interface {
	Classify(ctx context.Context, agentID string, content []ai.ContentBlock) Intent
}

type LLMIntentClassifier

type LLMIntentClassifier struct {
	// contains filtered or unexported fields
}

func NewLLMIntentClassifier

func NewLLMIntentClassifier(loadSnapshot SnapshotLoader, buildStream StreamFuncBuilder) *LLMIntentClassifier

func (*LLMIntentClassifier) Classify

func (c *LLMIntentClassifier) Classify(ctx context.Context, agentID string, content []ai.ContentBlock) Intent

type LLMSemanticGroupArbiter added in v0.43.0

type LLMSemanticGroupArbiter struct {
	// contains filtered or unexported fields
}

func NewLLMSemanticGroupArbiter added in v0.43.0

func NewLLMSemanticGroupArbiter(loadSnapshot SnapshotLoader, buildStream StreamFuncBuilder) *LLMSemanticGroupArbiter

func (*LLMSemanticGroupArbiter) Decide added in v0.43.0

type PublisherRegistry added in v0.43.0

type PublisherRegistry struct {
	// contains filtered or unexported fields
}

func NewPublisherRegistry added in v0.43.0

func NewPublisherRegistry() *PublisherRegistry

func (*PublisherRegistry) Get added in v0.43.0

func (r *PublisherRegistry) Get(channelID string) (GroupPublisher, bool)

func (*PublisherRegistry) Register added in v0.43.0

func (r *PublisherRegistry) Register(channelID string, publisher GroupPublisher)

type ResolvedChat

type ResolvedChat struct {
	Service    *agent.Service
	User       auth.User
	AgentID    string
	SessionKey string
	Channel    session.Channel
	ChatCtx    ChatContext
	GroupID    string // non-empty for group sessions; used as session scope (D9)
	// CurrentSpeaker is the per-turn group speaker (personalization target only).
	// Canonical source for group personalization; runtime/session scope still
	// flows from GroupID / sessionUserID(), never from User.ID.
	CurrentSpeaker memory.CurrentSpeaker
}

func Resolve

func Resolve(ctx context.Context, sm agent.ServiceManager, store config.Store, authStore channelAuthStore, engine *auth.PolicyEngine, platform, senderID, senderName, chatID string, isGroup bool) (*ResolvedChat, error)

func ResolveWithChannel

func ResolveWithChannel(ctx context.Context, sm agent.ServiceManager, store config.Store, authStore channelAuthStore, engine *auth.PolicyEngine, groupResolver GroupResolver, platform, channelID, senderID string, senderIDs []string, senderName, chatID, threadID string, isGroup bool) (*ResolvedChat, error)

func (*ResolvedChat) Chat

func (rc *ResolvedChat) Chat(ctx context.Context, message agent.MessageContent) (<-chan agent.Event, string, error)

func (*ResolvedChat) CompactSession

func (rc *ResolvedChat) CompactSession(ctx context.Context) (string, error)

func (*ResolvedChat) ResolveSession

func (rc *ResolvedChat) ResolveSession(ctx context.Context) (agent.SessionInfo, error)

func (*ResolvedChat) UserID

func (rc *ResolvedChat) UserID() string

type ResolvedIdentity

type ResolvedIdentity struct {
	User auth.User
}

func ResolveUser

func ResolveUser(ctx context.Context, store channelAuthStore, platform, externalID string) (ResolvedIdentity, error)

func ResolveUserCandidates

func ResolveUserCandidates(ctx context.Context, store channelAuthStore, platform string, externalIDs []string) (ResolvedIdentity, identityMatch, error)

type SemanticGroupArbiter added in v0.43.0

type SemanticGroupArbiter interface {
	Decide(ctx context.Context, req SemanticGroupRequest) SemanticGroupDecision
}

SemanticGroupArbiter decides which agents (if any) should answer a no-mention group message. A nil decision (ShouldReply=false) means "stay silent" — the only safe fallback for an ambiguous classifier.

type SemanticGroupContextMessage added in v0.43.0

type SemanticGroupContextMessage struct {
	ActorType string
	ActorID   string
	Content   string
}

SemanticGroupContextMessage is one prior group message, oldest→newest.

type SemanticGroupDecision added in v0.43.0

type SemanticGroupDecision struct {
	ShouldReply      bool
	RespondingAgents []string
	Reason           string
}

SemanticGroupDecision is the strict outcome. RespondingAgents is always a subset of request member IDs, deduped and capped; empty when ShouldReply.

type SemanticGroupMember added in v0.43.0

type SemanticGroupMember struct {
	AgentID        string
	Name           string
	Scope          string
	CreatorID      string
	Summary        string // bounded public routing summary
	ReplyChannelID string
}

SemanticGroupMember is one candidate agent for a no-mention group decision. Scope/CreatorID drive routing-agent eligibility (whose model_fast and creds classify the message); they are credential/billing signals, not access checks.

type SemanticGroupRequest added in v0.43.0

type SemanticGroupRequest struct {
	Message       string
	RecentContext []SemanticGroupContextMessage
	Members       []SemanticGroupMember
	OwnerUserID   string // ctx_group_state.created_by_user_id; "" for platform groups
}

SemanticGroupRequest is everything the arbiter needs to route a no-mention human message. The dispatcher owns DB access and builds this; the arbiter owns model I/O and policy.

type SnapshotLoader

type SnapshotLoader func(context.Context, string) (*config.Snapshot, error)

type StreamFuncBuilder added in v0.28.0

type StreamFuncBuilder func(context.Context, string, config.ProviderCreds) (providers.StreamFunc, error)

Jump to

Keyboard shortcuts

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