agent

package
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Mar 8, 2026 License: MIT Imports: 40 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GenerateAvatarWithAI

func GenerateAvatarWithAI(agentID string, persona string, name string, prompt string, logger *slog.Logger) (string, error)

GenerateAvatarWithAI generates an avatar using nanobanana.sh.

func GenerateName

func GenerateName(persona string, userPrompt string) (string, error)

GenerateName generates a character name using Gemini API based on persona description.

func GeneratePersona

func GeneratePersona(currentPersona string, userPrompt string) (string, error)

GeneratePersona elaborates or refines a persona description using Gemini API. currentPersona may be empty (generate from scratch) or non-empty (refine existing).

func GeneratePublicProfile

func GeneratePublicProfile(persona string) (string, error)

GeneratePublicProfile creates a short outward-facing description from a persona.

func GenerateSVGAvatarFile

func GenerateSVGAvatarFile(name string) (string, error)

GenerateSVGAvatarFile creates an SVG avatar file in a temp directory and returns its path. Used as fallback when AI avatar generation is unavailable.

func GenerateTOTPCode

func GenerateTOTPCode(secret, algorithm string, digits, period int) (string, int64, error)

GenerateTOTPCode generates a current TOTP code for the given secret and parameters.

func NormalizeTOTPSecret

func NormalizeTOTPSecret(secret string) (string, error)

NormalizeTOTPSecret normalizes and validates a TOTP secret. Returns the canonical form (upper-case, no padding) or an error.

func SaveAvatar

func SaveAvatar(agentID string, src io.Reader, ext string) error

SaveAvatar saves an uploaded avatar file for an agent.

func ServeAvatar

func ServeAvatar(w http.ResponseWriter, r *http.Request, a *Agent)

ServeAvatar serves the agent's avatar image, falling back to a generated SVG.

func SummarizePersona

func SummarizePersona(persona string) (string, error)

SummarizePersona generates a concise summary of a persona using Gemini API.

func SummarizeWithCLI

func SummarizeWithCLI(tool string, persona string) (string, error)

SummarizeWithCLI generates a persona summary using the agent's own CLI tool. Supports "claude" (stdin to -p) and "codex" (exec -o).

func ValidInterval

func ValidInterval(minutes int) bool

ValidInterval returns true if the given interval is in the allowed set.

func ValidateTOTPParams

func ValidateTOTPParams(secret, algorithm string, digits, period int) (string, error)

ValidateTOTPParams validates TOTP parameters and normalizes the secret. Returns the normalized secret or an error.

Types

type Agent

type Agent struct {
	ID              string `json:"id"`
	Name            string `json:"name"`
	Persona         string `json:"persona"`         // persona description (markdown)
	Model           string `json:"model"`           // e.g. "sonnet", "opus"
	Tool            string `json:"tool"`            // CLI tool: "claude", "codex", "gemini"
	IntervalMinutes int    `json:"intervalMinutes"` // periodic execution interval in minutes (0 = disabled)
	CreatedAt       string `json:"createdAt"`       // RFC3339
	UpdatedAt       string `json:"updatedAt"`       // RFC3339

	// Legacy field — only used during migration from cronExpr-based configs.
	// Not included in JSON output; consumed by store.Load migration.
	LegacyCronExpr string `json:"cronExpr,omitempty"`

	// HasAvatar indicates whether a custom avatar file exists.
	HasAvatar bool `json:"hasAvatar"`
	// AvatarHash is derived from the avatar file's modtime for cache busting.
	AvatarHash string `json:"avatarHash,omitempty"`

	// PublicProfile is a short outward-facing description generated from persona.
	// Shared with other agents via the directory endpoint. Does not expose internal persona details.
	PublicProfile         string `json:"publicProfile,omitempty"`
	PublicProfileOverride bool   `json:"publicProfileOverride,omitempty"`

	// LastMessage is a preview of the most recent message (for list display).
	LastMessage *MessagePreview `json:"lastMessage,omitempty"`
}

Agent represents a persistent AI persona (friend).

type AgentConfig

