server

package
v0.392.931446400 Latest Latest
Warning

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

Go to latest
Published: Apr 1, 2026 License: Apache-2.0 Imports: 65 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var DBPath string

DBPath is the path to the percy database, set at startup

Functions

func ExtractDisplayData

func ExtractDisplayData(message llm.Message) interface{}

ExtractDisplayData extracts display data from message content for storage

func GenerateSubagentSystemPrompt

func GenerateSubagentSystemPrompt(workingDir string) (string, error)

GenerateSubagentSystemPrompt generates a minimal system prompt for subagent conversations.

func GenerateSystemPrompt

func GenerateSystemPrompt(workingDir string) (string, error)

GenerateSystemPrompt generates the system prompt using the embedded template. If workingDir is empty, it uses the current working directory.

func IsAutogeneratedFile

func IsAutogeneratedFile(path string, content []byte) bool

IsAutogeneratedFile reports whether a file is autogenerated based on its path and content. For Go files, it also analyzes the content for autogeneration markers.

func IsAutogeneratedPath

func IsAutogeneratedPath(path string) bool

IsAutogeneratedPath reports whether a file path suggests it's autogenerated. This checks for common autogenerated file patterns based on path alone.

func LoggerMiddleware

func LoggerMiddleware(logger *slog.Logger) func(http.Handler) http.Handler

LoggerMiddleware adds request logging using slog-http

func RequireHeaderMiddleware

func RequireHeaderMiddleware(headerName string) func(http.Handler) http.Handler

RequireHeaderMiddleware requires a specific header to be present on all API requests. This is used to ensure requests come through an authenticated proxy.

Types

type APIMessage

type APIMessage struct {
	MessageID      string    `json:"message_id"`
	ConversationID string    `json:"conversation_id"`
	SequenceID     int64     `json:"sequence_id"`
	Type           string    `json:"type"`
	LlmData        *string   `json:"llm_data,omitempty"`
	UserData       *string   `json:"user_data,omitempty"`
	UsageData      *string   `json:"usage_data,omitempty"`
	CreatedAt      time.Time `json:"created_at"`
	DisplayData    *string   `json:"display_data,omitempty"`
	EndOfTurn      *bool     `json:"end_of_turn,omitempty"`
}

APIMessage is the message format sent to clients TODO: We could maybe omit llm_data when display_data is available

type ChannelTypeInfo

type ChannelTypeInfo struct {
	Type         string        `json:"type"`
	Label        string        `json:"label"`
	ConfigFields []ConfigField `json:"config_fields"`
}

type ChatRequest

type ChatRequest struct {
	Message string `json:"message"`
	Model   string `json:"model,omitempty"`
	Cwd     string `json:"cwd,omitempty"`
}

ChatRequest represents a chat message from the user

type CodebaseInfo

type CodebaseInfo struct {
	InjectFiles        []string
	InjectFileContents map[string]string
	GuidanceFiles      []string
}

type CommitInfo

type CommitInfo struct {
	SHA     string    `json:"sha"`
	Message string    `json:"message"`
	Author  string    `json:"author"`
	Date    time.Time `json:"date"`
}

CommitInfo represents a commit in the changelog.

type ConfigField

type ConfigField struct {
	Name        string `json:"name"`
	Label       string `json:"label"`
	Type        string `json:"type"`
	Required    bool   `json:"required"`
	Placeholder string `json:"placeholder,omitempty"`
}

type ContinueConversationRequest

type ContinueConversationRequest struct {
	SourceConversationID string `json:"source_conversation_id"`
	Model                string `json:"model,omitempty"`
	Cwd                  string `json:"cwd,omitempty"`
}

ContinueConversationRequest represents the request to continue a conversation in a new one

type ConversationListUpdate

type ConversationListUpdate struct {
	Type           string                  `json:"type"` // "update", "delete"
	Conversation   *generated.Conversation `json:"conversation,omitempty"`
	ConversationID string                  `json:"conversation_id,omitempty"` // For deletes
}

ConversationListUpdate represents an update to the conversation list

type ConversationManager

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

ConversationManager manages a single active conversation

func NewConversationManager

func NewConversationManager(conversationID string, database *db.DB, memoryDB *memory.DB, embedder memory.Embedder, baseLogger *slog.Logger, toolSetConfig claudetool.ToolSetConfig, recordMessage loop.MessageRecordFunc, onStateChange func(ConversationState), onConversationDone func(string)) *ConversationManager

NewConversationManager constructs a manager with dependencies but defers hydration until needed.

func (*ConversationManager) AcceptUserMessage

