agent

package
v0.12.0 Latest Latest
Warning

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

Go to latest
Published: Apr 21, 2026 License: MIT Imports: 49 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// DefaultEmbeddingModel is the Gemini embedding model used when no model
	// has been explicitly configured. Exported so HTTP handlers can present
	// the same fallback without duplicating the string.
	DefaultEmbeddingModel = "gemini-embedding-001"
)
View Source
const ErrMsgCancelled = "cancelled: process was terminated"

ErrMsgCancelled is the error message attached to a "done" event when the backend process is terminated due to a user-initiated cancellation (context.Canceled) rather than a deadline.

View Source
const ErrMsgTimeout = "timeout: process was terminated"

ErrMsgTimeout is the error message attached to a "done" event when the backend process is terminated due to a context timeout.

Variables

View Source
var (
	ErrAvatarInternal         = errors.New("cannot resolve temp dir")
	ErrAvatarNotFound         = errors.New("file not found")
	ErrAvatarUnsupportedImage = errors.New("unsupported image format")
)

Sentinel errors for ValidateTempAvatarPath, allowing callers to map to appropriate HTTP status codes.

View Source
var (
	ErrAgentNotFound  = errors.New("agent not found")
	ErrAgentBusy      = errors.New("agent is busy")
	ErrAgentResetting = errors.New("agent is being reset")

	ErrGroupNotFound      = errors.New("group not found")
	ErrGroupNotMember     = errors.New("agent is not a member of group")
	ErrGroupTooFew        = errors.New("group requires at least 2 members")
	ErrGroupAlreadyMember = errors.New("agent is already a member of group")

	ErrCredentialNotFound = errors.New("credential not found")
	ErrNoTOTPSecret       = errors.New("no TOTP secret configured")
	ErrInvalidTOTP        = errors.New("invalid TOTP parameters")

	ErrUnsupportedTool     = errors.New("unsupported tool")
	ErrUnsupportedInterval = errors.New("unsupported interval")
	ErrUnsupportedTimeout  = errors.New("unsupported timeout")

	ErrInvalidRegenerate = errors.New("invalid regenerate target")
)

Sentinel errors for the agent package.

View Source
var ErrMessageNotFound = errors.New("message not found")

ErrMessageNotFound is returned when a message with the given ID does not exist.

ValidGroupDMStyles is the set of accepted style values.

Functions

func ActiveTasksSummary added in v0.9.0

func ActiveTasksSummary(agentID string) string

ActiveTasksSummary returns a formatted string of open tasks for system prompt injection. Returns empty string if no open tasks exist. Task data is wrapped in a code fence to prevent prompt injection from task titles.

func AgentDir added in v0.10.0

func AgentDir(id string) string

AgentDir returns the data directory path for the given agent ID. Exported for use by external subsystems (e.g. slackbot Hub) that need to resolve agent data directories without importing agent internals.

func BuildMCPServers added in v0.12.0

func BuildMCPServers(agentID, apiBase string, hasSlackBot bool) map[string]mcpServerEntry

BuildMCPServers returns the set of MCP servers that should be available to the given agent. apiBase is the kojo server URL (e.g. "http://127.0.0.1:8080").

func DeleteTask added in v0.9.0

func DeleteTask(agentID, taskID string) error

DeleteTask removes a task by ID.

func DeleteTasksFile added in v0.9.0

func DeleteTasksFile(agentID string)

DeleteTasksFile removes the tasks.json file for an agent (used during data reset).

func GenerateAvatarWithAI

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

GenerateAvatarWithAI generates an avatar by calling the Gemini image generation API directly. Returns the path to an image file inside a kojo-avatar-* temp dir.

func GenerateName

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

GenerateName generates a character name based on persona description.

func GeneratePersona

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

GeneratePersona elaborates or refines a persona description. 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 IsAllowedImageExt added in v0.7.0

func IsAllowedImageExt(ext string) bool

