db

package
v0.260801.1 Latest Latest
Warning

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

Go to latest
Published: Aug 1, 2026 License: MPL-2.0 Imports: 22 Imported by: 0

Documentation

Index

Constants

View Source
const (
	HealthStatusOK      = "ok"
	HealthStatusError   = "error"
	HealthStatusNotInit = "not_initialized"
)

Health status constants

View Source
const (
	// DefaultAdminUserID is the user ID for the default admin user
	// This is used for usage records created before multi-tenant support
	DefaultAdminUserID = "admin"
)
View Source
const DefaultChatAgent = "tingly-box"

DefaultChatAgent is the agent a chat is driven by until something hands it off — Smart Guide is the entry point. This is the single definition; bot.AgentNameTinglyBox aliases it (the bot package imports this one, so the dependency can only run in that direction).

View Source
const ProjectHistoryCap = 20

ProjectHistoryCap bounds the per-chat MRU list so storage stays bounded and the /project list stays readable.

View Source
const (
	ToolTypeMCPRuntime = "mcp_runtime" // MCP runtime tool sources

)

ToolType constants for different tool configuration types

Variables

View Source
var (
	// ErrChatIDRequired is returned when an operation needs a chat ID and got none.
	ErrChatIDRequired = errors.New("chat_id is required")
	// ErrStoreClosed is returned when the store has no usable DB handle.
	ErrStoreClosed = errors.New("chat store not initialized")
)

Sentinels for the remote chat store.

Functions

func PushProjectHistory added in v0.260801.1

func PushProjectHistory(chat *Chat, path string)

PushProjectHistory sets chat.ProjectPath and prepends it to ProjectHistory (deduped, capped). When the chat already had a ProjectPath that wasn't in the history yet, it is preserved one slot below so a fresh upgrade keeps the previous binding visible.

Types

type APITokenRecord added in v0.260418.2200

type APITokenRecord struct {
	ID           uint       `gorm:"primaryKey;autoIncrement;column:id"`
	TokenID      string     `gorm:"uniqueIndex;column:token_id;not null;size:64"` // Token identifier (jti)
	UserID       string     `gorm:"index:idx_api_token_user_id;not null;column:user_id;size:64"`
	DisplayName  string     `gorm:"column:display_name;size:256"`
	Enabled      bool       `gorm:"column:enabled;default:true"`
	ExpiresAt    *time.Time `gorm:"column:expires_at;index"`
	LastUsedAt   *time.Time `gorm:"column:last_used_at"`
	CreatedAt    time.Time  `gorm:"column:created_at"`
	CreatedBy    string     `gorm:"column:created_by;size:64"`
	RevokedAt    *time.Time `gorm:"column:revoked_at"`
	RevokeReason string     `gorm:"column:revoke_reason;size:512"`
}

APITokenRecord represents a user API token for multi-tenant authentication

func (APITokenRecord) TableName added in v0.260418.2200

func (APITokenRecord) TableName() string

TableName specifies the table name for GORM

type APITokenStore added in v0.260418.2200

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

APITokenStore manages API tokens for multi-tenant authentication

func NewAPITokenStore added in v0.260418.2200

func NewAPITokenStore(baseDir string) (*APITokenStore, error)

NewAPITokenStore creates or loads an API token store using SQLite database.

func (*APITokenStore) CleanupExpiredTokens added in v0.260418.2200

func (s *APITokenStore) CleanupExpiredTokens(olderThan time.Duration) (int64, error)

CleanupExpiredTokens removes expired tokens older than the specified duration

func (*APITokenStore) Close added in v0.260418.2200

func (s *APITokenStore) Close() error

Close closes the database connection

func (*APITokenStore) CreateTokenWithTokenID added in v0.260418.2200

func (s *APITokenStore) CreateTokenWithTokenID(userID, tokenID, displayName, createdBy string, expiresAt *time.Time) (*APITokenRecord, error)

CreateTokenWithTokenID creates a new API token record with a specific token ID

func (*APITokenStore) DeleteToken added in v0.260418.2200

func (s *APITokenStore) DeleteToken(tokenID string) error

DeleteToken permanently deletes a token record

func (*APITokenStore) GetDB added in v0.260418.2200

func (s *APITokenStore) GetDB() *gorm.DB

GetDB returns the underlying GORM DB instance (for testing)

func (*APITokenStore) GetToken added in v0.260418.2200

func (s *APITokenStore) GetToken(tokenID string) (*APITokenRecord, error)

GetToken retrieves a token by token ID

func (*APITokenStore) ListTokens added in v0.260418.2200

func (s *APITokenStore) ListTokens(userID string, enabled *bool, limit, offset int) ([]APITokenRecord, int64, error)

ListTokens returns tokens matching filters

func (*APITokenStore) RevokeToken added in v0.260418.2200

func (s *APITokenStore) RevokeToken(tokenID, reason string) error

RevokeToken revokes a token by setting enabled to false

func (*APITokenStore) SetTokenEnabled added in v0.260418.2200

func (s *APITokenStore) SetTokenEnabled(tokenID string, enabled bool) error

SetTokenEnabled enables or disables a token

func (*APITokenStore) UpdateLastUsed added in v0.260418.2200

func (s *APITokenStore) UpdateLastUsed(tokenID string) error

UpdateLastUsed updates the last_used_at timestamp for a token

func (*APITokenStore) UpdateTokenString added in v0.260418.2200

func (s *APITokenStore) UpdateTokenString(tokenID, newTokenString string) error

UpdateTokenString updates the token string for a token (for regeneration)

func (*APITokenStore) ValidateToken added in v0.260418.2200

func (s *APITokenStore) ValidateToken(tokenID string) (*APITokenRecord, error)

ValidateToken validates a token ID and returns the associated token record

type AggregatedStat

type AggregatedStat struct {
	Key              string  `json:"key"`
	ProviderUUID     string  `json:"provider_uuid,omitempty"`
	ProviderName     string  `json:"provider_name,omitempty"`
	Model            string  `json:"model,omitempty"`
	Scenario         string  `json:"scenario,omitempty"`
	UserID           string  `json:"user_id,omitempty"`
	RequestCount     int64   `json:"request_count"`
	TotalTokens      int64   `json:"total_tokens"`
	InputTokens      int64   `json:"total_input_tokens"`
	OutputTokens     int64   `json:"total_output_tokens"`
	CacheReadTokens  int64   `json:"cache_read_tokens"`
	CacheWriteTokens int64   `json:"cache_write_tokens"`
	SystemTokens     int64   `json:"system_tokens"`
	AvgInputTokens   float64 `json:"avg_input_tokens"`
	AvgOutputTokens  float64 `json:"avg_output_tokens"`
	AvgLatencyMs     float64 `json:"avg_latency_ms"`
	ErrorCount       int64   `json:"error_count"`
	ErrorRate        float64 `json:"error_rate"`
	StreamedCount    int64   `json:"streamed_count"`
	StreamedRate     float64 `json:"streamed_rate"`
}

AggregatedStat represents aggregated usage statistics

type Chat added in v0.260801.1

