trigger

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: May 7, 2026 License: AGPL-3.0 Imports: 45 Imported by: 0

Documentation

Overview

Package trigger provides services that trigger agent containers in response to external events: webhooks, cron schedules, and channel messages.

Index

Constants

View Source
const (
	// PublicSessionModeSession persists turns in a per-channel conversation
	// (the default). The sweeper finalizes idle ones.
	PublicSessionModeSession = "session"

	// PublicSessionModeOneShot creates a fresh ephemeral conversation per
	// turn — no history loaded, conversation deleted immediately after the
	// run. If the user's message is a reply, the referenced text is
	// included as a wrapped context block so the LLM has at least the
	// thing being replied to.
	PublicSessionModeOneShot = "one_shot"
)

PublicSessionMode controls how public conversations carry context across turns.

View Source
const (
	// BridgeReferenceReply — the user replied to another message (Discord
	// reply, Telegram reply_to_message, Slack thread parent).
	BridgeReferenceReply = "reply"
	// BridgeReferenceForward — the user forwarded a message authored
	// elsewhere (Discord message snapshot, Telegram forward_origin).
	BridgeReferenceForward = "forward"
)

BridgeReferenceKind distinguishes the platform mechanism that produced the reference so the prompt builder can label it for the LLM.

View Source
const CancelButtonAfter = 20 * time.Second

CancelButtonAfter is how long a bridge run can stream before the driver posts a "Still working… Tap to stop" message with a cancel button. The message is deleted when the run ends (naturally or via the user tap).

View Source
const DefaultPublicPromptTimeoutSeconds = 60

DefaultPublicPromptTimeoutSeconds caps how long a single public-DM prompt run can take. Authenticated users get the full PromptHTTPCeiling; public callers are throttled tighter so a noisy abuser can't tie up the agent on long chains.

View Source
const DefaultPublicSessionTTLSeconds = 3 * 60 * 60

DefaultPublicSessionTTLSeconds is the inactivity window after which a public bridge conversation is swept and finalized. Three hours covers the "user wandered off" case while keeping public state from accumulating indefinitely.

View Source
const PromptHTTPCeiling = 30 * time.Minute

PromptHTTPCeiling is the absolute cap on a prompt run's outbound HTTP request. Generous on purpose: prompt runs may legitimately stream for many minutes (long tool chains, slow LLMs); the user cancels manually via DELETE /api/v1/runs/{runID} when they want to stop earlier. Cron and webhook callers pass their own (typically shorter) timeout to ForwardCron/Webhook.

Variables

View Source
var ErrTranscriptionNotConfigured = errors.New("transcription not configured")

ErrTranscriptionNotConfigured is returned when no system-wide transcription model is set. Callers handle this non-fatally (degrade to file-only delivery).

View Source
var Registry = []SlashCommand{
	{Name: "auth", Description: "Link your Airlock account", Access: agentsdk.AccessPublic},
	{Name: "cancel", Description: "Stop the current run", Access: agentsdk.AccessPublic},
	{Name: "clear", Description: "Clear conversation context", Access: agentsdk.AccessUser},
	{Name: "compact", Description: "Summarize and compact context", Access: agentsdk.AccessUser},
	{Name: "echo", Description: "Toggle tool output bubbles (on / off / blank=flip)", Access: agentsdk.AccessUser},
}

Registry is the canonical list of commands. Order is preserved when rendered into platform command menus.

Functions

func ResolveAgentAccess

func ResolveAgentAccess(ctx context.Context, q *dbq.Queries, agentID, userID uuid.UUID) agentsdk.Access

ResolveAgentAccess returns the caller's effective per-agent access level from agent_members. Non-members get AccessPublic, members map straight to AccessUser or AccessAdmin. Tenant role is deliberately not consulted — see airlock/CLAUDE.md "Permission Model" for why these axes are separate.

func ResolveEcho

func ResolveEcho(settingsJSON []byte, driverDefault bool) bool

