base

package
v0.18.3 Latest Latest
Warning

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

Go to latest
Published: Mar 5, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Index

Constants

View Source
const ErrSenderNotConfigured = "sender not configured"

ErrSenderNotConfigured is returned when SendMessage is called but no sender function has been configured.

Variables

View Source
var GlobalDangerRegistry = &DangerApprovalRegistry{}

GlobalDangerRegistry is the singleton registry for danger block approvals.

Functions

func ExtractInt64FromMetadata

func ExtractInt64FromMetadata(msg *ChatMessage, key string) int64

ExtractInt64FromMetadata extracts an int64 value from message metadata. Returns 0 if the key doesn't exist or the value is not a numeric type.

func ExtractStringFromMetadata

func ExtractStringFromMetadata(msg *ChatMessage, key string) string

ExtractStringFromMetadata extracts a string value from message metadata. Returns empty string if the key doesn't exist or the value is not a string.

func ReadBody

func ReadBody(r *http.Request) ([]byte, error)

func RespondWithError

func RespondWithError(w http.ResponseWriter, code int, message string)

func RespondWithJSON

func RespondWithJSON(w http.ResponseWriter, code int, data any) error

func RespondWithText

func RespondWithText(w http.ResponseWriter, code int, text string)

func RuneCount added in v0.18.0

func RuneCount(s string) int

RuneCount counts Unicode runes (characters) instead of bytes

func TruncateByRune added in v0.18.0

func TruncateByRune(s string, maxRunes int) string

TruncateByRune truncates string by rune count, not byte count (no ellipsis)

func TruncateWithEllipsis added in v0.18.0

func TruncateWithEllipsis(s string, maxRunes int) string

TruncateWithEllipsis truncates string by rune count with ellipsis

Types

type Adapter

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

Adapter is the base adapter implementing common functionality

func NewAdapter

func NewAdapter(
	platform string,
	config Config,
	logger *slog.Logger,
	opts ...AdapterOption,
) *Adapter

NewAdapter creates a new base adapter

func (*Adapter) AppendStream added in v0.18.0

func (a *Adapter) AppendStream(ctx context.Context, channelID, messageTS, content string) error

AppendStream is a no-op by default, overridden by platforms that support it

func (*Adapter) DeleteMessage added in v0.15.5

func (a *Adapter) DeleteMessage(ctx context.Context, channelID, messageTS string) error

DeleteMessage is a no-op by default, overridden by platforms that support it

func (*Adapter) FindSessionByUserAndChannel added in v0.12.0

func (a *Adapter) FindSessionByUserAndChannel(userID, channelID string) *Session

FindSessionByUserAndChannel finds a session by matching user_id and channel_id This is useful for slash commands where we don't have the exact key Performance: O(1) using secondary index

func (*Adapter) GetOrCreateSession

func (a *Adapter) GetOrCreateSession(userID, botUserID, channelID, threadID string) string

GetOrCreateSession gets or creates a session using deterministic session ID generation Parameters:

  • userID: the user's ID on the platform
  • botUserID: the bot's user ID (for multi-bot scenarios, empty for single bot)
  • channelID: the channel/room ID (empty for DM)
  • threadID: the thread/topic ID (empty if not applicable)

Returns the generated session ID (deterministic based on inputs)

func (*Adapter) GetSession

func (a *Adapter) GetSession(key string) (*Session, bool)

GetSession retrieves a session by key

func (*Adapter) HandleMessage

func (a *Adapter) HandleMessage(ctx context.Context, msg *ChatMessage) error

HandleMessage handles incoming message (stub for interface compliance)

func (*Adapter) Handler

func (a *Adapter) Handler() MessageHandler

Handler returns the message handler (thread-safe)

func (*Adapter) Logger

func (a *Adapter) Logger() *slog.Logger

Logger returns the logger

func (*Adapter) Platform

func (a *Adapter) Platform() string

Platform returns the platform name

func (*Adapter) SendMessage

func (a *Adapter) SendMessage(ctx context.Context, sessionID string, msg *ChatMessage) error

SendMessage sends a message (requires messageSender to be set)

func (*Adapter) SendThreadReply added in v0.18.0

func (a *Adapter) SendThreadReply(ctx context.Context, channelID, threadTS, text string) error

SendThreadReply is a no-op by default, overridden by platforms that support it (Space Folding)

func (*Adapter) SetAssistantStatus added in v0.18.0

func (a *Adapter) SetAssistantStatus(ctx context.Context, channelID, threadTS, status string) error

SetAssistantStatus is a no-op by default, overridden by platforms that support it

func (*Adapter) SetHandler