type AgentConfig struct {
	Name            string `json:"name"`
	Persona         string `json:"persona"`
	Model           string `json:"model"`
	Tool            string `json:"tool"`
	IntervalMinutes *int   `json:"intervalMinutes"` // nil = use default (30)
}

AgentConfig is the request body for creating an agent.

type AgentUpdateConfig

type AgentUpdateConfig struct {
	Name                  *string `json:"name"`
	Persona               *string `json:"persona"`
	PublicProfile         *string `json:"publicProfile"`
	PublicProfileOverride *bool   `json:"publicProfileOverride"`
	Model                 *string `json:"model"`
	Tool                  *string `json:"tool"`
	IntervalMinutes       *int    `json:"intervalMinutes"`
}

AgentUpdateConfig is the request body for PATCH updates. Pointer fields distinguish "not provided" (nil) from "set to zero".

type ChatBackend

type ChatBackend interface {
	// Chat sends a message and returns a channel of streaming events.
	// The channel is closed when the response is complete.
	Chat(ctx context.Context, agent *Agent, userMessage string, systemPrompt string) (<-chan ChatEvent, error)

	// Name returns the tool identifier (e.g. "claude", "codex", "gemini").
	Name() string

	// Available returns true if the CLI tool is installed and accessible.
	Available() bool
}

ChatBackend abstracts a CLI tool for agent chat.

type ChatEvent

type ChatEvent struct {
	Type         string   `json:"type"` // "status", "text", "thinking", "tool_use", "tool_result", "done", "error"
	Status       string   `json:"status,omitempty"`
	Delta        string   `json:"delta,omitempty"`
	ToolName     string   `json:"toolName,omitempty"`
	ToolInput    string   `json:"toolInput,omitempty"`
	ToolOutput   string   `json:"toolOutput,omitempty"`
	Message      *Message `json:"message,omitempty"`
	Usage        *Usage   `json:"usage,omitempty"`
	ErrorMessage string   `json:"errorMessage,omitempty"`
}

ChatEvent is streamed from backend to WebSocket during a chat.

type ClaudeBackend

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

ClaudeBackend implements ChatBackend using the Claude CLI with stream-json output.

func NewClaudeBackend

func NewClaudeBackend(logger *slog.Logger) *ClaudeBackend

func (*ClaudeBackend) Available

func (b *ClaudeBackend) Available() bool

func (*ClaudeBackend) Chat

func (b *ClaudeBackend) Chat(ctx context.Context, agent *Agent, userMessage string, systemPrompt string) (<-chan ChatEvent, error)

func (*ClaudeBackend) Name

func (b *ClaudeBackend) Name() string

type CodexBackend

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

CodexBackend implements ChatBackend for the Codex CLI.

func NewCodexBackend

func NewCodexBackend(logger *slog.Logger) *CodexBackend

func (*CodexBackend) Available

func (b *CodexBackend) Available() bool

func (*CodexBackend) Chat

func (b *CodexBackend) Chat(ctx context.Context, agent *Agent, userMessage string, systemPrompt string) (<-chan ChatEvent, error)

func (*CodexBackend) Name

func (b *CodexBackend) Name() string

type Credential

type Credential struct {
	ID            string `json:"id"`
	Label         string `json:"label"`
	Username      string `json:"username"`
	Password      string `json:"password"`
	TOTPSecret    string `json:"totpSecret,omitempty"`
	TOTPAlgorithm string `json:"totpAlgorithm,omitempty"` // SHA1 (default), SHA256, SHA512
	TOTPDigits    int    `json:"totpDigits,omitempty"`    // 6 (default) or 8
	TOTPPeriod    int    `json:"totpPeriod,omitempty"`    // 30 (default)
	CreatedAt     string `json:"createdAt"`
	UpdatedAt     string `json:"updatedAt"`
}

Credential represents a stored ID/password pair for an agent.

type CredentialStore

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

CredentialStore manages encrypted credential storage in SQLite.

func NewCredentialStore

func NewCredentialStore() (*CredentialStore, error)

NewCredentialStore opens or creates the encrypted credential store.

func (*CredentialStore) AddCredential

func (s *CredentialStore) AddCredential(agentID, label, username, password string, totp *TOTPParams) (*Credential, error)