IsAllowedImageExt returns true if ext (case-insensitive) is an accepted avatar image extension.

func IsWithinActiveHours added in v0.7.0

func IsWithinActiveHours(start, end string) bool

IsWithinActiveHours checks if the current local time is within the active window. Returns true if no restriction is set (both empty). Supports overnight ranges (e.g., 22:00-06:00).

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 NormalizeThinkingMode added in v0.10.1

func NormalizeThinkingMode(mode string) string

NormalizeThinkingMode normalizes "auto" to "" for storage.

func PreCompactSummarize added in v0.9.0

func PreCompactSummarize(agentID string, tool string, logger *slog.Logger) error

PreCompactSummarize is called by the PreCompact hook (via API) just before Claude Code compacts the conversation. It reads from Claude's live session JSONL (which contains the full current context including pending tool uses) rather than kojo's messages.jsonl (which may lag behind). Falls back to messages.jsonl if session JSONL is unavailable.

func PrepareClaudeSettings added in v0.9.0

func PrepareClaudeSettings(agentID, apiBase string, allowProtectedPaths []string, logger *slog.Logger)

PrepareClaudeSettings writes .claude/settings.local.json with persona override and (when apiBase is available) a PreCompact hook that calls kojo's API to save a conversation summary before Claude compacts context. Called from Manager.Chat before backend.Chat to ensure settings are in place before the Claude process reads them.

func RecentDiarySummary added in v0.9.0

func RecentDiarySummary(agentID string) string

RecentDiarySummary returns the most recent diary entries for system prompt injection. Returns empty string if no recent entries exist. Content is wrapped with a guard to prevent prompt injection from diary data.

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.

func SummarizeWithCLI

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

SummarizeWithCLI generates a persona summary using a specific CLI tool. Supports "claude", "codex", and "gemini".

func ValidActiveHours added in v0.7.0

func ValidActiveHours(start, end string) error

ValidActiveHours validates the active hours range. Both must be empty (no restriction) or both must be valid HH:MM format.

func ValidEffort added in v0.8.0

func ValidEffort(effort string) bool

ValidEffort returns true if the given effort level is valid.

func ValidInterval

func ValidInterval(minutes int) bool

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

func ValidModelEffort added in v0.10.1

func ValidModelEffort(model, effort string) bool

ValidModelEffort returns true if the model+effort combination is valid. xhigh is only allowed for specific models.

func ValidThinkingMode added in v0.10.1

func ValidThinkingMode(mode string) bool

ValidThinkingMode returns true if the given thinking mode is valid.

func ValidTimeout added in v0.10.0

func ValidTimeout(minutes int) bool

ValidTimeout returns true if the given timeout 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.

func ValidateTempAvatarPath added in v0.7.0

func ValidateTempAvatarPath(avatarPath string) (string, error)