ResolveEcho returns the effective echo flag for a conversation given its raw settings JSON and the driver's default. Explicit conversation settings win over the driver default; missing JSON or missing key falls through to the default.

func StartPublicSweeper added in v0.2.8

func StartPublicSweeper(ctx context.Context, database *db.DB, mgr *BridgeManager, interval time.Duration, logger *zap.Logger)

StartPublicSweeper kicks off a goroutine that runs SweepExpiredPublicConversations every interval until ctx is done. Single-replica only — for multi-replica deployments this needs a Postgres advisory lock so exactly one airlock holds the sweeper at any moment.

func StreamNDJSONResponse

func StreamNDJSONResponse(body io.Reader, runID string, events chan<- ResponseEvent) (string, []message.Message, *usageInfo, error)

StreamNDJSONResponse reads NDJSON events from a response stream, forwards ResponseEvents to the events channel for real-time delivery, and collects the full text response. Closes the events channel when done. runID is stamped onto confirmation_required events so drivers can build callback-bound UI (e.g. Telegram inline keyboards).

func SweepExpiredPublicConversations added in v0.2.8

func SweepExpiredPublicConversations(ctx context.Context, database *db.DB, mgr *BridgeManager, logger *zap.Logger)

SweepExpiredPublicConversations runs one pass: list public bridge conversations whose updated_at is older than the bridge's configured TTL, send a final "Conversation completed" message, then delete the conversation row (which cascade-drops messages via the FK).

Sends are best-effort — a failure to deliver the final message (network blip, revoked bot token, deleted DM channel) does not block deletion. Local state is the source of truth; the user just doesn't see the goodbye.

Types

type BridgeCallback

type BridgeCallback struct {
	Data      string // opaque payload, e.g. "approve:<runID>"
	AckID     string // platform-specific ack handle (Telegram callback_query.id)
	MessageID string // ID of the message the button is attached to (so we

}

BridgeCallback represents an interactive UI acknowledgement — a button tap on an inline keyboard or similar platform-native affordance. Drivers that don't support rich UI leave this nil.

type BridgeDriver

type BridgeDriver interface {
	// Init is called once when a bridge is first created.
	// Uses pointer so the driver can set initial config (e.g. poll offset).
	Init(ctx context.Context, br *dbq.Bridge) error

	// Activate is called on every startup for active bridges.
	Activate(ctx context.Context, br dbq.Bridge) error

	// Teardown is called when a bridge is deleted or disabled.
	Teardown(ctx context.Context, br dbq.Bridge) error

	// Poll fetches new events from the platform.
	// Uses pointer so the driver can update br.Config (e.g. poll offset).
	Poll(ctx context.Context, br *dbq.Bridge) ([]BridgeEvent, error)

	// SendStream delivers a response, streaming text deltas as they arrive.
	// echo controls whether tool-call / tool-result bubbles are rendered;
	// drivers that collapse tool output some other way may ignore it.
	// Returns the final assembled text.
	SendStream(ctx context.Context, br dbq.Bridge, externalID string, echo bool, events <-chan ResponseEvent) (string, error)

	// DefaultEcho returns whether tool bubbles render by default on this
	// platform. Drivers that display each tool-call/tool-result as its own
	// chat message (Telegram) should return false; drivers whose UI can
	// collapse tool output inline (web) should return true.
	// Used when a conversation has no explicit settings.echo override.
	DefaultEcho() bool

	// RemoveButtons strips the inline keyboard / component buttons from a
	// previously sent message, leaving its text intact. Called after the
	// user taps an approve/deny button so the resolved confirmation can't
	// be tapped again. Best-effort: errors are logged but not propagated.
	RemoveButtons(ctx context.Context, br dbq.Bridge, externalID, messageID string) error
}

BridgeDriver handles platform-specific message parsing and delivery.

type BridgeEvent