func (cm *ConversationManager) AcceptUserMessage(ctx context.Context, service llm.Service, modelID string, message llm.Message) (bool, error)

AcceptUserMessage enqueues a user message, ensuring the loop is ready first. The message is recorded to the database immediately so it appears in the UI, even if the loop is busy processing a previous request.

func (*ConversationManager) CancelConversation

func (cm *ConversationManager) CancelConversation(ctx context.Context) error

CancelConversation cancels the current conversation loop and records a cancelled tool result if a tool was in progress

func (*ConversationManager) GetModel

func (cm *ConversationManager) GetModel() string

GetModel returns the model ID used by this conversation.

func (*ConversationManager) Hydrate

func (cm *ConversationManager) Hydrate(ctx context.Context) error

Hydrate loads conversation metadata from the database and generates a system prompt if one doesn't exist yet. It does NOT cache the message history; ensureLoop reads messages fresh from the DB when creating a loop so that any messages added asynchronously (e.g. distillation) are always included.

func (*ConversationManager) IsAgentWorking

func (cm *ConversationManager) IsAgentWorking() bool

IsAgentWorking returns the current agent working state.

func (*ConversationManager) ResetLoop added in v0.347.976101525

func (cm *ConversationManager) ResetLoop()

ResetLoop nils out the loop so the next AcceptUserMessage recreates it with fresh history from the database. Use after deleting messages.

func (*ConversationManager) SetAgentWorking

func (cm *ConversationManager) SetAgentWorking(working bool)

SetAgentWorking updates the agent working state and notifies the server to broadcast.

func (*ConversationManager) SwitchModel added in v0.381.91062602

func (cm *ConversationManager) SwitchModel(ctx context.Context, service llm.Service, modelID string, cancelCurrentTurn bool) error

func (*ConversationManager) Touch

func (cm *ConversationManager) Touch()

Touch updates last activity timestamp.

type ConversationState

type ConversationState struct {
	ConversationID string `json:"conversation_id"`
	Working        bool   `json:"working"`
	Model          string `json:"model,omitempty"`
}

ConversationState represents the current state of a conversation. This is broadcast to all subscribers whenever the state changes.

type ConversationWithState

type ConversationWithState struct {
	generated.Conversation
	Working bool `json:"working"`
}

ConversationWithState combines a conversation with its working state.

type CreateModelRequest

type CreateModelRequest struct {
	DisplayName  string `json:"display_name"`
	ProviderType string `json:"provider_type"`
	Endpoint     string `json:"endpoint"`
	APIKey       string `json:"api_key"`
	ModelName    string `json:"model_name"`
	MaxTokens    int64  `json:"max_tokens"`
	Tags         string `json:"tags"` // Comma-separated tags
}

CreateModelRequest is the request body for creating a model

type CreateNotificationChannelRequest

type CreateNotificationChannelRequest struct {
	ChannelType string `json:"channel_type"`
	DisplayName string `json:"display_name"`
	Enabled     bool   `json:"enabled"`
	Config      any    `json:"config"`
}

type DirectoryEntry

type DirectoryEntry struct {
	Name           string `json:"name"`
	IsDir          bool   `json:"is_dir"`
	GitHeadSubject string `json:"git_head_subject,omitempty"`
}

DirectoryEntry represents a single directory entry for the directory picker

type DuplicateModelRequest

type DuplicateModelRequest struct {
	DisplayName string `json:"display_name,omitempty"`
}

DuplicateModelRequest allows overriding fields when duplicating

type EditMessageRequest added in v0.347.976101525

type EditMessageRequest struct {
	SequenceID int64  `json:"sequence_id"`
	Message    string `json:"message"`
}

EditMessageRequest is the request body for editing a message.

type ExecMessage

type ExecMessage struct {
	Type string `json:"type"`
	Data string `json:"data,omitempty"`
	Cols uint16 `json:"cols,omitempty"`
	Rows uint16 `json:"rows,omitempty"`
}

ExecMessage is the message format for terminal websocket communication

type ForkConversationRequest added in v0.347.976101525

type ForkConversationRequest struct {
	SourceConversationID string `json:"source_conversation_id"`
	AtSequenceID         int64  `json:"at_sequence_id"`
	Model                string `json:"model,omitempty"`
}

ForkConversationRequest is the request body for forking a conversation.

type GitDiffInfo

type GitDiffInfo struct {
	ID         string    `json:"id"`
	Message    string    `json:"message"`
	Author     string    `json:"author"`
	Timestamp  time.Time `json:"timestamp"`
	FilesCount int       `json:"filesCount"`
	Additions  int       `json:"additions"`
	Deletions  int       `json:"deletions"`
}