ValidateTempAvatarPath validates that a path points to an image file inside a kojo-avatar-* temp directory. Returns the resolved absolute path or an error. Used by handlers that accept user-supplied avatar paths.

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"
	Effort          string `json:"effort,omitempty"`      // claude only: "low", "medium", "high", "xhigh", "max"
	Tool            string `json:"tool"`                  // CLI tool: "claude", "codex", "gemini"
	WorkDir         string `json:"workDir,omitempty"`     // file storage directory (empty = agentDir)
	IntervalMinutes int    `json:"intervalMinutes"`       // periodic execution interval in minutes (0 = disabled)
	TimeoutMinutes  int    `json:"timeoutMinutes"`        // max duration per cron run in minutes (0 = default 10)
	ActiveStart     string `json:"activeStart,omitempty"` // HH:MM — start of active window (empty = no restriction)
	ActiveEnd       string `json:"activeEnd,omitempty"`   // HH:MM — end of active window (empty = no restriction)
	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"`

	// CustomBaseURL is the base URL for a custom Anthropic Messages API endpoint
	// (e.g., llama-server). Only used when Tool is "custom".
	CustomBaseURL string `json:"customBaseURL,omitempty"`

	// AllowedTools is a whitelist of tool names forwarded to a custom endpoint.
	// If non-empty, only listed tools are forwarded.
	// If empty, all tools are forwarded.
	AllowedTools []string `json:"allowedTools,omitempty"`

	// AllowProtectedPaths lists protected directory names (claude, git, husky)
	// for which Edit/Write/MultiEdit prompts should be suppressed. Recent
	// claude-code versions guard these dirs even under bypassPermissions —
	// explicit permissions.allow rules are the only bypass.
	AllowProtectedPaths []string `json:"allowProtectedPaths,omitempty"`

	// ThinkingMode controls reasoning/thinking for llama.cpp backend.
	// "on" = enable, "off" = disable, "" = server default.
	ThinkingMode string `json:"thinkingMode,omitempty"`

	// NotifySources holds notification source configurations for this agent.
	NotifySources []notifysource.Config `json:"notifySources,omitempty"`

	// SlackBot holds the Slack Socket Mode bot configuration for this agent.
	SlackBot *SlackBotConfig `json:"slackBot,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"`
	Effort          string  `json:"effort"`
	Tool            string  `json:"tool"`
	CustomBaseURL   string  `json:"customBaseURL"`
	ThinkingMode    string  `json:"thinkingMode"`
	WorkDir         string  `json:"workDir"`
	IntervalMinutes *int    `json:"intervalMinutes"` // nil = use default (30)
	TimeoutMinutes  *int    `json:"timeoutMinutes"`  // nil = use default (0 = 10 min)
	ActiveStart     *string `json:"activeStart"`     // HH:MM or empty
	ActiveEnd       *string `json:"activeEnd"`       // HH:MM or empty
}

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"`
	Effort                *string   `json:"effort"`
	Tool                  *string   `json:"tool"`
	WorkDir               *string   `json:"workDir"`
	IntervalMinutes       *int      `json:"intervalMinutes"`
	TimeoutMinutes        *int      `json:"timeoutMinutes"`
	ActiveStart           *string   `json:"activeStart"`
	ActiveEnd             *string   `json:"activeEnd"`
	CustomBaseURL         *string   `json:"customBaseURL"`
	ThinkingMode          *string   `json:"thinkingMode"`
	AllowedTools          []string  `json:"allowedTools"`
	AllowProtectedPaths   *[]string `json:"allowProtectedPaths"`
}

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, opts ChatOptions) (<-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", "message"
	Status       string   `json:"status,omitempty"`
	Delta        string   `json:"delta,omitempty"`
	ToolUseID    string   `json:"toolUseId,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 ChatOptions added in v0.10.0

type ChatOptions struct {
	// OneShot skips session resumption, running a fresh ephemeral session.
	// Used for Slack and other external platform conversations that have
	// their own conversation context.
	OneShot bool

	// MCPServers is the set of MCP tool servers to make available for this
	// chat session. Each backend injects them in its own way (CLI args,
	// config files, etc.). May be nil if no MCP servers are configured.
	MCPServers map[string]mcpServerEntry
}

ChatOptions holds optional parameters for a chat invocation.

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, opts ChatOptions) (<-chan ChatEvent, error)

func (*ClaudeBackend) Name

func (b *ClaudeBackend) Name() string

func (*ClaudeBackend) SetProxyURL added in v0.10.0

func (b *ClaudeBackend) SetProxyURL(url string)

SetProxyURL configures an ANTHROPIC_BASE_URL to inject into Claude CLI env.

type CodexBackend

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

CodexBackend implements ChatBackend for the Codex CLI using app-server (JSON-RPC 2.0 over stdio) for real streaming support.

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, opts ChatOptions) (<-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) DeleteToken added in v0.7.0

func (s *CredentialStore) DeleteToken(provider, agentID, sourceID, key string) error

DeleteToken removes a single token.

func (*CredentialStore) DeleteTokensByAgent added in v0.7.0

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

DeleteTokensByAgent removes all notify tokens for an agent.

func (*CredentialStore) DeleteTokensBySource added in v0.7.0

func (s *CredentialStore) DeleteTokensBySource(provider, agentID, sourceID string) error

DeleteTokensBySource removes all tokens for a specific source.

func (*CredentialStore) GetSetting added in v0.12.0

func (s *CredentialStore) GetSetting(key string) string

GetSetting retrieves a global setting value. Returns "" if the key is not found. Unexpected database errors (connection issues, schema drift, etc.) are logged rather than silently swallowed so they don't masquerade as a missing value.

func (*CredentialStore) GetTOTPCode

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

GetTOTPCode generates the current TOTP code for a credential.

func (*CredentialStore) GetToken added in v0.7.0

func (s *CredentialStore) GetToken(provider, agentID, sourceID, key string) (string, error)

GetToken retrieves and decrypts a token value.

func (*CredentialStore) GetTokenExpiry added in v0.7.0

func (s *CredentialStore) GetTokenExpiry(provider, agentID, sourceID, key string) (string, time.Time, error)

GetTokenExpiry retrieves a token's value and expiration time.

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) ListTokenKeys added in v0.7.0

func (s *CredentialStore) ListTokenKeys(provider, agentID, sourceID string) ([]string, error)

ListTokenKeys returns all keys stored for a given provider/agent/source.

func (*CredentialStore) RevealPassword

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

RevealPassword returns the plaintext password for a credential.

func (*CredentialStore) SetSetting added in v0.12.0

func (s *CredentialStore) SetSetting(key, value string) error

SetSetting stores a global setting value.

func (*CredentialStore) SetToken added in v0.7.0

func (s *CredentialStore) SetToken(provider, agentID, sourceID, key, value string, expiresAt time.Time) error

SetToken stores an encrypted token value. Use empty agent_id/source_id for global tokens (e.g., OAuth2 client secrets).

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 CustomBackend added in v0.10.1

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

CustomBackend implements ChatBackend for custom Anthropic Messages API endpoints (e.g., llama-server). It delegates to ClaudeBackend with ANTHROPIC_BASE_URL pointed at the custom endpoint.

func NewCustomBackend added in v0.10.1

func NewCustomBackend(logger *slog.Logger) *CustomBackend

func (*CustomBackend) Available added in v0.10.1

func (b *CustomBackend) Available() bool

Available returns true if the claude CLI is in PATH (required as the client).

func (*CustomBackend) Chat added in v0.10.1

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

func (*CustomBackend) Name added in v0.10.1

func (b *CustomBackend) Name() string

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 ForkOptions added in v0.11.0

type ForkOptions struct {
	Name              string
	IncludeTranscript bool
}

ForkOptions controls what state is copied into the forked agent.

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, opts ChatOptions) (<-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"`
	Cooldown  int           `json:"cooldown"` // notification cooldown in seconds (0 = use default)
	Style     GroupDMStyle  `json:"style"`    // communication style: "efficient" or "expressive"
	CreatedAt string        `json:"createdAt"`
	UpdatedAt string        `json:"updatedAt"`
}

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) AddMember added in v0.8.1