type BridgeEvent struct {
	BridgeID          uuid.UUID
	ExternalID        string // platform chat_id (Telegram chat ID, etc.)
	SenderID          string // platform user ID of sender (for identity lookup)
	SenderName        string
	Text              string
	Files             []BridgeFile // attached files (photos, documents)
	Callback          *BridgeCallback
	ReferencedMessage *BridgeReferencedMessage // reply target / forward source (driver-populated)
	RawPayload        []byte
}

BridgeEvent represents a normalized incoming event from any platform. Either Text/Files (new user message) or Callback (button tap) is populated.

type BridgeFile

type BridgeFile struct {
	FileID      string // platform file ID (e.g. Telegram file_id)
	Filename    string
	ContentType string
	Size        int64
	Data        []byte // file content (downloaded by driver)

	// IsVoiceNote marks a short voice recording (e.g. Telegram "voice")
	// that the bridge layer should auto-transcribe before forwarding to
	// the agent. Plain audio/video/document attachments leave this false.
	IsVoiceNote bool
}

BridgeFile is a file attached to a bridge message.

type BridgeManager

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

BridgeManager manages bridge drivers and routes events to agents.

func NewBridgeManager

func NewBridgeManager(drivers map[string]BridgeDriver, prompter *PromptProxy, database *db.DB, encryptor secrets.Store, hmacSecret, publicURL string, logger *zap.Logger) *BridgeManager

NewBridgeManager creates a BridgeManager.

func (*BridgeManager) AddBridge

func (m *BridgeManager) AddBridge(bridgeID uuid.UUID)

AddBridge activates a newly created bridge and starts its poller. Idempotent: if a poller is already running for this bridge ID (e.g. the bridge was re-registered after a config change), the existing one is cancelled first so only one poller hits the platform at a time.

func (*BridgeManager) HandleEvent

func (m *BridgeManager) HandleEvent(ctx context.Context, event BridgeEvent) error

HandleEvent processes a parsed BridgeEvent — routes to agent via PromptProxy.

func (*BridgeManager) RemoveBridge

func (m *BridgeManager) RemoveBridge(bridgeID uuid.UUID)

RemoveBridge stops the poller for a bridge. Safe to call for an unknown bridge ID — it's a no-op. The DB row is NOT touched here; callers that want full deletion do the DB work separately (typically by calling q.DeleteBridge alongside this).

func (*BridgeManager) SendMessage

func (m *BridgeManager) SendMessage(ctx context.Context, bridgeID uuid.UUID, externalID, text string) error

SendMessage sends a text message to a bridge conversation. Convenience wrapper.

func (*BridgeManager) SendParts

func (m *BridgeManager) SendParts(ctx context.Context, bridgeID uuid.UUID, externalID string, parts []agentsdk.DisplayPart) error

SendParts sends display parts to a bridge conversation. Looks up the bridge, decrypts the token, and delegates to the driver.

func (*BridgeManager) Start

func (m *BridgeManager) Start(ctx context.Context) error

Start sets up all active bridges and starts pollers.

func (*BridgeManager) Stop

func (m *BridgeManager) Stop()

Stop gracefully shuts down all pollers.

type BridgeReferencedMessage added in v0.2.8

type BridgeReferencedMessage struct {
	Kind       string // BridgeReferenceReply | BridgeReferenceForward
	SenderName string // author of the referenced content
	Text       string
	AuthoredAt time.Time
	FromBot    bool // true when the referenced message was authored by our bot (replies only)
}

BridgeReferencedMessage describes a message the current event points at — either a reply target or a forwarded message. Surfaced to the LLM as a wrapped context block regardless of session mode, so the model has the referenced content even when it's outside the active conversation history (or when there is no history at all).

type BridgeSettings added in v0.2.8