GitDiffInfo represents a commit or working changes

type GitFileDiff

type GitFileDiff struct {
	Path       string `json:"path"`
	OldContent string `json:"oldContent"`
	NewContent string `json:"newContent"`
}

GitFileDiff represents the content of a file diff

type GitFileInfo

type GitFileInfo struct {
	Path        string `json:"path"`
	Status      string `json:"status"` // added, modified, deleted
	Additions   int    `json:"additions"`
	Deletions   int    `json:"deletions"`
	IsGenerated bool   `json:"isGenerated"`
}

GitFileInfo represents a file in a diff

type GitInfo

type GitInfo struct {
	Root string
}

type GitInfoUserData

type GitInfoUserData struct {
	Worktree string `json:"worktree"`
	Branch   string `json:"branch"`
	Commit   string `json:"commit"`
	Subject  string `json:"subject"`
	Text     string `json:"text"` // Human-readable description
}

GitInfoUserData is the structured data stored in user_data for gitinfo messages.

type LLMConfig

type LLMConfig struct {
	// API keys for each provider
	AnthropicAPIKey string
	OpenAIAPIKey    string
	GeminiAPIKey    string
	FireworksAPIKey string

	// Gateway is the base URL of the LLM gateway (optional)
	Gateway string

	// TerminalURL is the URL to the terminal interface (optional)
	TerminalURL string

	// DefaultModel is the default model to use (optional, defaults to models.Default())
	DefaultModel string

	// Links are custom links to be displayed in the UI (optional)
	Links []Link

	// NotificationChannels is a list of notification channel configs from percy.json.
	// Each entry is a map with at least a "type" key, plus channel-specific fields.
	NotificationChannels []map[string]any

	// OllamaURL is the base URL of a local Ollama instance for auto-discovery.
	// Default: "http://localhost:11434". Set to "" to disable.
	OllamaURL string

	// DB is the database for recording LLM requests (optional)
	DB *db.DB

	Logger *slog.Logger
}

LLMConfig holds all configuration for LLM services

type LLMProvider

type LLMProvider interface {
	GetService(modelID string) (llm.Service, error)
	GetAvailableModels() []string
	HasModel(modelID string) bool
	GetModelInfo(modelID string) *models.ModelInfo
	RefreshCustomModels() error
}

LLMProvider is an interface for getting LLM services

func NewLLMServiceManager

func NewLLMServiceManager(cfg *LLMConfig) LLMProvider

NewLLMServiceManager creates a new LLM service manager from config

type Link struct {
	Title   string `json:"title"`
	IconSVG string `json:"icon_svg,omitempty"` // SVG path data for the icon
	URL     string `json:"url"`
}

Link represents a custom link to be displayed in the UI

type ListDirectoryResponse

type ListDirectoryResponse struct {
	Path            string           `json:"path"`
	Parent          string           `json:"parent"`
	Entries         []DirectoryEntry `json:"entries"`
	GitHeadSubject  string           `json:"git_head_subject,omitempty"`
	GitWorktreeRoot string           `json:"git_worktree_root,omitempty"`
}

ListDirectoryResponse is the response from the list-directory endpoint

type ModelAPI

type ModelAPI struct {
	ModelID      string `json:"model_id"`
	DisplayName  string `json:"display_name"`
	ProviderType string `json:"provider_type"`
	Endpoint     string `json:"endpoint"`
	APIKey       string `json:"api_key"`
	ModelName    string `json:"model_name"`
	MaxTokens    int64  `json:"max_tokens"`
	Tags         string `json:"tags"` // Comma-separated tags (e.g., "slug" for slug generation)
}

ModelAPI is the API representation of a model

type ModelInfo

type ModelInfo struct {
	ID               string `json:"id"`
	DisplayName      string `json:"display_name,omitempty"`
	Source           string `json:"source,omitempty"` // Human-readable source (e.g., "exe.dev gateway", "$ANTHROPIC_API_KEY")
	Ready            bool   `json:"ready"`
	MaxContextTokens int    `json:"max_context_tokens,omitempty"`
}

ModelInfo represents a model in the API response

type NotificationChannelAPI

type NotificationChannelAPI struct {
	ChannelID   string `json:"channel_id"`
	ChannelType string `json:"channel_type"`
	DisplayName string `json:"display_name"`
	Enabled     bool   `json:"enabled"`
	Config      any    `json:"config"`
}

type ReleaseInfo