func (m *GroupDMManager) AddMember(id, newAgentID, callerAgentID string) (*GroupDM, error)

AddMember adds an agent to an existing group DM. callerAgentID must be a current member. Notifies existing members about the new addition.

func (*GroupDMManager) CheckMembership added in v0.9.0

func (m *GroupDMManager) CheckMembership(groupID, agentID string) error

CheckMembership verifies that the group exists and the agent is a member.

func (*GroupDMManager) Create

func (m *GroupDMManager) Create(name string, memberIDs []string, cooldown int, style GroupDMStyle) (*GroupDM, error)

Create creates a new group DM with the given members. cooldown is the notification cooldown in seconds (0 = use default). style controls the communication style ("efficient" or "expressive"; empty = "efficient").

func (*GroupDMManager) Delete

func (m *GroupDMManager) Delete(id string, notify bool) error

Delete removes a group DM and its data. If notify is true, members are notified about the deletion before the group is removed.

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) LeaveGroup added in v0.8.1

func (m *GroupDMManager) LeaveGroup(id, agentID string) error

LeaveGroup removes an agent from a group DM voluntarily. The group is deleted if fewer than 2 members remain.

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) Rename added in v0.8.0

func (m *GroupDMManager) Rename(id, name, callerAgentID string) (*GroupDM, error)