type Chat struct {
	ChatID         string   `json:"chat_id"`
	Platform       string   `json:"platform"`
	ProjectPath    string   `json:"project_path,omitempty"`
	ProjectHistory []string `json:"project_history,omitempty"` // MRU list of paths this chat has bound to
	OwnerID        string   `json:"owner_id,omitempty"`

	// Pairing (TOFU) — applies to direct messages only. Group chats continue
	// to use the IsWhitelisted gate, but the operator who whitelisted the
	// group must themselves be paired in DM with the same bot.
	IsPaired       bool      `json:"is_paired,omitempty"`
	PairedBotUUID  string    `json:"paired_bot_uuid,omitempty"`
	PairedSenderID string    `json:"paired_sender_id,omitempty"`
	PairedAt       time.Time `json:"paired_at,omitempty"`

	// Group-specific
	IsWhitelisted bool   `json:"is_whitelisted"`
	WhitelistedBy string `json:"whitelisted_by,omitempty"`

	// Bash state
	BashCwd string `json:"bash_cwd,omitempty"`

	// CurrentAgent is which agent is driving the chat ("tingly-box" or "claude").
	CurrentAgent string `json:"current_agent,omitempty"`

	// Chat-level settings
	Verbose *bool `json:"verbose,omitempty"` // Verbose mode: nil=use bot default, true=verbose, false=quiet

	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

Chat is all state associated with an IM chat (direct or group): which project it is bound to, whether it is paired or whitelisted, and which agent is currently driving it.

The type lives here rather than in internal/remote_control/bot because that package already imports this one; keeping the domain type on this side lets the GORM store implement bot.ChatStoreInterface without an import cycle. The bot package aliases it (type Chat = db.Chat), so callers are unaffected.

type HealthStatus

type HealthStatus struct {
	Healthy         bool              `json:"healthy"`
	TotalStores     int               `json:"total_stores"`
	HealthyStores   int               `json:"healthy_stores"`
	UnhealthyStores int               `json:"unhealthy_stores"`
	StoreStatus     map[string]string `json:"store_status"`
}

HealthStatus represents the health of all stores.

type ImBotSettingsRecord

type ImBotSettingsRecord struct {
	BotUUID       string `gorm:"primaryKey;column:bot_uuid"`
	Name          string `gorm:"column:name"`
	Platform      string `gorm:"column:platform;index:idx_platform"`
	AuthType      string `gorm:"column:auth_type"`
	AuthConfig    string `gorm:"column:auth_config;type:text"` // JSON string of auth map
	ProxyURL      string `gorm:"column:proxy_url"`
	ChatIDLock    string `gorm:"column:chat_id_lock"`
	BashAllowlist string `gorm:"column:bash_allowlist;type:text"` // JSON array string
	DefaultCwd    string `gorm:"column:default_cwd"`              // Default working directory
	DefaultAgent  string `gorm:"column:default_agent"`            // Default Agent UUID
	Enabled       bool   `gorm:"column:enabled;index:idx_enabled"`

	// Output behavior settings
	Debug   bool  `gorm:"column:debug;default:false"`  // Show message IDs in output
	Verbose *bool `gorm:"column:verbose;default:true"` // Send intermediate messages (nil = true)

	// SmartGuide model configuration (required for @tb agent)
	SmartGuideProvider string `gorm:"column:smartguide_provider"` // Provider UUID
	SmartGuideModel    string `gorm:"column:smartguide_model"`    // Model identifier

	// RequirePairing enforces TOFU pairing-code binding for direct messages.
	// Nil for legacy rows is treated as false (opt-in migration); the bot
	// create wizard sets this to true for newly created bots.
	RequirePairing *bool `gorm:"column:require_pairing"`

	// Scenarios is a JSON-encoded list of scenario bindings declaring
	// which Claude Code hook scenarios this bot serves and the IM target.
	// Schema: see internal/server/module/notify/binding.go ScenarioBinding.
	Scenarios string `gorm:"column:scenarios;type:text"`

	CreatedAt time.Time `gorm:"column:created_at"`
	UpdatedAt time.Time `gorm:"column:updated_at"`
}

ImBotSettingsRecord is the GORM model for persisting ImBot credentials

func (*ImBotSettingsRecord) GetVerbose

func (r *ImBotSettingsRecord) GetVerbose() bool

GetVerbose returns verbose setting with default true

func (ImBotSettingsRecord) TableName

func (ImBotSettingsRecord) TableName() string

TableName specifies the table name for GORM

type ImBotSettingsStore

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

ImBotSettingsStore persists ImBot settings in SQLite using GORM.

func NewImBotSettingsStore

func NewImBotSettingsStore(baseDir string) (*ImBotSettingsStore, error)

NewImBotSettingsStore creates or loads an ImBot settings store using SQLite database.

func (*ImBotSettingsStore) CreateSettings

func (s *ImBotSettingsStore) CreateSettings(settings Settings) (Settings, error)

CreateSettings creates a new ImBot configuration.

func (*ImBotSettingsStore) DeleteSettings

func (s *ImBotSettingsStore) DeleteSettings(uuid string) error

DeleteSettings deletes an ImBot configuration.

func (*ImBotSettingsStore) GetSettingsByUUID

func (s *ImBotSettingsStore) GetSettingsByUUID(uuid string) (Settings, error)

GetSettingsByUUID returns a single ImBot configuration by UUID.

func (*ImBotSettingsStore) ListEnabledSettings

func (s *ImBotSettingsStore) ListEnabledSettings() ([]Settings, error)

ListEnabledSettings returns all enabled ImBot configurations.

func (*ImBotSettingsStore) ListSettings

func (s *ImBotSettingsStore) ListSettings() ([]Settings, error)

ListSettings returns all ImBot configurations.

func (*ImBotSettingsStore) ToggleSettings

func (s *ImBotSettingsStore) ToggleSettings(uuid string) (bool, error)

ToggleSettings toggles the enabled status of an ImBot configuration.

func (*ImBotSettingsStore) UpdateSettings

func (s *ImBotSettingsStore) UpdateSettings(uuid string, settings Settings) error

UpdateSettings updates an existing ImBot configuration. Only updates fields that are non-zero/empty in the settings struct.

type ModelSource added in v0.260604.1

type ModelSource string

ModelSource identifies how a cached model list was obtained.

const (
	ModelSourceAPI      ModelSource = "api"
	ModelSourceTemplate ModelSource = "template"
)

type ModelStore

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

ModelStore persists provider model information in SQLite using GORM.

func NewModelStore

func NewModelStore(baseDir string) (*ModelStore, error)

NewModelStore creates or loads a model store using SQLite database.

func (*ModelStore) Close added in v0.260716.1

func (s *ModelStore) Close() error

Close releases the store's database connection. Safe to call more than once. Short-lived embedders (tests, harness environments) must close, or each instance leaks a SQLite handle for the process lifetime.

func (*ModelStore) GetAllModelRecords

func (ms *ModelStore) GetAllModelRecords() []ProviderModelRecord

GetAllModelRecords returns all provider records (with metadata)

func (*ModelStore) GetAllProviders

func (ms *ModelStore) GetAllProviders() []string

GetAllProviders returns all provider UUIDs that have models

func (*ModelStore) GetModelCount

func (ms *ModelStore) GetModelCount(providerUUID string) int

GetModelCount returns the number of models for a provider

func (*ModelStore) GetModels

func (ms *ModelStore) GetModels(providerUUID string, ttl time.Duration) []string

GetModels returns models for a provider by UUID. All records use the same TTL (1 hour), regardless of source. If multiple records exist (api + template), the most recently updated is returned.

func (*ModelStore) GetModelsBySource added in v0.260604.1

func (ms *ModelStore) GetModelsBySource(providerUUID string, source ModelSource, ttl time.Duration) []string

GetModelsBySource returns models for a provider by UUID, filtered by source. Records are only returned if they match the source AND are within the TTL.

func (*ModelStore) GetProviderInfo

func (ms *ModelStore) GetProviderInfo(providerUUID string) (apiBase string, lastUpdated string, exists bool)

GetProviderInfo returns basic info about a provider (apiBase, lastUpdated, exists)

func (*ModelStore) HasModels

func (ms *ModelStore) HasModels(providerUUID string) bool

HasModels checks if a provider has models

func (*ModelStore) RemoveProvider

func (ms *ModelStore) RemoveProvider(providerUUID string) error

RemoveProvider removes all models for a provider by UUID

func (*ModelStore) SaveModels

func (ms *ModelStore) SaveModels(provider *typ.Provider, models []string, source ModelSource) error

SaveModels saves models for a provider by UUID

type ProviderModelRecord

type ProviderModelRecord struct {
	ProviderUUID string      `gorm:"primaryKey;column:provider_uuid"`
	ProviderName string      `gorm:"column:provider_name;index"`
	APIBase      string      `gorm:"column:api_base"`
	Models       string      `gorm:"column:models;type:text"`
	Source       ModelSource `gorm:"column:source"`
	LastUpdated  time.Time   `gorm:"column:last_updated"`
	CreatedAt    time.Time   `gorm:"column:created_at"`
	UpdatedAt    time.Time   `gorm:"column:updated_at"`
}

ProviderModelRecord is the GORM model for persisting provider models

func (ProviderModelRecord) TableName

func (ProviderModelRecord) TableName() string

TableName specifies the table name for GORM

type ProviderRecord

type ProviderRecord struct {
	UUID     string `gorm:"primaryKey;column:uuid"`
	Name     string `gorm:"column:name;not null;index"`
	APIBase  string `gorm:"column:api_base;not null"`
	APIStyle string `gorm:"column:api_style;not null"`    // "openai" or "anthropic"
	AuthType string `gorm:"column:auth_type;not null"`    // "api_key", "oauth", or "vmodel"
	Source   string `gorm:"column:source;default:'user'"` // "user" (default) or "builtin"

	// Configuration fields
	NoKeyRequired bool   `gorm:"column:no_key_required;default:false"`
	Enabled       bool   `gorm:"column:enabled;default:true"`
	ProxyURL      string `gorm:"column:proxy_url"`
	Timeout       int64  `gorm:"column:timeout"`
	Tags          string `gorm:"column:tags;type:text"` // JSON array
	LastUpdated   string `gorm:"column:last_updated"`

	// Dual-mode optional fields. Independent of APIBase/APIStyle.
	APIBaseOpenAI    string `gorm:"column:api_base_openai"`
	APIBaseAnthropic string `gorm:"column:api_base_anthropic"`

	// OpenAIEndpointMode declares which OpenAI endpoints this provider exposes
	// ("", "chat", "responses", "both"). See ai.OpenAIEndpointMode.
	OpenAIEndpointMode string `gorm:"column:openai_endpoint_mode"`

	// Credential fields - stored with provider as a unit
	// For api_key auth: stores the API key
	// For oauth auth: stores OAuth access token
	Token             string `gorm:"column:token"`                        // API key or access token
	OAuthProviderType string `gorm:"column:oauth_provider_type"`          // For oauth: provider type
	OAuthUserID       string `gorm:"column:oauth_user_id"`                // For oauth: user ID
	OAuthRefreshToken string `gorm:"column:oauth_refresh_token"`          // For oauth: refresh token
	OAuthExpiresAt    string `gorm:"column:oauth_expires_at"`             // For oauth: token expiration (RFC3339)
	OAuthExtraFields  string `gorm:"column:oauth_extra_fields;type:text"` // For oauth: JSON

	// VModel-specific fields (only populated when AuthType == "vmodel")
	VModelDetail string `gorm:"column:vmodel_detail;type:text"` // JSON-encoded typ.VModelDetail

	// Credential holds multi-field credentials for non-bearer auth types
	// (aws_sigv4, azure_key, gcp_sa). JSON-encoded typ.CredentialBundle.
	// Empty for api_key/oauth/vmodel. Added additively; AutoMigrate creates
	// the column on existing databases with no backfill required.
	Credential string `gorm:"column:credential;type:text"`

	CreatedAt time.Time `gorm:"column:created_at"`
	UpdatedAt time.Time `gorm:"column:updated_at"`
}

ProviderRecord is the GORM model for persisting a complete provider This includes both configuration and credentials as one logical entity

func (ProviderRecord) TableName

func (ProviderRecord) TableName() string

TableName specifies the table name for GORM

type ProviderStore

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

ProviderStore manages providers as complete units (configuration + credentials)

func NewProviderStore

func NewProviderStore(baseDir string) (*ProviderStore, error)

NewProviderStore creates or loads a provider store using SQLite database.

func (*ProviderStore) Close

func (ps *ProviderStore) Close() error

Close closes the database connection

func (*ProviderStore) Count

func (ps *ProviderStore) Count() (int64, error)

Count returns the total number of providers

func (*ProviderStore) Delete

func (ps *ProviderStore) Delete(uuid string) error

Delete removes a provider by UUID

func (*ProviderStore) Exists

func (ps *ProviderStore) Exists(uuid string) bool

Exists checks if a provider exists by UUID

func (*ProviderStore) GetAccessToken

func (ps *ProviderStore) GetAccessToken(uuid string) (string, error)

GetAccessToken returns the access token for a provider (convenience method)

func (*ProviderStore) GetByName

func (ps *ProviderStore) GetByName(name string) (*typ.Provider, error)

GetByName returns a provider by name

func (*ProviderStore) GetByUUID

func (ps *ProviderStore) GetByUUID(uuid string) (*typ.Provider, error)

GetByUUID returns a provider by UUID

func (*ProviderStore) GetDB

func (ps *ProviderStore) GetDB() *gorm.DB

GetDB returns the underlying GORM DB instance (for testing/advanced usage)

func (*ProviderStore) IsOAuthExpired

func (ps *ProviderStore) IsOAuthExpired(uuid string) (bool, error)

IsOAuthExpired checks if the OAuth token for a provider is expired

func (*ProviderStore) List

func (ps *ProviderStore) List() ([]*typ.Provider, error)

List returns all providers

func (*ProviderStore) ListEnabled

func (ps *ProviderStore) ListEnabled() ([]*typ.Provider, error)

ListEnabled returns all enabled providers

func (*ProviderStore) ListOAuth

func (ps *ProviderStore) ListOAuth() ([]*typ.Provider, error)

ListOAuth returns all OAuth-enabled providers

func (*ProviderStore) Save

func (ps *ProviderStore) Save(provider *typ.Provider) error

Save saves a provider (create or update)

func (*ProviderStore) UpdateCredential

func (ps *ProviderStore) UpdateCredential(uuid string, token string, oauthDetail *typ.OAuthDetail) error

UpdateCredential updates only the credential fields of a provider

func (*ProviderStore) UpdateCredentialBundle added in v0.260531.1

func (ps *ProviderStore) UpdateCredentialBundle(uuid string, bundle *typ.CredentialBundle) error

UpdateCredentialBundle updates only the multi-field credential of a provider (auth types aws_sigv4, azure_key, gcp_sa). Added alongside UpdateCredential to avoid changing that method's signature and its existing callers.

func (*ProviderStore) UpdateOAuthAccessToken

func (ps *ProviderStore) UpdateOAuthAccessToken(uuid, accessToken string) error

UpdateOAuthAccessToken updates only the OAuth access token for a provider

type RemoteChatRecord added in v0.260801.1

type RemoteChatRecord struct {
	ChatID      string `gorm:"primaryKey;column:chat_id"`
	Platform    string `gorm:"column:platform"`
	ProjectPath string `gorm:"column:project_path"`
	OwnerID     string `gorm:"column:owner_id;index:idx_remote_chats_owner,priority:1"`

	// ProjectHistory is the MRU path list, JSON-encoded.
	ProjectHistory string `gorm:"column:project_history;type:text"`

	IsPaired       bool      `gorm:"column:is_paired"`
	PairedBotUUID  string    `gorm:"column:paired_bot_uuid;index:idx_remote_chats_paired_bot"`
	PairedSenderID string    `gorm:"column:paired_sender_id"`
	PairedAt       time.Time `gorm:"column:paired_at"`

	IsWhitelisted bool   `gorm:"column:is_whitelisted;index:idx_remote_chats_whitelisted"`
	WhitelistedBy string `gorm:"column:whitelisted_by"`

	BashCwd string `gorm:"column:bash_cwd"`

	CurrentAgent string `gorm:"column:current_agent"`

	Verbose *bool `gorm:"column:verbose"`

	CreatedAt time.Time `gorm:"column:created_at"`
	UpdatedAt time.Time `gorm:"column:updated_at"`
}

RemoteChatRecord is the GORM model behind Chat.

ProjectHistory stays a JSON column for now — splitting it into its own table is the next step (see .design/remote-storage.md P2); everything the product actually queries on (owner, whitelist, pairing) is a real column with an index.

func (RemoteChatRecord) TableName added in v0.260801.1

func (RemoteChatRecord) TableName() string

TableName specifies the table name for GORM.

type RemoteChatStore added in v0.260801.1

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

RemoteChatStore persists IM chat state in the shared SQLite database.

It replaces a JSON file that every bot loaded into its own memory and rewrote whole, which meant concurrent writers silently erased each other — in-process (fixed earlier by sharing one store) and across processes (only fixed by being here, on one WAL database with row-level updates).

func NewRemoteChatStore added in v0.260801.1

func NewRemoteChatStore(db *gorm.DB) *RemoteChatStore

NewRemoteChatStore builds a store over an existing DB handle. The handle is owned by the StoreManager; Close here does not close it.

func (*RemoteChatStore) AddToWhitelist added in v0.260801.1

func (s *RemoteChatStore) AddToWhitelist(chatID, platform, addedBy string) error

AddToWhitelist whitelists a chat, creating it if needed.

func (*RemoteChatStore) BindProject added in v0.260801.1

func (s *RemoteChatStore) BindProject(chatID, platform, projectPath, ownerID string) error

BindProject binds a project to a chat, creating the chat if needed, and pushes the path onto the chat's MRU history.

func (*RemoteChatStore) ClearPaired added in v0.260801.1

func (s *RemoteChatStore) ClearPaired(chatID string) error

ClearPaired removes any pairing recorded on the chat, preserving the rest of its state.

func (*RemoteChatStore) GetBashCwd added in v0.260801.1

func (s *RemoteChatStore) GetBashCwd(chatID string) (string, bool, error)

GetBashCwd retrieves the bash working directory for a chat.

func (*RemoteChatStore) GetChat added in v0.260801.1

func (s *RemoteChatStore) GetChat(chatID string) (*Chat, error)

GetChat retrieves a chat by ID. A missing chat is (nil, nil).

func (*RemoteChatStore) GetCurrentAgent added in v0.260801.1

func (s *RemoteChatStore) GetCurrentAgent(chatID string) (string, error)

GetCurrentAgent retrieves the chat's current agent, defaulting to Smart Guide as the entry point.

func (*RemoteChatStore) GetOrCreateChat added in v0.260801.1

func (s *RemoteChatStore) GetOrCreateChat(chatID, platform string) (*Chat, error)

GetOrCreateChat returns the chat, creating an empty one if it does not exist.

The table is keyed by chat_id alone (no platform dimension), so when an existing row's platform differs from the requested platform we refuse rather than silently returning (and later overwriting) another platform's chat. This is the guard against cross-platform chatID-string collisions leaking platform A's chat into platform B.

func (*RemoteChatStore) GetProjectPath added in v0.260801.1

func (s *RemoteChatStore) GetProjectPath(chatID string) (string, bool, error)

GetProjectPath retrieves the project path bound to a chat.

func (*RemoteChatStore) ImportChat added in v0.260801.1

func (s *RemoteChatStore) ImportChat(chat *Chat) error

ImportChat stores a chat exactly as given, without restamping UpdatedAt.

Migration needs this for the same reason sessions do: UpdatedAt orders ListChats, so stamping it on import would make every migrated chat look equally and freshly active in the bot's chat list.

func (*RemoteChatStore) IsChatPaired added in v0.260801.1

func (s *RemoteChatStore) IsChatPaired(chatID, botUUID string) bool

IsChatPaired reports whether the chat is paired with the given bot UUID.

func (*RemoteChatStore) IsWhitelisted added in v0.260801.1

func (s *RemoteChatStore) IsWhitelisted(chatID string) bool

IsWhitelisted reports whether a chat is whitelisted.

func (*RemoteChatStore) ListChatProjectPaths added in v0.260801.1

func (s *RemoteChatStore) ListChatProjectPaths(chatID string) ([]string, error)

ListChatProjectPaths returns the per-chat MRU list of project paths (newest first), falling back to [ProjectPath] for chats with no history yet.

func (*RemoteChatStore) ListChats added in v0.260801.1

func (s *RemoteChatStore) ListChats(platform string) ([]*Chat, error)

ListChats returns the chat records this bot can reach on platform — those whose Platform field is set AND matches. Empty/mismatched-platform records are dropped at the source (see bot.ChatStoreInterface.ListChats for why). Ordered newest-first by updated_at, with chat_id as a stable tiebreaker, so the most recently active chats surface at the top.

func (*RemoteChatStore) ListChatsByOwner added in v0.260801.1

func (s *RemoteChatStore) ListChatsByOwner(ownerID, platform string) ([]*Chat, error)

ListChatsByOwner lists a user's chats on a platform that have a project bound.

func (*RemoteChatStore) RemoveFromWhitelist added in v0.260801.1

func (s *RemoteChatStore) RemoveFromWhitelist(chatID string) error

RemoveFromWhitelist clears a chat's whitelist flag.

func (*RemoteChatStore) SetBashCwd added in v0.260801.1

func (s *RemoteChatStore) SetBashCwd(chatID, cwd string) error

SetBashCwd sets the bash working directory for a chat.

func (*RemoteChatStore) SetCurrentAgent added in v0.260801.1

func (s *RemoteChatStore) SetCurrentAgent(chatID, platform, agentType string) error

SetCurrentAgent sets the chat's current agent, creating the chat row if it doesn't exist yet — without the auto-create, handoff state was silently dropped for any chat that hadn't been bound or paired first.

func (*RemoteChatStore) SetPaired added in v0.260801.1

func (s *RemoteChatStore) SetPaired(chatID, platform, botUUID, senderID string) error

SetPaired marks a chat as paired with the given bot and sender, creating the chat if needed.

func (*RemoteChatStore) UpdateChat added in v0.260801.1

func (s *RemoteChatStore) UpdateChat(chatID string, fn func(*Chat)) error

UpdateChat applies fn to an existing chat inside a transaction, so a concurrent writer cannot interleave between the read and the write. A missing chat is a no-op, matching the store this replaces.

func (*RemoteChatStore) UpsertChat added in v0.260801.1

func (s *RemoteChatStore) UpsertChat(chat *Chat) error

UpsertChat creates or replaces a chat row.

type RemoteSessionRecord added in v0.260801.1

type RemoteSessionRecord struct {
	ID      string `gorm:"primaryKey;column:id"`
	ChatID  string `gorm:"column:chat_id;index:idx_remote_sessions_bind,priority:1;index:idx_remote_sessions_chat"`
	Agent   string `gorm:"column:agent;index:idx_remote_sessions_bind,priority:2"`
	Project string `gorm:"column:project;index:idx_remote_sessions_bind,priority:3"`
	Status  string `gorm:"column:status;index:idx_remote_sessions_status"`

	Request        string `gorm:"column:request;type:text"`
	Response       string `gorm:"column:response;type:text"`
	Error          string `gorm:"column:error;type:text"`
	PermissionMode string `gorm:"column:permission_mode"`

	CreatedAt    time.Time `gorm:"column:created_at"`
	LastActivity time.Time `gorm:"column:last_activity;index:idx_remote_sessions_bind,priority:4"`
	ExpiresAt    time.Time `gorm:"column:expires_at"`
}

RemoteSessionRecord is the GORM model for a remote-control execution session — one row per (chat, agent, project) conversation with an agent.

This is the session INDEX only — binding, status, timestamps. The message history lives beside it in an append-only transcript file per session (see session.Transcript): small bounded metadata belongs in a table where the composite index on (chat_id, agent, project, last_activity) turns FindByChatAgentProject into one lookup, while unbounded conversation text belongs in a file that appends in O(1) and never bloats the shared database. See .design/remote-storage.md.

func (RemoteSessionRecord) TableName added in v0.260801.1

func (RemoteSessionRecord) TableName() string

TableName specifies the table name for GORM.

type RemoteSessionStore added in v0.260801.1

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

RemoteSessionStore persists remote-control sessions across two media, chosen per access pattern rather than by preference:

  • the session INDEX (binding, status, timestamps) goes in the shared SQLite database. Not for lookup speed — the manager keeps live sessions in memory and scans that first — but because it is small mutable state that several processes write concurrently: a session's status turns over repeatedly, and the CLI and the server must not overwrite each other the way two holders of a JSON file did;
  • the message TRANSCRIPT goes in an append-only file per session, because it is write-once, read whole, never queried by content, and unbounded. See session.Transcript for the full argument.

func NewRemoteSessionStore added in v0.260801.1

func NewRemoteSessionStore(db *gorm.DB, transcript *session.Transcript) *RemoteSessionStore

NewRemoteSessionStore builds a store over an existing DB handle plus a transcript directory. The handle is owned by the StoreManager; Close here does not close it. A nil transcript simply drops history.

func (*RemoteSessionStore) AppendMessage added in v0.260801.1

func (s *RemoteSessionStore) AppendMessage(sessionID string, msg session.Message) error

AppendMessage adds one message to a session's transcript file.

func (*RemoteSessionStore) Delete added in v0.260801.1

func (s *RemoteSessionStore) Delete(sessionID string) error

Delete removes a session's index record and its transcript.

The row goes first: an orphaned transcript file is inert (nothing can reach it without an index entry) whereas an index entry whose transcript is gone would surface in listings as a session whose history silently vanished.

func (*RemoteSessionStore) FindByChatAgentProject added in v0.260801.1

func (s *RemoteSessionStore) FindByChatAgentProject(chatID, agent, project string) (*session.Session, error)

FindByChatAgentProject returns the most recently active non-terminal session bound to the tuple. Manager.FindBy consults its in-memory map first, so this is the cold path: a session written by another process, or one the manager has not loaded since restart.

func (*RemoteSessionStore) Get added in v0.260801.1

func (s *RemoteSessionStore) Get(sessionID string) (*session.Session, error)

Get retrieves a session's index record by ID. Messages are not loaded — call Messages when the transcript is actually needed. A missing session is (nil, nil), matching the store it replaces.

func (*RemoteSessionStore) Import added in v0.260801.1

func (s *RemoteSessionStore) Import(sess *session.Session) error

Import stores a session exactly as given, without stamping LastActivity.

Migration needs this: LastActivity orders FindByChatAgentProject and drives retention, so touching it on import would make every migrated session look like it was just active and reorder conversations that had been dormant for weeks.

func (*RemoteSessionStore) List added in v0.260801.1

func (s *RemoteSessionStore) List() []*session.Session

List returns the session index records worth warming the manager's in-memory map with. Two bounds matter here, both because this runs synchronously at startup: it must not read transcripts (that would pull every conversation ever held into memory), and it skips terminal sessions, which accumulate forever — Manager.Close leaves closed rows behind on purpose.

Skipping them changes nothing observable: Manager.FindBy ignores closed and expired sessions anyway, and GetOrLoad still fetches any session by id.

func (*RemoteSessionStore) ListByChat added in v0.260801.1

func (s *RemoteSessionStore) ListByChat(chatID string) ([]*session.Session, error)

ListByChat returns all sessions for a chat, newest activity first.

func (*RemoteSessionStore) Messages added in v0.260801.1

func (s *RemoteSessionStore) Messages(sessionID string) ([]session.Message, error)

Messages reads a session's transcript on demand.

func (*RemoteSessionStore) Set added in v0.260801.1

func (s *RemoteSessionStore) Set(sessionID string, sess *session.Session) error

Set upserts a session's index record.

This writes one row. It used to also reconcile the message rows — counting what was stored, appending only the new tail, trimming a shortened history — machinery that existed purely because the transcript was a table. With messages in an append-only file, AppendMessage stands on its own and this stays a single upsert.

type ServiceStatsRecord

type ServiceStatsRecord struct {
	// Composite primary key: provider + model (stats are global, not per-rule)
	Provider             string    `gorm:"primaryKey;column:provider"`
	Model                string    `gorm:"primaryKey;column:model"`
	ServiceID            string    `gorm:"column:service_id"`
	RequestCount         int64     `gorm:"column:request_count"`
	LastUsed             time.Time `gorm:"column:last_used"`
	WindowStart          time.Time `gorm:"column:window_start"`
	WindowRequestCount   int64     `gorm:"column:window_request_count"`
	WindowTokensConsumed int64     `gorm:"column:window_tokens_consumed"`
	WindowInputTokens    int64     `gorm:"column:window_input_tokens"`
	WindowOutputTokens   int64     `gorm:"column:window_output_tokens"`
	TimeWindow           int       `gorm:"column:time_window"`
}

ServiceStatsRecord is the GORM model for persisting service statistics

func (ServiceStatsRecord) TableName

func (ServiceStatsRecord) TableName() string

TableName specifies the table name for GORM

type Settings

type Settings struct {
	UUID          string            `json:"uuid,omitempty"`
	Name          string            `json:"name,omitempty"`
	Token         string            `json:"token,omitempty"` // Legacy: for backward compatibility
	Platform      string            `json:"platform"`
	AuthType      string            `json:"auth_type"`
	Auth          map[string]string `json:"auth"`
	ProxyURL      string            `json:"proxy_url,omitempty"`
	ChatIDLock    string            `json:"chat_id_lock,omitempty"` // Restriction on which chat is accepted, NOT a live chat id (see bot.BotSetting)
	BashAllowlist []string          `json:"bash_allowlist,omitempty"`
	DefaultCwd    string            `json:"default_cwd,omitempty"`   // Default working directory
	DefaultAgent  string            `json:"default_agent,omitempty"` // Default Agent UUID
	Enabled       bool              `json:"enabled"`
	// SmartGuide model configuration (required for @tb agent)
	SmartGuideProvider string `json:"smartguide_provider,omitempty"` // Provider UUID
	SmartGuideModel    string `json:"smartguide_model,omitempty"`    // Model identifier
	// RequirePairing enforces TOFU pairing for DMs. Nil = legacy/opt-in.
	RequirePairing *bool `json:"require_pairing,omitempty"`
	// Scenarios is the raw JSON-encoded list of hook scenarios this bot
	// serves. The notify module parses it into typed bindings; the
	// settings store keeps it opaque to avoid a cross-package dependency.
	Scenarios string    `json:"scenarios,omitempty"`
	CreatedAt time.Time `json:"created_at,omitempty"`
	UpdatedAt time.Time `json:"updated_at,omitempty"`
}

Settings represents ImBot configuration (exported for use by remote_coder module)

type StatsStore

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

StatsStore persists service usage statistics in SQLite using GORM.

func NewStatsStore

func NewStatsStore(baseDir string) (*StatsStore, error)

NewStatsStore creates or loads a stats store using SQLite database.

func (*StatsStore) ClearAll

func (ss *StatsStore) ClearAll() error

ClearAll removes all persisted stats.

func (*StatsStore) ClearService added in v0.260709.1

func (ss *StatsStore) ClearService(provider, model string) error

ClearService removes persisted stats for a single provider:model. No error if no rows matched (the service simply had no recorded stats).

func (*StatsStore) Get

func (ss *StatsStore) Get(provider, model string) (loadbalance.ServiceStats, bool)

Get returns stats for a specific provider/model combination.

func (*StatsStore) HydrateRules

func (ss *StatsStore) HydrateRules(rules []typ.Rule) error

HydrateRules injects stored stats into the provided rules and initializes missing entries.

func (*StatsStore) RecordUsage

func (ss *StatsStore) RecordUsage(service *loadbalance.Service, inputTokens, outputTokens int) (loadbalance.ServiceStats, error)

RecordUsage records usage for a service and persists the updated stats.

func (*StatsStore) ServiceKey

func (ss *StatsStore) ServiceKey(provider, model string) string

ServiceKey builds a unique key for a provider/model combination.

func (*StatsStore) Snapshot

func (ss *StatsStore) Snapshot() map[string]loadbalance.ServiceStats

Snapshot returns a copy of all stats keyed by provider:model.

func (*StatsStore) UpdateFromService

func (ss *StatsStore) UpdateFromService(service *loadbalance.Service) error

UpdateFromService stores the current stats from a service into the store.

type StoreManager

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

StoreManager manages all database stores with a shared GORM DB instance. It provides unified initialization, thread-safe access, and lifecycle management.

func NewStoreManager

func NewStoreManager(baseDir string) (*StoreManager, error)

NewStoreManager creates a new StoreManager and initializes all stores. It opens a single SQLite database connection shared by all stores.

Parameters:

baseDir - Base directory for database storage

Returns:

*StoreManager - Initialized store manager
error - Error if any store fails to initialize

func NewStoreManagerWithConfig

func NewStoreManagerWithConfig(config StoreManagerConfig) (*StoreManager, error)

NewStoreManagerWithConfig creates a StoreManager with custom configuration.

func (*StoreManager) APIToken added in v0.260418.2200

func (sm *StoreManager) APIToken() *APITokenStore

APIToken returns the APITokenStore (thread-safe). Returns nil if the store is not initialized or after Close() has been called.

func (*StoreManager) BaseDir

func (sm *StoreManager) BaseDir() string

BaseDir returns the base directory for this StoreManager.

func (*StoreManager) Close

func (sm *StoreManager) Close() error

Close closes all database connections and cleans up resources. After Close() is called, all accessor methods will return nil.

func (*StoreManager) HealthCheck

func (sm *StoreManager) HealthCheck() (*HealthStatus, error)

HealthCheck checks the health of all stores. Returns a HealthStatus with the state of each store.

func (*StoreManager) ImBotSettings

func (sm *StoreManager) ImBotSettings() *ImBotSettingsStore

ImBotSettings returns the ImBotSettingsStore (thread-safe). Returns nil if the store is not initialized or after Close() has been called.

func (*StoreManager) Model

func (sm *StoreManager) Model() *ModelStore

Model returns the ModelStore (thread-safe). Returns nil if the store is not initialized or after Close() has been called.

func (*StoreManager) Provider

func (sm *StoreManager) Provider() *ProviderStore

Provider returns the ProviderStore (thread-safe). Returns nil if the store is not initialized or after Close() has been called.

func (*StoreManager) RemoteChats added in v0.260801.1

func (sm *StoreManager) RemoteChats() *RemoteChatStore

RemoteChats returns the RemoteChatStore (thread-safe). Returns nil if the store is not initialized or after Close() has been called.

func (*StoreManager) RemoteSessions added in v0.260801.1

func (sm *StoreManager) RemoteSessions() *RemoteSessionStore

RemoteSessions returns the RemoteSessionStore (thread-safe). Returns nil if the store is not initialized or after Close() has been called.

func (*StoreManager) Stats

func (sm *StoreManager) Stats() *StatsStore

Stats returns the StatsStore (thread-safe). Returns nil if the store is not initialized or after Close() has been called.

func (*StoreManager) Tasks added in v0.260507.1

func (sm *StoreManager) Tasks() *TaskStore

Tasks returns the TaskStore (thread-safe). Returns nil if the store is not initialized or after Close() has been called.

func (*StoreManager) ToolConfig

func (sm *StoreManager) ToolConfig() *ToolConfigStore

ToolConfig returns the ToolConfigStore (thread-safe). Returns nil if the store is not initialized or after Close() has been called.

func (*StoreManager) Usage

func (sm *StoreManager) Usage() *UsageStore

Usage returns the UsageStore (thread-safe). Returns nil if the store is not initialized or after Close() has been called.

type StoreManagerConfig

type StoreManagerConfig struct {
	BaseDir     string
	BusyTimeout int // Milliseconds, default 5000
}

StoreManagerConfig holds configuration for StoreManager initialization.

type TaskRecord added in v0.260507.1

type TaskRecord struct {
	ID               uint       `gorm:"primaryKey;autoIncrement;column:id"`
	TaskID           string     `gorm:"uniqueIndex;column:task_id;not null;size:64"`
	Type             string     `gorm:"column:type;not null;size:128"`
	Status           string     `gorm:"column:status;not null;size:32;index:idx_tasks_status_scheduled"`
	OwnerType        string     `gorm:"column:owner_type;size:64;index:idx_tasks_owner"`
	OwnerID          string     `gorm:"column:owner_id;size:64;index:idx_tasks_owner"`
	Source           string     `gorm:"column:source;size:64"`
	SerializationKey string     `gorm:"column:serialization_key;size:256;index:idx_tasks_key_status"`
	Payload          string     `gorm:"column:payload;type:text"`
	Result           string     `gorm:"column:result;type:text"`
	Progress         string     `gorm:"column:progress;size:512"`
	Error            string     `gorm:"column:error;type:text"`
	Attempt          int        `gorm:"column:attempt;default:0"`
	MaxAttempts      int        `gorm:"column:max_attempts;default:1"`
	ScheduledAt      *time.Time `gorm:"column:scheduled_at;index:idx_tasks_status_scheduled"`
	StartedAt        *time.Time `gorm:"column:started_at"`
	FinishedAt       *time.Time `gorm:"column:finished_at"`
	CancelledAt      *time.Time `gorm:"column:cancelled_at"`
	// Recurrence and ParentTaskID are reserved for Phase 4 (recurring tasks).
	Recurrence   string    `gorm:"column:recurrence;type:text"`
	ParentTaskID string    `gorm:"column:parent_task_id;size:64"`
	CreatedAt    time.Time `gorm:"column:created_at;index:idx_tasks_owner;index:idx_tasks_key_status"`
	UpdatedAt    time.Time `gorm:"column:updated_at"`
}

TaskRecord is the GORM model for the tasks table.

func (TaskRecord) TableName added in v0.260507.1

func (TaskRecord) TableName() string

type TaskStore added in v0.260507.1

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

TaskStore persists and retrieves tasks using a shared GORM DB.

func (*TaskStore) Create added in v0.260507.1

func (s *TaskStore) Create(ctx context.Context, t *task.Task) error

func (*TaskStore) FindDueTasks added in v0.260507.1

func (s *TaskStore) FindDueTasks(ctx context.Context, now time.Time, limit int) ([]task.Task, error)

func (*TaskStore) FindQueuedForKey added in v0.260507.1

func (s *TaskStore) FindQueuedForKey(ctx context.Context, key string) (*task.Task, error)

func (*TaskStore) Get added in v0.260507.1

func (s *TaskStore) Get(ctx context.Context, taskID string) (*task.Task, error)

func (*TaskStore) List added in v0.260507.1

func (s *TaskStore) List(ctx context.Context, filter task.ListFilter) ([]task.Task, error)

func (*TaskStore) MarkInterruptedOnStartup added in v0.260507.1

func (s *TaskStore) MarkInterruptedOnStartup(ctx context.Context) error

func (*TaskStore) Update added in v0.260507.1

func (s *TaskStore) Update(ctx context.Context, t *task.Task) error

func (*TaskStore) UpdateStatus added in v0.260507.1

func (s *TaskStore) UpdateStatus(ctx context.Context, taskID string, fields map[string]interface{}) error

type TimeSeriesData

type TimeSeriesData struct {
	Timestamp        string  `json:"timestamp"`
	RequestCount     int64   `json:"request_count"`
	TotalTokens      int64   `json:"total_tokens"`
	InputTokens      int64   `json:"input_tokens"`
	OutputTokens     int64   `json:"output_tokens"`
	CacheReadTokens  int64   `json:"cache_read_tokens"`
	CacheWriteTokens int64   `json:"cache_write_tokens"`
	SystemTokens     int64   `json:"system_tokens"`
	ErrorCount       int64   `json:"error_count"`
	AvgLatencyMs     float64 `json:"avg_latency_ms"`
}

TimeSeriesData represents a single time bucket in time series data

type ToolConfigRecord

type ToolConfigRecord struct {
	UUID         string    `gorm:"primaryKey;column:uuid"`
	ProviderUUID string    `gorm:"column:provider_uuid;not null;index:idx_provider_uuid"`
	ToolType     string    `gorm:"column:tool_type;not null;index:idx_tool_type"`
	ConfigJSON   string    `gorm:"column:config_json;type:text"`
	Disabled     bool      `gorm:"column:disabled;default:false"`
	CreatedAt    time.Time `gorm:"column:created_at"`
	UpdatedAt    time.Time `gorm:"column:updated_at"`
}

ToolConfigRecord is the GORM model for persisting provider-specific tool configurations

func (ToolConfigRecord) TableName

func (ToolConfigRecord) TableName() string

TableName specifies the table name for GORM

type ToolConfigStore

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

ToolConfigStore manages provider-specific tool configurations

func NewToolConfigStore

func NewToolConfigStore(baseDir string) (*ToolConfigStore, error)

NewToolConfigStore creates or loads a tool config store using SQLite database. It uses the same database file as the provider store.

func (*ToolConfigStore) Close

func (tcs *ToolConfigStore) Close() error

Close closes the database connection

func (*ToolConfigStore) Delete

func (tcs *ToolConfigStore) Delete(uuid string) error

Delete removes a tool config by UUID

func (*ToolConfigStore) DeleteByProvider

func (tcs *ToolConfigStore) DeleteByProvider(providerUUID string) error

DeleteByProvider removes all tool configs for a provider

func (*ToolConfigStore) GetByProvider

func (tcs *ToolConfigStore) GetByProvider(providerUUID string) ([]*ToolConfigRecord, error)

GetByProvider returns all tool configs for a provider

func (*ToolConfigStore) GetByProviderAndType

func (tcs *ToolConfigStore) GetByProviderAndType(providerUUID string, toolType string) (*ToolConfigRecord, error)

GetByProviderAndType returns a tool config by provider UUID and tool type

func (*ToolConfigStore) GetDB

func (tcs *ToolConfigStore) GetDB() *gorm.DB

GetDB returns the underlying GORM DB instance (for testing/advanced usage)

func (*ToolConfigStore) GetToolConfig

func (tcs *ToolConfigStore) GetToolConfig(providerUUID, toolType string, target interface{}) (disabled, found bool, err error)

GetToolConfig returns the config for a specific provider and tool type (generic) target is a pointer to the config struct to unmarshal into Returns (disabled, found, error) - disabled: true if the tool is explicitly disabled for this provider - found: true if a config record exists for this provider/tool type - error: any unmarshaling error

func (*ToolConfigStore) Save

func (tcs *ToolConfigStore) Save(config *ToolConfigRecord) error

Save saves a tool config (create or update)

func (*ToolConfigStore) SetToolConfig

func (tcs *ToolConfigStore) SetToolConfig(providerUUID, toolType string, config interface{}, disabled bool) (string, error)

SetToolConfig sets the config for a specific provider and tool type (generic) config is any struct that can be marshaled to JSON Returns the UUID of the created/updated record

type UsageDailyRecord

type UsageDailyRecord struct {
	ID           uint   `gorm:"primaryKey;autoIncrement;column:id"`
	Date         string `gorm:"column:date;index:idx_date;uniqueIndex:uq_daily_dim,priority:1;not null"` // YYYY-MM-DD (UTC)
	ProviderUUID string `gorm:"column:provider_uuid;uniqueIndex:uq_daily_dim,priority:2;not null"`
	ProviderName string `gorm:"column:provider_name;not null"`
	Model        string `gorm:"column:model;uniqueIndex:uq_daily_dim,priority:3;not null"`
	UserID       string `gorm:"column:user_id;uniqueIndex:uq_daily_dim,priority:4;not null;default:''"`
	RequestCount int64  `gorm:"column:request_count;not null"`
	TotalTokens  int64  `gorm:"column:total_tokens;not null"`
	InputTokens  int64  `gorm:"column:input_tokens;not null"`
	OutputTokens int64  `gorm:"column:output_tokens;not null"`
	// Cache tokens: reads, then writes (a subset of InputTokens)
	CacheReadTokens  int64 `gorm:"column:cache_input_tokens;default:0"`
	CacheWriteTokens int64 `gorm:"column:cache_write_tokens;default:0"`
	// System tokens
	SystemTokens  int64 `gorm:"column:system_tokens;default:0"`
	ErrorCount    int64 `gorm:"column:error_count;default:0"`
	StreamedCount int64 `gorm:"column:streamed_count;default:0"`
	// Sum of latency_ms across the day, so merged averages stay weighted
	LatencySumMs int64 `gorm:"column:latency_sum_ms;default:0"`
}

UsageDailyRecord is the GORM model for daily aggregated usage statistics. One row per (UTC day, provider, model, user). Date uses the same day boundary as SQLite's date(timestamp) so daily rows can substitute raw usage_records scans for completed days.

func (UsageDailyRecord) TableName

func (UsageDailyRecord) TableName() string

TableName specifies the table name for GORM

type UsageMonthlyRecord

type UsageMonthlyRecord struct {
	ID           uint   `gorm:"primaryKey;autoIncrement;column:id"`
	Year         int    `gorm:"column:year;not null"`
	Month        int    `gorm:"column:month;not null"`
	ProviderUUID string `gorm:"column:provider_uuid;not null"`
	ProviderName string `gorm:"column:provider_name;not null"`
	Model        string `gorm:"column:model;not null"`
	RequestCount int64  `gorm:"column:request_count;not null"`
	TotalTokens  int64  `gorm:"column:total_tokens;not null"`
	InputTokens  int64  `gorm:"column:input_tokens;not null"`
	OutputTokens int64  `gorm:"column:output_tokens;not null"`
	// Cache tokens: reads, then writes (a subset of InputTokens)
	CacheReadTokens  int64 `gorm:"column:cache_input_tokens;default:0"`
	CacheWriteTokens int64 `gorm:"column:cache_write_tokens;default:0"`
	// System tokens
	SystemTokens int64 `gorm:"column:system_tokens;default:0"`
	ErrorCount   int64 `gorm:"column:error_count;default:0"`
}

UsageMonthlyRecord is the GORM model for monthly aggregated usage statistics

func (UsageMonthlyRecord) TableName

func (UsageMonthlyRecord) TableName() string

TableName specifies the table name for GORM

type UsageRecord

type UsageRecord struct {
	ID           uint      `gorm:"primaryKey;autoIncrement;column:id"`
	ProviderUUID string    `gorm:"column:provider_uuid;index:idx_provider_model;not null"`
	ProviderName string    `gorm:"column:provider_name;not null"`
	Model        string    `gorm:"column:model;index:idx_provider_model;not null"`
	Scenario     string    `gorm:"column:scenario;index:idx_scenario;not null"`
	RuleUUID     string    `gorm:"column:rule_uuid;index:idx_rule"`
	UserID       string    `gorm:"column:user_id;index:idx_user;not null;default:''"`
	RequestModel string    `gorm:"column:request_model"`
	Timestamp    time.Time `gorm:"column:timestamp;index:idx_timestamp;index:idx_timestamp_scenario;not null"`
	InputTokens  int       `gorm:"column:input_tokens;not null"`
	OutputTokens int       `gorm:"column:output_tokens;not null"`
	TotalTokens  int       `gorm:"column:total_tokens;index;not null"`
	// CacheReadTokens counts cache-READ hits only. Cache writes are billed
	// separately (Anthropic cache_creation, OpenAI cache_write_tokens since
	// gpt-5.6) and are counted in CacheWriteTokens — which is a SUBSET of
	// InputTokens, not an addition to it.
	//
	// The physical column is still cache_input_tokens: renaming it would mean
	// an ALTER on three tables for no behavioral gain, so the legacy name is
	// pinned in the tag and the mismatch stops at this line. Raw SQL selects
	// alias it (`SUM(cache_input_tokens) as cache_read_tokens`) so Scan binds.
	CacheReadTokens  int `gorm:"column:cache_input_tokens;default:0"`
	CacheWriteTokens int `gorm:"column:cache_write_tokens;default:0"`
	// System tokens (framework overhead, templates, etc.)
	SystemTokens int    `gorm:"column:system_tokens;default:0"`
	Status       string `gorm:"column:status;index;not null"` // success, error, partial
	ErrorCode    string `gorm:"column:error_code"`
	LatencyMs    int    `gorm:"column:latency_ms"`
	TTFTMs       int    `gorm:"column:ttft_ms;default:0"`
	Streamed     bool   `gorm:"column:streamed;type:integer"`
}

UsageRecord is the GORM model for persisting individual usage records

func (UsageRecord) TableName

func (UsageRecord) TableName() string

TableName specifies the table name for GORM

type UsageStatsQuery

type UsageStatsQuery struct {
	GroupBy   string // model, provider, scenario, rule, user, daily, hourly
	StartTime time.Time
	EndTime   time.Time
	Provider  string
	Model     string
	Scenario  string
	RuleUUID  string
	UserID    string
	Status    string
	Limit     int
	SortBy    string // total_tokens, request_count, avg_latency
	SortOrder string // asc, desc
}

GetAggregatedStats returns aggregated usage statistics based on query parameters

type UsageStore

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

UsageStore persists usage records in SQLite using GORM.

func NewUsageStore

func NewUsageStore(baseDir string) (*UsageStore, error)

NewUsageStore creates or loads a usage store using SQLite database.

func (*UsageStore) AggregateToDaily

func (us *UsageStore) AggregateToDaily(date time.Time) (int64, error)

AggregateToDaily (re)builds the usage_daily rows for the UTC day containing the given time. It returns the number of aggregate rows written.

func (*UsageStore) DeleteOlderThan

func (us *UsageStore) DeleteOlderThan(cutoffDate time.Time) (int64, error)

DeleteOlderThan deletes records older than the specified date, together with the daily aggregates derived from them so both views stay consistent.

func (*UsageStore) GetAggregatedStats

func (us *UsageStore) GetAggregatedStats(query UsageStatsQuery) ([]AggregatedStat, error)

GetAggregatedStats returns aggregated statistics. For queries spanning several completed days it combines the usage_daily pre-aggregation table with a raw scan of only the partial edge days (see usage_daily.go), so dashboard loads stay fast regardless of how many raw records accumulate.

func (*UsageStore) GetRecords

func (us *UsageStore) GetRecords(startTime, endTime time.Time, filters map[string]string, limit, offset int) ([]UsageRecord, int64, error)

GetRecords returns individual usage records (for debugging/audit)

func (*UsageStore) GetRecordsAfterID

func (us *UsageStore) GetRecordsAfterID(lastID uint, startTime time.Time, limit int) ([]UsageRecord, error)

GetRecordsAfterID returns usage records with id greater than lastID. On initial sync, startTime can be used to cap the historical backfill window.

func (*UsageStore) GetTimeSeries

func (us *UsageStore) GetTimeSeries(interval string, startTime, endTime time.Time, filters map[string]string) ([]TimeSeriesData, error)

GetTimeSeries returns time-series data for usage. Day-interval queries spanning several completed days are served from usage_daily with raw scans only for the partial edge days (see usage_daily.go).

func (*UsageStore) RecordUsage

func (us *UsageStore) RecordUsage(record *UsageRecord) error

RecordUsage records a single usage event

func (*UsageStore) RenameRuleUUID added in v0.260611.1

func (us *UsageStore) RenameRuleUUID(oldUUID, newUUID string) error

RenameRuleUUID re-attributes historical usage records from oldUUID to newUUID so per-rule usage stats survive a rule UUID normalization.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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