func (a *Adapter) SetHandler(handler MessageHandler)

SetHandler sets the message handler (thread-safe)

func (*Adapter) SetLogger

func (a *Adapter) SetLogger(logger *slog.Logger)

SetLogger sets the logger

func (*Adapter) Start

func (a *Adapter) Start(ctx context.Context) error

Start starts the adapter

func (*Adapter) StartStream added in v0.18.0

func (a *Adapter) StartStream(ctx context.Context, channelID, threadTS string) (string, error)

StartStream is a no-op by default, overridden by platforms that support it

func (*Adapter) Stop

func (a *Adapter) Stop() error

Stop stops the adapter

func (*Adapter) StopStream added in v0.18.0

func (a *Adapter) StopStream(ctx context.Context, channelID, messageTS string) error

StopStream is a no-op by default, overridden by platforms that support it

func (*Adapter) SystemPrompt

func (a *Adapter) SystemPrompt() string

SystemPrompt returns the system prompt

func (*Adapter) UpdateMessage added in v0.15.5

func (a *Adapter) UpdateMessage(ctx context.Context, channelID, messageTS string, msg *ChatMessage) error

UpdateMessage is a no-op by default, overridden by platforms that support it

func (*Adapter) WebhookHandler

func (a *Adapter) WebhookHandler() http.Handler

WebhookHandler returns an http.Handler with all webhook endpoints registered

func (*Adapter) WebhookPath

func (a *Adapter) WebhookPath() string

WebhookPath returns the primary webhook path for this adapter

type AdapterOption

type AdapterOption func(*Adapter)

AdapterOption configures the base adapter

func WithHTTPHandler

func WithHTTPHandler(path string, handler http.HandlerFunc) AdapterOption

WithHTTPHandler adds an HTTP handler

func WithMessageParser

func WithMessageParser(parser MessageParser) AdapterOption

WithMessageParser sets the message parser

func WithMessageSender

func WithMessageSender(sender MessageSender) AdapterOption

WithMessageSender sets the message sender

func WithMetadataExtractor

func WithMetadataExtractor(extractor MetadataExtractor) AdapterOption

WithMetadataExtractor sets the metadata extractor

func WithSessionIDGenerator added in v0.12.0

func WithSessionIDGenerator(generator SessionIDGenerator) AdapterOption

WithSessionIDGenerator sets the session ID generator Use this to customize session ID generation per platform

func WithSessionTimeout

func WithSessionTimeout(timeout time.Duration) AdapterOption

WithSessionTimeout sets the session timeout

func WithoutServer

func WithoutServer() AdapterOption

WithoutServer disables the embedded HTTP server Use this when running adapters under a unified server

type Attachment

type Attachment struct {
	Type     string `json:"type"`
	URL      string `json:"url"`
	Title    string `json:"title"`
	Text     string `json:"text"`
	ThumbURL string `json:"thumb_url,omitempty"`
}

type ChatAdapter

type ChatAdapter interface {
	Platform() string
	SystemPrompt() string
	Start(ctx context.Context) error
	Stop() error
	SendMessage(ctx context.Context, sessionID string, msg *ChatMessage) error
	HandleMessage(ctx context.Context, msg *ChatMessage) error
	SetHandler(MessageHandler)
}

type ChatMessage

type ChatMessage struct {
	Type        MessageType // Message type for rendering decisions
	Platform    string
	SessionID   string
	UserID      string
	Content     string
	MessageID   string
	Timestamp   time.Time
	Metadata    map[string]any
	RichContent *RichContent
}

type Config

type Config struct {
	ServerAddr   string
	SystemPrompt string
}

Config is the common configuration for all adapters

type DangerApprovalRegistry added in v0.18.0

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

DangerApprovalRegistry manages pending danger block approvals. Used by chatapps to block on WAF interception until user approves/denies via Slack buttons.

func (*DangerApprovalRegistry) Cancel added in v0.18.0

func (r *DangerApprovalRegistry) Cancel(sessionID string)

Cancel removes a pending approval without resolving it (e.g. on context cancellation).

func (*DangerApprovalRegistry) Register added in v0.18.0

func (r *DangerApprovalRegistry) Register(sessionID string) chan bool

Register creates a pending approval channel for the given sessionID. Returns the channel to block on. The caller should select on ctx.Done() and this channel.

func (*DangerApprovalRegistry) Resolve added in v0.18.0

func (r *DangerApprovalRegistry) Resolve(sessionID string, approved bool) bool

Resolve resolves a pending approval for the given sessionID. Returns true if the sessionID was found and resolved, false otherwise.

type EngineSupport added in v0.15.5