Rename changes the name of a group DM. Only members can rename.

func (*GroupDMManager) SetAPIBase

func (m *GroupDMManager) SetAPIBase(base string)

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

func (*GroupDMManager) SetCooldown added in v0.8.0

func (m *GroupDMManager) SetCooldown(id string, seconds int) (*GroupDM, error)

SetCooldown updates the notification cooldown for a group (in seconds).

func (*GroupDMManager) SetStyle added in v0.9.0

func (m *GroupDMManager) SetStyle(id string, style GroupDMStyle, callerAgentID string) (*GroupDM, error)

SetStyle updates the communication style for a group. callerAgentID must be a member. An empty callerAgentID skips the membership check (for admin/UI calls).

type GroupDMStyle added in v0.9.0

type GroupDMStyle string

GroupDM represents a group conversation between agents. GroupDMStyle controls the communication style for a group conversation. "efficient" (default): concise, token-saving, no pleasantries. "expressive": human-like chat with greetings and conversational filler.

const (
	GroupDMStyleEfficient  GroupDMStyle = "efficient"
	GroupDMStyleExpressive GroupDMStyle = "expressive"
)

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 LlamaCppBackend added in v0.10.1

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

LlamaCppBackend implements ChatBackend by talking directly to llama-server's OpenAI-compatible /v1/chat/completions endpoint via HTTP SSE streaming. No CLI dependency — just needs HTTP access to the server.

func NewLlamaCppBackend added in v0.10.1

func NewLlamaCppBackend(logger *slog.Logger) *LlamaCppBackend

func (*LlamaCppBackend) Available added in v0.10.1

func (b *LlamaCppBackend) Available() bool

func (*LlamaCppBackend) Chat added in v0.10.1

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

func (*LlamaCppBackend) Name added in v0.10.1

func (b *LlamaCppBackend) Name() string

type Manager

type Manager struct {

	// OnChatDone is called when an agent finishes its response.
	OnChatDone func(agent *Agent, message *Message)
	// 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) BackendAvailability added in v0.10.0

func (m *Manager) BackendAvailability() map[string]bool

BackendAvailability returns which agent backends are available.

func (*Manager) BusySince added in v0.7.0

func (m *Manager) BusySince(agentID string) (time.Time, bool)

BusySince returns the time when the agent started its current chat. Returns zero time and false if the agent is not busy.

func (*Manager) Chat

func (m *Manager) Chat(ctx context.Context, agentID string, userMessage string, role string, attachments []MessageAttachment) (<-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) ChatOneShot added in v0.10.0

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

ChatOneShot runs a one-shot chat that does not save to transcript (messages.jsonl) and does not resume the CLI session. Used for external platform conversations (Slack, Discord) that carry their own context. Memory (MEMORY.md, diary) access is still available via system prompt.

func (*Manager) ClearAllEmbeddings added in v0.12.0

func (m *Manager) ClearAllEmbeddings()

ClearAllEmbeddings resets embeddings to NULL for all cached indexes. Called when the embedding model changes (dimensions may differ).

func (*Manager) CloseAllIndexes added in v0.9.0

func (m *Manager) CloseAllIndexes()

CloseAllIndexes closes all cached MemoryIndex instances.

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) DeleteMessage added in v0.10.1