type BridgeSettings struct {
	// AllowPublicDMs lets unauthenticated users DM the bot at AccessPublic.
	// When false, unauth DMs are dropped (except /auth, which is the
	// linking opt-in path and always works).
	AllowPublicDMs bool `json:"allow_public_dms"`

	// PublicSessionTTLSeconds is the inactivity window before a public
	// conversation is finalized. 0 disables sweeping for that bridge.
	// Only meaningful when PublicSessionMode == "session".
	PublicSessionTTLSeconds int `json:"public_session_ttl_seconds"`

	// PublicSessionMode chooses between persistent ("session") and
	// stateless ("one_shot") public conversations. Defaults to session.
	PublicSessionMode string `json:"public_session_mode"`

	// PublicPromptTimeoutSeconds caps wall-clock duration of a single
	// public-DM prompt run. Authenticated users are not affected (they
	// run under the global PromptHTTPCeiling). 0 means "use the default"
	// (DefaultPublicPromptTimeoutSeconds).
	PublicPromptTimeoutSeconds int `json:"public_prompt_timeout_seconds"`
}

BridgeSettings is the user-tunable subset of bridge config exposed in the dashboard. Stored in bridges.settings JSONB. Distinct from bridges.config which carries driver-internal state.

func DecodeBridgeSettings added in v0.2.8

func DecodeBridgeSettings(raw []byte) BridgeSettings

DecodeBridgeSettings parses a bridges.settings JSON blob, falling back to defaults for any missing keys. Empty / invalid JSON returns the default settings.

func DefaultBridgeSettings added in v0.2.8

func DefaultBridgeSettings() BridgeSettings

DefaultBridgeSettings returns the settings a freshly created bridge row should carry. New bridges currently insert `{}` and rely on this function to materialize defaults at read time.