type EngineSupport interface {
	SetEngine(eng *engine.Engine)
}

EngineSupport defines optional interface for adapters that need engine integration

type HTTPClient

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

HTTPClient provides common HTTP request patterns for chat adapters. This eliminates the duplicate HTTP request building code across adapters.

func NewHTTPClient

func NewHTTPClient() *HTTPClient

NewHTTPClient creates a new HTTPClient with the default http.Client.

func NewHTTPClientWithConfig

func NewHTTPClientWithConfig(timeout time.Duration, maxRetries int) *HTTPClient

NewHTTPClientWithConfig creates a new HTTPClient with custom configuration.

func (*HTTPClient) Get

func (c *HTTPClient) Get(ctx context.Context, url string, headers map[string]string) ([]byte, error)

Get sends a GET request and returns the response body.

func (*HTTPClient) PostJSON

func (c *HTTPClient) PostJSON(ctx context.Context, url string, payload any, headers map[string]string) ([]byte, error)

PostJSON sends a POST request with a JSON body and returns the response body. headers is a map of header key-value pairs to add to the request.

func (*HTTPClient) PostJSONWithResponse

func (c *HTTPClient) PostJSONWithResponse(ctx context.Context, url string, payload any, headers map[string]string, dest any) error

PostJSONWithResponse sends a POST request and decodes the JSON response into dest.

type MessageHandler

type MessageHandler func(ctx context.Context, msg *ChatMessage) error

type MessageOperations added in v0.15.5

type MessageOperations interface {
	DeleteMessage(ctx context.Context, channelID, messageTS string) error
	UpdateMessage(ctx context.Context, channelID, messageTS string, msg *ChatMessage) error
	// SetAssistantStatus sets the native assistant status text at the bottom of the thread
	// Used to drive dynamic status hints (e.g., "Thinking...", "Searching code...")
	SetAssistantStatus(ctx context.Context, channelID, threadTS, status string) error
	// SendThreadReply sends a message as a reply inside a thread (Space Folding)
	SendThreadReply(ctx context.Context, channelID, threadTS, text string) error
	// StartStream starts a native streaming message, returns message_ts as anchor for subsequent updates
	StartStream(ctx context.Context, channelID, threadTS string) (string, error)
	// AppendStream incrementally pushes content to an existing stream
	AppendStream(ctx context.Context, channelID, messageTS, content string) error
	// StopStream ends the stream and finalizes the message
	StopStream(ctx context.Context, channelID, messageTS string) error
}

MessageOperations defines platform-specific message operations

type MessageParser

type MessageParser func(body []byte, metadata map[string]any) (*ChatMessage, error)

MessageParser parses incoming requests into ChatMessage

type MessageSender

type MessageSender func(ctx context.Context, sessionID string, msg *ChatMessage) error

MessageSender sends messages to the platform

type MessageType added in v0.13.0

type MessageType string

MessageType defines the normalized message types across all chat platforms

const (
	// MessageTypeThinking indicates the AI is reasoning or thinking
	MessageTypeThinking MessageType = "thinking"
	// MessageTypeAnswer indicates text output from the AI
	MessageTypeAnswer MessageType = "answer"
	// MessageTypeToolUse indicates a tool invocation is starting
	MessageTypeToolUse MessageType = "tool_use"
	// MessageTypeToolResult indicates a tool execution result
	MessageTypeToolResult MessageType = "tool_result"
	// MessageTypeError indicates an error occurred
	MessageTypeError MessageType = "error"
	// MessageTypePlanMode indicates AI is in plan mode and generating a plan
	MessageTypePlanMode MessageType = "plan_mode"
	// MessageTypeExitPlanMode indicates AI completed planning and requests user approval
	MessageTypeExitPlanMode MessageType = "exit_plan_mode"
	// MessageTypeAskUserQuestion indicates AI is asking a clarifying question
	MessageTypeAskUserQuestion MessageType = "ask_user_question"
	// MessageTypeDangerBlock indicates a dangerous operation confirmation block
	MessageTypeDangerBlock MessageType = "danger_block"
	// MessageTypeSessionStats indicates session statistics
	MessageTypeSessionStats MessageType = "session_stats"
	// MessageTypeCommandProgress indicates a slash command is executing with progress updates
	MessageTypeCommandProgress MessageType = "command_progress"
	// MessageTypeCommandComplete indicates a slash command has completed
	MessageTypeCommandComplete MessageType = "command_complete"
	// MessageTypeSystem indicates a system-level message
	MessageTypeSystem MessageType = "system"
	// MessageTypeUser indicates a user message reflection
	MessageTypeUser MessageType = "user"
	// MessageTypeStepStart indicates a new step/milestone (OpenCode specific)
	MessageTypeStepStart MessageType = "step_start"
	// MessageTypeStepFinish indicates a step/milestone completed (OpenCode specific)
	MessageTypeStepFinish MessageType = "step_finish"
	// MessageTypeRaw indicates unparsed raw output (fallback)
	MessageTypeRaw MessageType = "raw"
	// MessageTypeSessionStart indicates a new session is starting (cold start)
	MessageTypeSessionStart MessageType = "session_start"
	// MessageTypeEngineStarting indicates the engine is starting up
	MessageTypeEngineStarting MessageType = "engine_starting"
	// MessageTypeUserMessageReceived indicates user message has been received
	MessageTypeUserMessageReceived MessageType = "user_message_received"
	// MessageTypePermissionRequest indicates a permission request from Claude Code
	MessageTypePermissionRequest MessageType = "permission_request"
)