func (m *Manager) DeleteMessage(agentID, msgID string) error

DeleteMessage removes a single message from the transcript. Only supported for the llama.cpp backend. Rejected with ErrAgentBusy while the agent has an active chat.

func (*Manager) Directory

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

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

func (*Manager) Fork added in v0.11.0

func (m *Manager) Fork(srcID string, opts ForkOptions) (*Agent, error)

Fork creates a new agent by deep-copying the source agent's metadata and data files. Memory (MEMORY.md, memory/, persona, avatar) is always copied. Transcript (messages.jsonl) and its derived state (index/, autosummary marker, tasks.json) are copied only when IncludeTranscript is true.

External integrations are intentionally NOT copied: SlackBot, NotifySources, and credentials all require per-agent tokens that cannot be safely shared. CLI local state (.claude/, .gemini/) is also skipped so the fork starts a fresh session. WorkDir is cleared so the fork does not share external output storage with the source.

Known limitations: Manager.Update and the task API can write to persona.md / tasks.json without honoring the resetting flag, so the snapshot is not fully atomic against concurrent PATCH /agents/{id} or task mutations. The same looseness applies to Reset today.

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) Regenerate added in v0.10.1

func (m *Manager) Regenerate(agentID, msgID string) error

Regenerate truncates the transcript at msgID and re-runs the associated user message through the llama.cpp backend. If msgID is an assistant message, msgID and all subsequent messages are removed and the preceding user message is re-sent. If msgID is a user message, all subsequent messages are removed and msgID itself is re-sent.

Returns after truncation and busy-slot registration so reloading clients can immediately see the in-progress chat. The backend request and event streaming run in the background; any backend.Chat failure is surfaced as an error event on the broadcaster (and persisted as a system message).

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) ResetSession added in v0.9.0

func (m *Manager) ResetSession(agentID string) error

ResetSession clears the CLI session (e.g. Claude JSONL) for an agent without deleting conversation history or memory. The next chat will start a fresh CLI session with the full system prompt re-injected.

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, notify polling, and cancels active chats.

func (*Manager) Subscribe added in v0.7.0

func (m *Manager) Subscribe(agentID string) (startedAt time.Time, past []ChatEvent, live <-chan ChatEvent, unsub func(), busy bool)

Subscribe returns a snapshot of all past events and a live channel for an agent's ongoing chat. The caller must call unsub when done to free resources. If the agent is not busy, busy is false and all other values are zero.

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.

func (*Manager) UpdateMessageContent added in v0.10.1

func (m *Manager) UpdateMessageContent(agentID, msgID, content string) (*Message, error)

UpdateMessageContent replaces the content of a single message in the transcript. Only supported for the llama.cpp backend. Rejected with ErrAgentBusy while the agent has an active chat.

func (*Manager) UpdateNotifySources added in v0.7.0

func (m *Manager) UpdateNotifySources(id string, sources []notifysource.Config) error

UpdateNotifySources updates the notification source configs for an agent and rebuilds the poller's source instances.

func (*Manager) UpdateSlackBot added in v0.10.0

func (m *Manager) UpdateSlackBot(id string, cfg *SlackBotConfig) error

UpdateSlackBot updates the Slack bot configuration for an agent. Pass nil to remove the configuration.

func (*Manager) WatchChatStart added in v0.10.0

func (m *Manager) WatchChatStart(agentID string) (<-chan struct{}, func())

WatchChatStart returns a channel that receives a signal whenever a new chat starts for the given agent. Call the returned function to unsubscribe.

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, creds *CredentialStore) (*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) ClearEmbeddings added in v0.12.0

func (idx *MemoryIndex) ClearEmbeddings()