type ReleaseInfo struct {
	TagName      string            `json:"tag_name"`
	Version      string            `json:"version"`
	Commit       string            `json:"commit"`
	CommitFull   string            `json:"commit_full"`
	CommitTime   string            `json:"commit_time"`
	PublishedAt  string            `json:"published_at"`
	DownloadURLs map[string]string `json:"download_urls"`
	ChecksumsURL string            `json:"checksums_url"`
}

ReleaseInfo represents release metadata.

type RenameRequest

type RenameRequest struct {
	Slug string `json:"slug"`
}

RenameRequest represents a request to rename a conversation

type Server

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

Server manages the HTTP API and active conversations

func NewServer

func NewServer(database *db.DB, llmManager LLMProvider, toolSetConfig claudetool.ToolSetConfig, logger *slog.Logger, predictableOnly bool, terminalURL, defaultModel, requireHeader string, links []Link) *Server

NewServer creates a new server instance

func (*Server) Cleanup

func (s *Server) Cleanup()

Cleanup removes inactive conversation managers

func (*Server) EnqueueIndex

func (s *Server) EnqueueIndex(conversationID string)

EnqueueIndex enqueues a conversation ID for memory indexing. Non-blocking: drops the request if the queue is full.

func (*Server) IsAgentWorking

func (s *Server) IsAgentWorking(conversationID string) bool

IsAgentWorking returns whether the agent is currently working on the given conversation. Returns false if the conversation doesn't have an active manager.

func (*Server) RegisterNotificationChannel

func (s *Server) RegisterNotificationChannel(ch notifications.Channel)

RegisterNotificationChannel adds a backend notification channel to the dispatcher.

func (*Server) RegisterRoutes

func (s *Server) RegisterRoutes(mux *http.ServeMux)

RegisterRoutes registers HTTP routes on the given mux

func (*Server) ReloadNotificationChannels

func (s *Server) ReloadNotificationChannels()

ReloadNotificationChannels reads enabled channels from DB and replaces the dispatcher's channel set.

func (*Server) SeedNotificationChannelsFromConfig

func (s *Server) SeedNotificationChannelsFromConfig(configs []map[string]any)

SeedNotificationChannelsFromConfig seeds the DB with notification channels from percy.json config if the DB table is empty. One-time migration for backwards compatibility.

func (*Server) SetClusterNode added in v0.306.970370273

func (s *Server) SetClusterNode(node *cluster.Node)

SetClusterNode sets the cluster node for multi-agent coordination. If nil, cluster features are disabled.

func (*Server) SetEmbedder

func (s *Server) SetEmbedder(e memory.Embedder)

SetEmbedder sets the embedding provider for vector search and indexing. If nil, vector search is disabled (FTS-only).

func (*Server) SetMemoryDB

func (s *Server) SetMemoryDB(mdb *memory.DB)

SetMemoryDB sets the memory database for post-conversation indexing. If nil, memory indexing is silently skipped.

func (*Server) SetMuninnSink added in v0.365.963511

func (s *Server) SetMuninnSink(sink *muninn.Sink)

SetMuninnSink sets the MuninnDB sink for dual-writing memory cells.

func (*Server) Start

func (s *Server) Start(port string) error

Start starts the HTTP server and handles the complete lifecycle

func (*Server) StartWithListener

func (s *Server) StartWithListener(listener net.Listener) error

StartWithListener starts the HTTP server using the provided listener. This is useful for systemd socket activation where the listener is created externally.

type StaticCommitInfo

type StaticCommitInfo struct {
	SHA     string `json:"sha"`
	Subject string `json:"subject"`
}

StaticCommitInfo represents a commit from commits.json.

type StreamResponse

type StreamResponse struct {
	Messages          []APIMessage           `json:"messages"`
	Conversation      generated.Conversation `json:"conversation"`
	ConversationState *ConversationState     `json:"conversation_state,omitempty"`
	ContextWindowSize uint64                 `json:"context_window_size,omitempty"`
	// ConversationListUpdate is set when another conversation in the list changed
	ConversationListUpdate *ConversationListUpdate `json:"conversation_list_update,omitempty"`
	// Heartbeat indicates this is a heartbeat message (no new data, just keeping connection alive)
	Heartbeat bool `json:"heartbeat,omitempty"`
	// NotificationEvent is set when a notification-worthy event occurs (e.g. agent finished).
	NotificationEvent *notifications.Event `json:"notification_event,omitempty"`
}

StreamResponse represents the response format for conversation streaming

type SubagentRunner

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

SubagentRunner implements claudetool.SubagentRunner.

func NewSubagentRunner

func NewSubagentRunner(s *Server) *SubagentRunner

NewSubagentRunner creates a new SubagentRunner.

func (*SubagentRunner) RunSubagent