type MetadataExtractor

type MetadataExtractor func(update any) map[string]any

MetadataExtractor extracts platform-specific metadata from incoming requests

type ParseMode

type ParseMode string
const (
	ParseModeNone     ParseMode = ""
	ParseModeMarkdown ParseMode = "markdown"
	ParseModeHTML     ParseMode = "html"
)

type PendingMessage added in v0.15.7

type PendingMessage struct {
	SessionID   string
	ChannelID   string
	MessageTS   string
	OriginalMsg *ChatMessage
	CreatedAt   time.Time
	Reason      string
}

PendingMessage represents a message pending approval

type PendingMessageStore added in v0.15.7

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

PendingMessageStore stores pending messages awaiting approval

func NewPendingMessageStore added in v0.15.7

func NewPendingMessageStore(ttl time.Duration) *PendingMessageStore

NewPendingMessageStore creates a new pending message store

func (*PendingMessageStore) Delete added in v0.15.7

func (s *PendingMessageStore) Delete(sessionID string)

Delete removes a pending message from the store

func (*PendingMessageStore) Get added in v0.15.7

func (s *PendingMessageStore) Get(sessionID string) (*PendingMessage, bool)

Get retrieves a pending message by session ID

func (*PendingMessageStore) Stop added in v0.17.0

func (s *PendingMessageStore) Stop()

Stop stops the cleanup goroutine

func (*PendingMessageStore) Store added in v0.15.7

func (s *PendingMessageStore) Store(sessionID string, msg *PendingMessage)

Store adds a pending message to the store

type RichContent

type RichContent struct {
	ParseMode      ParseMode
	InlineKeyboard any
	Blocks         []any
	Embeds         []any
	Attachments    []Attachment
}

type SenderFunc

type SenderFunc func(ctx context.Context, sessionID string, msg *ChatMessage) error

SenderFunc is the function signature for sending messages to a platform.

type SenderWithMutex

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

SenderWithMutex provides thread-safe sender management for chat adapters. This eliminates the duplicate sender/senderMu pattern across all adapters.

func NewSenderWithMutex

func NewSenderWithMutex() *SenderWithMutex

NewSenderWithMutex creates a new SenderWithMutex with no sender configured.

func NewSenderWithMutexFunc

func NewSenderWithMutexFunc(sender SenderFunc) *SenderWithMutex

NewSenderWithMutexFunc creates a new SenderWithMutex with an initial sender.

func (*SenderWithMutex) HasSender

func (s *SenderWithMutex) HasSender() bool

HasSender returns true if a sender has been configured.

func (*SenderWithMutex) SendMessage

func (s *SenderWithMutex) SendMessage(ctx context.Context, sessionID string, msg *ChatMessage) error

SendMessage sends a message using the configured sender. Thread-safe. Returns ErrSenderNotConfigured if no sender has been set.

func (*SenderWithMutex) Sender

func (s *SenderWithMutex) Sender() SenderFunc

Sender returns the current sender function (may be nil). Note: This does not acquire the lock, so the caller should ensure thread-safety if the sender might be modified concurrently.

func (*SenderWithMutex) SetSender

func (s *SenderWithMutex) SetSender(fn SenderFunc)

SetSender sets the sender function. Thread-safe.

type Session

type Session struct {
	SessionID  string
	UserID     string
	ChannelID  string
	Platform   string
	LastActive time.Time
}

Session represents a user session in a chat platform

type SessionIDGenerator added in v0.12.0

type SessionIDGenerator interface {
	// Generate creates a deterministic session ID based on:
	// - platform: the platform name (e.g., "slack", "telegram")
	// - userID: the user's ID on the platform
	// - botUserID: the bot's user ID (for multi-bot scenarios)
	// - channelID: the channel/room ID (empty for DM)
	// - threadID: the thread/topic ID (empty if not applicable)
	Generate(platform, userID, botUserID, channelID, threadID string) string
}