AddCredential adds a new credential for an agent.

func (*CredentialStore) Close

func (s *CredentialStore) Close() error

Close closes the database connection.

func (*CredentialStore) DeleteAllForAgent

func (s *CredentialStore) DeleteAllForAgent(agentID string) error

DeleteAllForAgent removes all credentials for an agent.

func (*CredentialStore) DeleteCredential

func (s *CredentialStore) DeleteCredential(agentID, credID string) error

DeleteCredential removes a credential by ID.

func (*CredentialStore) GetTOTPCode

func (s *CredentialStore) GetTOTPCode(agentID, credID string) (string, int64, error)

GetTOTPCode generates the current TOTP code for a credential.

func (*CredentialStore) ListCredentials

func (s *CredentialStore) ListCredentials(agentID string) ([]*Credential, error)

ListCredentials returns all credentials for an agent with secrets masked. Passwords and TOTP secrets are NOT decrypted — only metadata is returned.

func (*CredentialStore) RevealPassword

func (s *CredentialStore) RevealPassword(agentID, credID string) (string, error)

RevealPassword returns the plaintext password for a credential.

func (*CredentialStore) UpdateCredential

func (s *CredentialStore) UpdateCredential(agentID, credID string, label, username, password *string, totp *TOTPParams) (*Credential, error)

UpdateCredential updates an existing credential. Only non-nil fields are applied.

type DirectoryEntry

type DirectoryEntry struct {
	ID            string `json:"id"`
	Name          string `json:"name"`
	PublicProfile string `json:"publicProfile"`
}

DirectoryEntry is the minimal public info shared with other agents.

type GeminiBackend

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

GeminiBackend implements ChatBackend for the Gemini CLI.

func NewGeminiBackend

func NewGeminiBackend(logger *slog.Logger) *GeminiBackend

func (*GeminiBackend) Available

func (b *GeminiBackend) Available() bool

func (*GeminiBackend) Chat

func (b *GeminiBackend) Chat(ctx context.Context, agent *Agent, userMessage string, systemPrompt string) (<-chan ChatEvent, error)

func (*GeminiBackend) Name

func (b *GeminiBackend) Name() string

type GroupDM

type GroupDM struct {
	ID        string        `json:"id"`
	Name      string        `json:"name"`
	Members   []GroupMember `json:"members"`
	CreatedAt string        `json:"createdAt"`
	UpdatedAt string        `json:"updatedAt"`
}

GroupDM represents a group conversation between agents.

type GroupDMManager

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

GroupDMManager manages group DM CRUD, message posting, and notifications.

func NewGroupDMManager

func NewGroupDMManager(agentMgr *Manager, logger *slog.Logger) *GroupDMManager

NewGroupDMManager creates a new GroupDMManager.

func (*GroupDMManager) APIBase

func (m *GroupDMManager) APIBase() string

APIBase returns the current API base URL.

func (*GroupDMManager) Create

func (m *GroupDMManager) Create(name string, memberIDs []string) (*GroupDM, error)

Create creates a new group DM with the given members.

func (*GroupDMManager) Delete

func (m *GroupDMManager) Delete(id string) error

Delete removes a group DM and its data.

func (*GroupDMManager) Get

func (m *GroupDMManager) Get(id string) (*GroupDM, bool)

Get returns a group DM by ID.

func (*GroupDMManager) GroupsForAgent

func (m *GroupDMManager) GroupsForAgent(agentID string) []*GroupDM

GroupsForAgent returns all groups that contain the specified agent.

func (*GroupDMManager) List

func (m *GroupDMManager) List() []*GroupDM

List returns all group DMs.

func (*GroupDMManager) Messages

func (m *GroupDMManager) Messages(groupID string, limit int, before string) ([]*GroupMessage, bool, error)

Messages returns paginated messages for a group.

func (*GroupDMManager) PostMessage

func (m *GroupDMManager) PostMessage(ctx context.Context, groupID, agentID, content string, notify bool) (*GroupMessage, error)

PostMessage posts a message to a group and optionally notifies other members. If notify is true, other members receive a system notification in their 1:1 chat. Set notify=false for messages sent from notification-triggered chats to prevent loops.