func (r *SubagentRunner) RunSubagent(ctx context.Context, conversationID, prompt string, wait bool, timeout time.Duration, model string) (string, error)

RunSubagent implements claudetool.SubagentRunner.

type SubagentSystemPromptData

type SubagentSystemPromptData struct {
	WorkingDirectory string
	CurrentDate      string // Today's date in YYYY-MM-DD format
	GitInfo          *GitInfo
}

SubagentSystemPromptData contains data for subagent system prompts (minimal subset)

type SwitchModelRequest added in v0.381.91062602

type SwitchModelRequest struct {
	Model             string `json:"model"`
	CancelCurrentTurn bool   `json:"cancel_current_turn"`
}

type SystemPromptData

type SystemPromptData struct {
	WorkingDirectory string
	CurrentDate      string // Today's date in YYYY-MM-DD format
	GitInfo          *GitInfo
	Codebase         *CodebaseInfo
	IsExeDev         bool
	IsSudoAvailable  bool
	Hostname         string // For exe.dev, the public hostname (e.g., "vmname.exe.xyz")
	PercyDBPath      string // Path to the percy database
	SkillsXML        string // XML block for available skills
}

SystemPromptData contains all the data needed to render the system prompt template

type TestModelRequest

type TestModelRequest struct {
	ModelID      string `json:"model_id,omitempty"` // If provided, use stored API key
	ProviderType string `json:"provider_type"`
	Endpoint     string `json:"endpoint"`
	APIKey       string `json:"api_key"`
	ModelName    string `json:"model_name"`
}

TestModelRequest is the request body for testing a model

type TouchedFile added in v0.347.976101525

type TouchedFile struct {
	Path      string `json:"path"`
	Operation string `json:"operation"` // "read", "write", "patch", "navigate"
	Count     int    `json:"count"`     // number of interactions
}

TouchedFile represents a file the agent interacted with.

type UpdateModelRequest

type UpdateModelRequest struct {
	DisplayName  string `json:"display_name"`
	ProviderType string `json:"provider_type"`
	Endpoint     string `json:"endpoint"`
	APIKey       string `json:"api_key"` // Empty string means keep existing
	ModelName    string `json:"model_name"`
	MaxTokens    int64  `json:"max_tokens"`
	Tags         string `json:"tags"` // Comma-separated tags
}

UpdateModelRequest is the request body for updating a model

type UpdateNotificationChannelRequest

type UpdateNotificationChannelRequest struct {
	DisplayName string `json:"display_name"`
	Enabled     bool   `json:"enabled"`
	Config      any    `json:"config"`
}

type VersionChecker

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

VersionChecker checks for new versions of Percy from GitHub releases.

func NewVersionChecker

func NewVersionChecker() *VersionChecker

NewVersionChecker creates a new version checker.

func (*VersionChecker) Check

func (vc *VersionChecker) Check(ctx context.Context, forceRefresh bool) (*VersionInfo, error)

Check checks for a new version, using the cache if still valid.

func (*VersionChecker) DoUpgrade

func (vc *VersionChecker) DoUpgrade(ctx context.Context) error

DoUpgrade downloads and applies the update with checksum verification.

func (*VersionChecker) FetchChangelog

func (vc *VersionChecker) FetchChangelog(ctx context.Context, currentTag, latestTag string) ([]CommitInfo, error)

FetchChangelog fetches the commits between current and latest versions.

type VersionInfo

type VersionInfo struct {
	CurrentVersion      string       `json:"current_version"`
	CurrentTag          string       `json:"current_tag,omitempty"`
	CurrentCommit       string       `json:"current_commit,omitempty"`
	CurrentCommitTime   string       `json:"current_commit_time,omitempty"`
	LatestVersion       string       `json:"latest_version,omitempty"`
	LatestTag           string       `json:"latest_tag,omitempty"`
	PublishedAt         time.Time    `json:"published_at,omitempty"`
	HasUpdate           bool         `json:"has_update"`    // True if minor version is newer (for showing upgrade button)
	ShouldNotify        bool         `json:"should_notify"` // True if should show red dot (newer + 5 days old)
	DownloadURL         string       `json:"download_url,omitempty"`
	ExecutablePath      string       `json:"executable_path,omitempty"`
	Commits             []CommitInfo `json:"commits,omitempty"`
	CheckedAt           time.Time    `json:"checked_at"`
	Error               string       `json:"error,omitempty"`
	RunningUnderSystemd bool         `json:"running_under_systemd"` // True if INVOCATION_ID env var is set (systemd)
	ReleaseInfo         *ReleaseInfo `json:"-"`                     // Internal, not exposed to JSON
}

VersionInfo contains version check results.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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