ClearEmbeddings resets all embeddings to NULL and clears the embedding cache. Called when the embedding model changes (dimensions may differ). Errors are logged rather than returned because callers are best-effort (an HTTP handler invalidating state on config change), but silent failure would leave the index in an inconsistent state.

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"`
	Attachments []MessageAttachment `json:"attachments,omitempty"`
	Timestamp   string              `json:"timestamp"` // RFC3339
	Usage       *Usage              `json:"usage,omitempty"`
}

Message represents a single chat message in the transcript.

type MessageAttachment added in v0.7.0

type MessageAttachment struct {
	Path string `json:"path"`
	Name string `json:"name"`
	Size int64  `json:"size"`
	Mime string `json:"mime"`
}

MessageAttachment represents a file attached to a chat message.

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 ReplyTagFilter added in v0.10.0

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

ReplyTagFilter extracts text inside <reply>...</reply> tags from a stream of text deltas. Text outside the tags is treated as internal reasoning and discarded. If the stream ends without any <reply> tag, Flush returns all accumulated text as a graceful degradation (so a misbehaving agent still produces output).

func (*ReplyTagFilter) Feed added in v0.10.0

func (f *ReplyTagFilter) Feed(delta string) string

Feed processes a text delta and returns the portion that should be forwarded (text inside <reply> tags). Returns empty string for text outside tags or when buffering incomplete tags.

func (*ReplyTagFilter) Flush added in v0.10.0

func (f *ReplyTagFilter) Flush() string

Flush returns any remaining buffered content. Called when the stream ends. If no <reply> tag was ever seen, returns ALL accumulated text (graceful degradation so the agent's response is not silently swallowed).

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 SlackBotConfig added in v0.10.0

type SlackBotConfig struct {
	Enabled       bool `json:"enabled"`
	ThreadReplies bool `json:"threadReplies"` // always reply in-thread (default true)

	// Reaction patterns — which message types the bot responds to.
	// All default to true for backwards compatibility.
	RespondDM      *bool `json:"respondDM,omitempty"`      // respond to direct messages
	RespondMention *bool `json:"respondMention,omitempty"` // respond to @mentions in channels
	RespondThread  *bool `json:"respondThread,omitempty"`  // auto-reply in threads with history
}

SlackBotConfig holds Slack bot configuration for an agent.

func (*SlackBotConfig) ReactDM added in v0.10.0

func (c *SlackBotConfig) ReactDM() bool

ReactDM returns whether the bot should respond to direct messages.

func (*SlackBotConfig) ReactMention added in v0.10.0

func (c *SlackBotConfig) ReactMention() bool

ReactMention returns whether the bot should respond to @mentions.

func (*SlackBotConfig) ReactThread added in v0.10.0

func (c *SlackBotConfig) ReactThread() bool

ReactThread returns whether the bot should auto-reply in threads with history.

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 Task added in v0.9.0

type Task struct {
	ID        string `json:"id"`
	Title     string `json:"title"`
	Status    string `json:"status"` // "open", "done"
	CreatedAt string `json:"createdAt"`
	UpdatedAt string `json:"updatedAt"`
}

Task represents a persistent task for an agent.

func CreateTask added in v0.9.0

func CreateTask(agentID string, params TaskCreateParams) (*Task, error)

CreateTask adds a new task for an agent.

func ListTasks added in v0.9.0

func ListTasks(agentID string) ([]*Task, error)

ListTasks returns all tasks for an agent.

func UpdateTask added in v0.9.0

func UpdateTask(agentID, taskID string, params TaskUpdateParams) (*Task, error)

UpdateTask updates a task by ID.

type TaskCreateParams added in v0.9.0

type TaskCreateParams struct {
	Title string `json:"title"`
}

TaskCreateParams is the request body for creating a task.

type TaskUpdateParams added in v0.9.0

type TaskUpdateParams struct {
	Title  *string `json:"title"`
	Status *string `json:"status"`
}

TaskUpdateParams is the request body for updating a task.

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