func (*GroupDMManager) RemoveAgent

func (m *GroupDMManager) RemoveAgent(agentID string)

RemoveAgent removes an agent from all groups. Groups with fewer than 2 members are deleted.

func (*GroupDMManager) SetAPIBase

func (m *GroupDMManager) SetAPIBase(base string)

SetAPIBase sets the base URL for agent-facing API docs in system prompts.

type GroupMember

type GroupMember struct {
	AgentID   string `json:"agentId"`
	AgentName string `json:"agentName"`
}

GroupMember is a participant in a group DM.

type GroupMessage

type GroupMessage struct {
	ID        string `json:"id"`
	AgentID   string `json:"agentId"`
	AgentName string `json:"agentName"`
	Content   string `json:"content"`
	Timestamp string `json:"timestamp"`
}

GroupMessage is a single message in a group DM transcript.

type Manager

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

Manager manages agent CRUD, chat orchestration, and lifecycle.

func NewManager

func NewManager(logger *slog.Logger) *Manager

NewManager creates a new agent manager.

func (*Manager) Abort

func (m *Manager) Abort(agentID string)

Abort cancels any running chat for an agent.

func (*Manager) Chat

func (m *Manager) Chat(ctx context.Context, agentID string, userMessage string, role string) (<-chan ChatEvent, error)

Chat sends a message to an agent and returns a channel of streaming events. The role parameter controls how the input message is stored in the transcript ("user" for interactive chat, "system" for cron-triggered messages).

func (*Manager) Create

func (m *Manager) Create(cfg AgentConfig) (*Agent, error)

Create creates a new agent.

func (*Manager) Credentials

func (m *Manager) Credentials() *CredentialStore

Credentials returns the credential store. Returns nil if the store failed to initialize.

func (*Manager) CronPaused

func (m *Manager) CronPaused() bool

CronPaused returns whether all cron jobs are globally paused.

func (*Manager) Delete

func (m *Manager) Delete(id string) error

Delete removes an agent and its data.

func (*Manager) Directory

func (m *Manager) Directory() []DirectoryEntry

Directory returns minimal public info for all agents (for agent-to-agent discovery).

func (*Manager) Get

func (m *Manager) Get(id string) (*Agent, bool)

Get returns a deep copy of an agent by ID.

func (*Manager) HasCredentials

func (m *Manager) HasCredentials() bool

HasCredentials returns true if the credential store is available.

func (*Manager) IsBusy

func (m *Manager) IsBusy(agentID string) bool

IsBusy returns true if the agent has an active chat.

func (*Manager) List

func (m *Manager) List() []*Agent

List returns deep copies of all agents.

func (*Manager) Messages

func (m *Manager) Messages(agentID string, limit int) ([]*Message, error)

Messages returns recent messages for an agent.

func (*Manager) MessagesPaginated

func (m *Manager) MessagesPaginated(agentID string, limit int, before string) ([]*Message, bool, error)

MessagesPaginated returns messages with cursor-based pagination.

func (*Manager) ResetData

func (m *Manager) ResetData(id string) error

ResetData removes conversation logs and memory but keeps settings, persona, avatar, and credentials.

func (*Manager) SetCronPaused

func (m *Manager) SetCronPaused(paused bool)

SetCronPaused sets the global cron pause state and persists it.

func (*Manager) SetGroupDMManager

func (m *Manager) SetGroupDMManager(gdm *GroupDMManager)

SetGroupDMManager sets the group DM manager reference. Called after both managers are created to avoid circular dependency.

func (*Manager) Shutdown

func (m *Manager) Shutdown()

Shutdown stops all cron jobs and cancels active chats.

func (*Manager) Update

func (m *Manager) Update(id string, cfg AgentUpdateConfig) (*Agent, error)

Update updates an agent's configuration. Only non-nil fields are applied.

type MemoryIndex

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

MemoryIndex provides FTS5-based keyword search across agent memory.

func OpenMemoryIndex

func OpenMemoryIndex(agentID string, logger *slog.Logger) (*MemoryIndex, error)

OpenMemoryIndex opens or creates the FTS5 index for an agent.