SessionIDGenerator generates deterministic session IDs

type SessionOperations added in v0.15.5

type SessionOperations interface {
	GetSession(key string) (*Session, bool)
	FindSessionByUserAndChannel(userID, channelID string) *Session
}

SessionOperations defines platform-specific session operations Note: Session is defined in base/adapter.go to avoid circular dependencies

type SimpleKeyGenerator added in v0.12.0

type SimpleKeyGenerator struct{}

SimpleKeyGenerator generates session IDs using a simple concatenated key This is useful for debugging or when you don't need UUID format

func NewSimpleKeyGenerator added in v0.12.0

func NewSimpleKeyGenerator() *SimpleKeyGenerator

NewSimpleKeyGenerator creates a new simple key generator

func (*SimpleKeyGenerator) Generate added in v0.12.0

func (g *SimpleKeyGenerator) Generate(platform, userID, botUserID, channelID, threadID string) string

Generate creates a session ID by concatenating all components Format: platform:userID:botUserID:channelID:threadID

type StatusProvider added in v0.18.0

type StatusProvider interface {
	// SetStatus sets current status, adapter converts to native API or bubble message
	// channelID and threadTS specify where to display the status
	SetStatus(ctx context.Context, channelID, threadTS string, status StatusType, text string) error

	// ClearStatus clears status indicator
	ClearStatus(ctx context.Context, channelID, threadTS string) error
}

StatusProvider defines the abstraction for status notification Follows Dependency Inversion Principle - adapters decide the concrete implementation

type StatusType added in v0.18.0

type StatusType string

StatusType defines AI working states

const (
	StatusInitializing StatusType = "initializing"
	StatusThinking     StatusType = "thinking"
	StatusToolUse      StatusType = "tool_use"
	StatusToolResult   StatusType = "tool_result"
	StatusAnswering    StatusType = "answering"
	StatusIdle         StatusType = "idle"
)

func MessageTypeToStatusType added in v0.18.0

func MessageTypeToStatusType(msgType MessageType) StatusType

MessageTypeToStatusType converts MessageType to StatusType for status notification Returns StatusIdle for unrecognized types

type StreamWriter added in v0.18.0

type StreamWriter interface {
	io.Writer
	io.Closer
	// MessageTS returns the message timestamp after stream starts
	MessageTS() string
}

StreamWriter defines the interface for streaming message writes Platform-agnostic abstraction for native streaming support

type UUID5Generator added in v0.12.0

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

UUID5Generator generates session IDs using UUID5 (SHA1 hash) This ensures the same inputs always produce the same session ID

func NewUUID5Generator added in v0.12.0

func NewUUID5Generator(namespace string) *UUID5Generator

NewUUID5Generator creates a new UUID5 generator with the given namespace

func (*UUID5Generator) Generate added in v0.12.0

func (g *UUID5Generator) Generate(platform, userID, botUserID, channelID, threadID string) string

Generate creates a deterministic session ID Format: UUID5(namespace + ":session:" + platform + ":" + userID + ":" + botUserID + ":" + channelID + ":" + threadID)

type WebhookProvider

type WebhookProvider interface {
	WebhookPath() string
	WebhookHandler() http.Handler
}

WebhookProvider exposes HTTP handlers for unified server integration

type WebhookRunner

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

WebhookRunner manages the lifecycle of webhook processing goroutines. This eliminates the duplicate webhookWg pattern across all adapters.

func NewWebhookRunner

func NewWebhookRunner(logger *slog.Logger) *WebhookRunner

NewWebhookRunner creates a new WebhookRunner with deduplication.

func (*WebhookRunner) Run

func (r *WebhookRunner) Run(ctx context.Context, handler MessageHandler, msg *ChatMessage)

Run executes the handler in a goroutine and tracks its completion. If handler is nil, this is a no-op. Implements event deduplication to prevent duplicate processing.

func (*WebhookRunner) Stop

func (r *WebhookRunner) Stop() bool

Stop is an alias for WaitDefault for API consistency with adapters.

func (*WebhookRunner) Wait

func (r *WebhookRunner) Wait(timeout time.Duration) bool

Wait blocks until all running goroutines complete or timeout occurs. Returns true if all goroutines completed, false if timeout occurred.

func (*WebhookRunner) WaitDefault

func (r *WebhookRunner) WaitDefault() bool

WaitDefault blocks with the default 5 second timeout.

Jump to

Keyboard shortcuts

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