AllowPublicDMs defaults to false: opening a bot to anonymous users is a deliberate operator choice (it exposes the agent's free-tier surface to the public internet). Operators flip it on from the bridge settings dialog when they actually want public access.

type CommandRegistrar

type CommandRegistrar interface {
	RegisterCommands(ctx context.Context, br dbq.Bridge, cmds []SlashCommand) error
}

CommandRegistrar is an optional BridgeDriver capability: platforms with a native command menu (Telegram setMyCommands, Discord app commands, ...) implement it to receive the slash-command registry on activation. Drivers without such a menu simply don't implement it.

type DiscordDriver added in v0.2.8

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

DiscordDriver speaks the Discord Gateway WebSocket for inbound events and the v10 REST API for outbound message delivery. Single-replica only today: the Gateway connection holds session state and only one process can identify per token, so multi-replica needs a per-bridge advisory lock added later.

func NewDiscordDriver added in v0.2.8

func NewDiscordDriver(logger *zap.Logger) *DiscordDriver

NewDiscordDriver creates a DiscordDriver. logger surfaces gateway and REST errors. Pass zap.NewNop() in tests.

func NewDiscordDriverWithBase added in v0.2.8

func NewDiscordDriverWithBase(apiBase string, client *http.Client) *DiscordDriver

NewDiscordDriverWithBase creates a DiscordDriver pointed at an alternate REST base URL. The Gateway URL is fetched via GET /gateway/bot and inherits the same prefix for tests.

func (*DiscordDriver) Activate added in v0.2.8

func (d *DiscordDriver) Activate(ctx context.Context, br dbq.Bridge) error

func (*DiscordDriver) CreateDMChannel added in v0.2.8

func (d *DiscordDriver) CreateDMChannel(ctx context.Context, token, recipientID string) (string, error)

CreateDMChannel opens (or fetches) a DM channel with a recipient and returns its channel ID. Idempotent — Discord returns the same channel every time for a given (bot, user) pair.

func (*DiscordDriver) DefaultEcho added in v0.2.8

func (d *DiscordDriver) DefaultEcho() bool

DefaultEcho mirrors Telegram: tool-call/tool-result bubbles are off by default. Each tool call would render as a separate message and a chatty agent quickly fills the DM.

func (*DiscordDriver) FetchUser added in v0.2.8

func (d *DiscordDriver) FetchUser(ctx context.Context, token, userID string) (DiscordUserInfo, error)

FetchUser hits GET /users/{id} for a snowflake. Bot tokens have permission to look up any user by ID, returning the public-profile fields (username, global_name, avatar). Used by the identity-linking preview so the user can verify which Discord account is about to bind.

func (*DiscordDriver) GetMe added in v0.2.8

func (d *DiscordDriver) GetMe(ctx context.Context, token string) (string, error)

GetMe is the public-facing token validator used by the bridge create handler. Returns the bot's username for storage on the bridge row.

func (*DiscordDriver) Init added in v0.2.8

func (d *DiscordDriver) Init(ctx context.Context, br *dbq.Bridge) error

func (*DiscordDriver) Poll added in v0.2.8

func (d *DiscordDriver) Poll(ctx context.Context, br *dbq.Bridge) ([]BridgeEvent, error)

Poll blocks until the gateway goroutine has buffered events or the timeout fires. The driver handles its own reconnect, so Poll never returns errors — a dead WS just yields no events until reconnect.

func (*DiscordDriver) RegisterCommands added in v0.2.8

func (d *DiscordDriver) RegisterCommands(ctx context.Context, br dbq.Bridge, cmds []SlashCommand) error

RegisterCommands bulk-overwrites the global slash commands for this bot's application. Global commands take up to ~1 hour to propagate across Discord's edge — first-time install is fine, name changes lag.

func (*DiscordDriver) RemoveButtons added in v0.2.11

func (d *DiscordDriver) RemoveButtons(ctx context.Context, br dbq.Bridge, externalID, messageID string) error

RemoveButtons strips the action-row components from a previously sent message via PATCH /channels/{ch}/messages/{msg} with components=[]. The message text is left intact.

func (*DiscordDriver) SendDM added in v0.2.8

func (d *DiscordDriver) SendDM(ctx context.Context, token, recipientID, content string) error

SendDM is a convenience wrapper that opens (or reuses) a DM channel with a user and posts a plain-text message. Used by the bridge layer for the identity-linking prompt so the link is never posted publicly.

func (*DiscordDriver) SendParts added in v0.2.8

func (d *DiscordDriver) SendParts(ctx context.Context, token, channelID string, parts []agentsdk.DisplayPart) error

SendParts sends display parts to a Discord channel — text, images, files.

func (*DiscordDriver) SendStream added in v0.2.8

func (d *DiscordDriver) SendStream(ctx context.Context, br dbq.Bridge, externalID string, echo bool, events <-chan ResponseEvent) (string, error)

func (*DiscordDriver) Teardown added in v0.2.8

func (d *DiscordDriver) Teardown(_ context.Context, br dbq.Bridge) error

type DiscordUserInfo added in v0.2.8

type DiscordUserInfo struct {
	ID         string
	Username   string // legacy @handle (no discriminator)
	GlobalName string // newer "display name" (Discord rolled out 2023)
	AvatarURL  string
}

DiscordUserInfo is the slice of /users/{id} we surface in identity linking. AvatarURL is the resolved CDN URL ("" if the user has no avatar set) so callers don't have to know the snowflake-hash format.

type Dispatcher

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

Dispatcher ensures agent containers are running and forwards HTTP requests to them.

func NewDispatcher

func NewDispatcher(cfg *config.Config, db *db.DB, containers container.ContainerManager, enc secrets.Store, logger *zap.Logger) *Dispatcher

NewDispatcher creates a Dispatcher.

func (*Dispatcher) CancelRun added in v0.2.8

func (d *Dispatcher) CancelRun(runID uuid.UUID) bool

CancelRun aborts the in-flight outbound request for the given run, if any. Returns true if a cancel was fired. Idempotent — repeat calls and calls for runs that already finished are no-ops.

func (*Dispatcher) EnsureRunning

func (d *Dispatcher) EnsureRunning(ctx context.Context, agentID uuid.UUID) (*container.Container, error)

EnsureRunning looks up the agent, decrypts its DB credentials, and starts (or reconnects to) the agent container. Returns the running container.

func (*Dispatcher) ForwardCron

func (d *Dispatcher) ForwardCron(ctx context.Context, agentID uuid.UUID, cronName string, timeout time.Duration) (io.ReadCloser, uuid.UUID, error)

ForwardCron ensures the agent is running, creates a run record, and POSTs to the agent's cron endpoint. Returns the response body stream and the run ID. The timeout parameter controls the HTTP client timeout.

func (*Dispatcher) ForwardPrompt

func (d *Dispatcher) ForwardPrompt(ctx context.Context, agentID uuid.UUID, input agentsdk.PromptInput, bridgeID *uuid.UUID) (io.ReadCloser, uuid.UUID, error)

ForwardPrompt ensures the agent is running, creates a run record, and POSTs the prompt input to the agent container. Returns the response body stream (NDJSON) and the run ID.

func (*Dispatcher) ForwardWebhook

func (d *Dispatcher) ForwardWebhook(ctx context.Context, agentID uuid.UUID, path string, body []byte, bridgeID *uuid.UUID, timeout time.Duration) (io.ReadCloser, uuid.UUID, error)

ForwardWebhook ensures the agent is running, creates a run record, and POSTs the webhook payload to the agent container. Returns the response body stream and the run ID. The timeout parameter controls the HTTP client timeout.

func (*Dispatcher) InFlightIDs added in v0.2.8

func (d *Dispatcher) InFlightIDs() []uuid.UUID

InFlightIDs returns a snapshot of currently-tracked run IDs. Used by the stuck-run sweeper so it doesn't race the dispatcher and prematurely terminate a still-live run.

func (*Dispatcher) RefreshAgent

func (d *Dispatcher) RefreshAgent(ctx context.Context, agentID uuid.UUID) error

RefreshAgent triggers a synchronous re-sync on the agent container. Used after server-side state changes the cached system prompt depends on (typically MCP OAuth completion) so the running agent picks up new tools without a restart. If the container isn't running, returns nil — there's nothing to refresh; the agent will sync fresh on its next startup.

type PromptProxy

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

PromptProxy manages conversation history and forwards prompts to agent containers.

func NewPromptProxy

func NewPromptProxy(dispatcher *Dispatcher, database *db.DB, s3 *storage.S3Client, resolveTranscription TranscriptionResolver, logger *zap.Logger) *PromptProxy

NewPromptProxy creates a PromptProxy. The resolver is invoked to obtain the admin-configured transcription model when a voice note arrives; pass nil to disable auto-transcription (voice notes flow through as plain attachments).

func (*PromptProxy) HandleCallback

func (p *PromptProxy) HandleCallback(
	ctx context.Context,
	agentID, bridgeID, userID uuid.UUID,
	externalID, data string,
	events chan<- ResponseEvent,
) (staleRun bool, err error)

HandleCallback resolves a suspended run based on a bridge UI callback (inline-keyboard tap). data is the opaque platform payload — expected format:

"approve:<runID>"  — resume with Approved=true, no prompt
"deny:<runID>"     — resume with Approved=false + a "Rejected by user." prompt

If the referenced run is no longer suspended (e.g. already resolved via web), emits a single "info" event and returns — the driver's AnswerCallbackQuery should still fire to clear the spinner.

func (*PromptProxy) HandleMessage

func (p *PromptProxy) HandleMessage(
	ctx context.Context,
	agentID, bridgeID, userID uuid.UUID,
	externalID string,
	storeHistory bool,
	oneShot bool,
	userMessage string,
	files []BridgeFile,
	referenced *BridgeReferencedMessage,
	events chan<- ResponseEvent,
) (string, error)

HandleMessage processes an incoming DM for an agent via a bridge. Manages conversation history, forwards to agent, streams response events to the provided channel, stores response when complete. The events channel is closed when streaming completes.

type ResponseEvent

type ResponseEvent struct {
	Type       string // "run_started", "text-delta", "tool-call", "tool-result", "confirmation_required", "info"
	Text       string // for text-delta / info: the delta text or info message
	ToolCallID string // for tool_call/tool_result
	ToolName   string // for tool_call/tool_result
	ToolInput  string // for tool_call: the tool arguments
	ToolOutput string // for tool_result: the tool output
	ToolError  string // for tool_result: error message if failed
	Raw        []byte // full NDJSON line (for non-text events drivers may need)

	// Populated when Type == "run_started" or "confirmation_required":
	RunID      string
	Permission string
	Patterns   []string
	Code       string
}

ResponseEvent represents an NDJSON event from the agent response stream, forwarded to the bridge driver for progressive delivery.

type RunCanceler added in v0.2.11

type RunCanceler interface {
	CancelRun(runID uuid.UUID) bool
}

RunCanceler is the slice of *Dispatcher that TrySlashCommand uses to fire the /cancel command. Defined as an interface so tests can stub without standing up containers / DB / encryptor.

type Scheduler

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

Scheduler manages cron schedules for all active agents.

func NewScheduler

func NewScheduler(dispatcher *Dispatcher, db *db.DB, logger *zap.Logger) *Scheduler

NewScheduler creates a Scheduler.

func (*Scheduler) ReloadAgent

func (s *Scheduler) ReloadAgent(ctx context.Context, agentID uuid.UUID) error

ReloadAgent reloads cron entries for a specific agent after a sync.

func (*Scheduler) Start

func (s *Scheduler) Start(ctx context.Context) error

Start loads all enabled crons from the database and starts the scheduler.

func (*Scheduler) Stop

func (s *Scheduler) Stop()

Stop gracefully stops the scheduler.

type SlashCommand

type SlashCommand struct {
	Name        string
	Description string
	Access      agentsdk.Access
}

SlashCommand describes a user-typable command. Registry is the single source of truth: TrySlashCommand dispatches from it, and bridge drivers that support native command menus (Telegram setMyCommands, etc.) expose it verbatim. Name is stored without the leading slash.

type SlashCommandResult

type SlashCommandResult struct {
	Handled          bool
	Reply            string
	ForwardAsCompact bool
}

SlashCommandResult is the outcome of handling a slash command. Handled=false means the message was not a slash command — callers should forward it to the agent as usual. Handled=true means the command was recognized and processed. Reply is the short message to show the user, unless ForwardAsCompact is set — in that case the caller should invoke the agent with PromptInput.ForceCompact=true so Sol's summarization produces the user-visible text via a normal run.

func TrySlashCommand

func TrySlashCommand(
	ctx context.Context,
	q *dbq.Queries,
	canceler RunCanceler,
	convID pgtype.UUID,
	agentID uuid.UUID,
	access agentsdk.Access,
	message string,
	logger *zap.Logger,
) (SlashCommandResult, error)

TrySlashCommand parses message for a leading slash command and, if recognized, executes it against the conversation. Returns Handled=false when the message is a plain user prompt. Both web (api.conversationsHandler.Prompt) and bridge (PromptProxy) paths call this before forwarding to the agent.

access is the caller's resolved agent access (see ResolveAgentAccess) and is compared against the command's required level. agentID is used by /clear to resolve any pending suspended run so the pending-confirmation dialog doesn't linger after context clear.

type TelegramChatInfo

type TelegramChatInfo struct {
	Username  string
	FirstName string
	LastName  string
}

TelegramChatInfo holds the subset of getChat fields we expose.

type TelegramDriver

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

TelegramDriver handles Telegram bridges via long-polling.

func NewTelegramDriver

func NewTelegramDriver(logger *zap.Logger) *TelegramDriver

NewTelegramDriver creates a TelegramDriver. logger is used to surface Telegram API errors (notably the silent-failure Markdown/HTML parse rejections that otherwise vanish). Pass zap.NewNop() in tests.

func NewTelegramDriverWithBaseURL

func NewTelegramDriverWithBaseURL(baseURL string, client *http.Client) *TelegramDriver

NewTelegramDriverWithBaseURL creates a TelegramDriver with a custom base URL and HTTP client (for testing).

func (*TelegramDriver) Activate

func (d *TelegramDriver) Activate(ctx context.Context, br dbq.Bridge) error

func (*TelegramDriver) AnswerCallbackQuery

func (d *TelegramDriver) AnswerCallbackQuery(ctx context.Context, token, callbackID, text string) error

AnswerCallbackQuery clears the loading spinner on a tapped inline-keyboard button. If text is non-empty, Telegram shows it as a transient toast.

func (*TelegramDriver) DefaultEcho

func (d *TelegramDriver) DefaultEcho() bool

DefaultEcho reports that Telegram defaults to hiding tool bubbles: each tool-call / tool-result is rendered as its own chat message and a chatty agent quickly swamps the conversation. Users can opt in per-chat with `/echo on`.

func (*TelegramDriver) GetChat

func (d *TelegramDriver) GetChat(ctx context.Context, token, chatID string) (TelegramChatInfo, error)

GetChat calls the Telegram getChat API for a private chat. Because private chat IDs equal the user ID, this resolves a telegram user's public username and display name as long as that user has DM'd the bot.

func (*TelegramDriver) GetMe

func (d *TelegramDriver) GetMe(ctx context.Context, token string) (string, error)

GetMe calls the Telegram getMe API and returns the bot username.

func (*TelegramDriver) Init

func (d *TelegramDriver) Init(ctx context.Context, br *dbq.Bridge) error

func (*TelegramDriver) Poll

func (d *TelegramDriver) Poll(ctx context.Context, br *dbq.Bridge) ([]BridgeEvent, error)

func (*TelegramDriver) RegisterCommands

func (d *TelegramDriver) RegisterCommands(ctx context.Context, br dbq.Bridge, cmds []SlashCommand) error

RegisterCommands publishes the slash-command registry to Telegram's global command menu via setMyCommands. Telegram stores names without the leading slash.

func (*TelegramDriver) RemoveButtons added in v0.2.11

func (d *TelegramDriver) RemoveButtons(ctx context.Context, br dbq.Bridge, externalID, messageID string) error

RemoveButtons strips the inline keyboard from a previously sent message via editMessageReplyMarkup. The message text is left intact so the conversation history still shows what was being confirmed.

func (*TelegramDriver) SendMessage

func (d *TelegramDriver) SendMessage(ctx context.Context, token string, chatID int64, text string) error

SendMessage sends a text message to a Telegram chat.

func (*TelegramDriver) SendParts

func (d *TelegramDriver) SendParts(ctx context.Context, token string, chatID int64, parts []agentsdk.DisplayPart) error

SendParts sends display parts to a Telegram chat. Renders each part appropriately: text → sendMessage, image → sendPhoto, file → sendDocument.

func (*TelegramDriver) SendStream

func (d *TelegramDriver) SendStream(ctx context.Context, br dbq.Bridge, externalID string, echo bool, events <-chan ResponseEvent) (string, error)

func (*TelegramDriver) Teardown

func (d *TelegramDriver) Teardown(_ context.Context, _ dbq.Bridge) error

type TranscriptionResolver

type TranscriptionResolver func(ctx context.Context) (model.TranscriptionModel, error)

TranscriptionResolver returns the admin-configured transcription model or ErrTranscriptionNotConfigured when no model is set.

func NewTranscriptionResolver

func NewTranscriptionResolver(database *db.DB, encryptor secrets.Store) TranscriptionResolver

NewTranscriptionResolver returns a resolver that reads system_settings.default_stt_provider_id + default_stt_model and looks up the associated provider row credentials.

Jump to

Keyboard shortcuts

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