func (*MemoryIndex) BuildContextFromQuery

func (idx *MemoryIndex) BuildContextFromQuery(query string) string

BuildContextFromQuery searches the index and returns formatted context for injection into system prompt. Uses hybrid search (FTS5 + vector) when embeddings are available, falls back to FTS5 only.

func (*MemoryIndex) Close

func (idx *MemoryIndex) Close() error

Close closes the database.

func (*MemoryIndex) IndexFiles

func (idx *MemoryIndex) IndexFiles(agentID string) error

IndexFiles indexes MEMORY.md and daily notes.

func (*MemoryIndex) IndexFilesIfStale

func (idx *MemoryIndex) IndexFilesIfStale(agentID string)

IndexFilesIfStale re-indexes files only if they've changed since last index.

func (*MemoryIndex) IndexMessages

func (idx *MemoryIndex) IndexMessages(agentID string) error

IndexMessages indexes messages from the JSONL transcript.

func (*MemoryIndex) IndexNewMessages

func (idx *MemoryIndex) IndexNewMessages(agentID string)

IndexNewMessages incrementally indexes only new messages since last index.

func (*MemoryIndex) Reindex

func (idx *MemoryIndex) Reindex(agentID string) error

Reindex re-indexes all sources for an agent (full rebuild).

func (*MemoryIndex) Search

func (idx *MemoryIndex) Search(query string, limit int) ([]SearchResult, error)

Search performs a FTS5 search and returns relevant context snippets.

type Message

type Message struct {
	ID        string    `json:"id"`
	Role      string    `json:"role"` // "user", "assistant", "system"
	Content   string    `json:"content"`
	Thinking  string    `json:"thinking,omitempty"` // intermediate reasoning (shown collapsed in UI)
	ToolUses  []ToolUse `json:"toolUses,omitempty"`
	Timestamp string    `json:"timestamp"` // RFC3339
	Usage     *Usage    `json:"usage,omitempty"`
}

Message represents a single chat message in the transcript.

type MessagePreview

type MessagePreview struct {
	Content   string `json:"content"`
	Role      string `json:"role"`
	Timestamp string `json:"timestamp"`
}

MessagePreview is a short summary for agent list display.

type OTPEntry

type OTPEntry struct {
	Label     string `json:"label"`
	Issuer    string `json:"issuer"`
	Username  string `json:"username"`
	Secret    string `json:"totpSecret"`
	Algorithm string `json:"algorithm,omitempty"`
	Digits    int    `json:"digits,omitempty"`
	Period    int    `json:"period,omitempty"`
}

OTPEntry represents a parsed OTP entry from a QR code or migration payload.

func DecodeQRImage

func DecodeQRImage(r io.Reader) ([]*OTPEntry, error)

DecodeQRImage decodes a QR code from an image reader and returns parsed OTP entries.

func ParseOTPURI

func ParseOTPURI(uri string) ([]*OTPEntry, error)

ParseOTPURI parses an otpauth:// or otpauth-migration:// URI and returns OTP entries.

type SearchResult

type SearchResult struct {
	Source      string  `json:"source"`      // "message", "memory", "daily"
	Snippet     string  `json:"snippet"`     // text snippet with context
	ContentHash string  `json:"contentHash"` // for dedup/merge across search methods
	Timestamp   string  `json:"timestamp"`
	Score       float64 `json:"score"`
}

SearchResult represents a single search hit.

type TOTPParams

type TOTPParams struct {
	Secret    string
	Algorithm string // SHA1, SHA256, SHA512
	Digits    int    // 6 or 8
	Period    int    // seconds
}

TOTPParams holds TOTP configuration for a credential.

type ToolUse

type ToolUse struct {
	ID     string `json:"id,omitempty"` // tool call ID for matching results
	Name   string `json:"name"`
	Input  string `json:"input"`  // JSON string
	Output string `json:"output"` // truncated output
}

ToolUse represents a single tool invocation within a message.

type Usage

type Usage struct {
	InputTokens  int `json:"inputTokens"`
	OutputTokens int `json:"outputTokens"`
}

Usage tracks token consumption for a message.

Jump to

Keyboard shortcuts

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