Documentation
¶
Overview ¶
Package config handles loading, defaulting, and validating Thane's YAML configuration.
Configuration is loaded from a single YAML file located via FindConfig. After Load returns, all fields carry usable values — callers never need empty-string checks or fallback logic. The load pipeline is:
- Read the file and expand environment variables (os.ExpandEnv).
- Unmarshal YAML into a Config struct.
- Apply sensible defaults for any unset fields ([Config.applyDefaults]).
- Validate internal consistency (Config.Validate).
Secrets (API keys, tokens) can be written directly in the config file. Protect the file with appropriate permissions (chmod 600). Environment variable expansion is available as a convenience for container and 12-factor deployments but is not the recommended default.
To regenerate examples/config.example.yaml from source:
go generate ./internal/config
Index ¶
- Constants
- func DefaultSearchPaths() []string
- func FindConfig(explicit string) (string, error)
- func ParseLogLevel(s string) (slog.Level, error)
- func ReplaceLogLevelNames(groups []string, a slog.Attr) slog.Attr
- type AgentConfig
- type AnalysisConfig
- type AnthropicConfig
- type ArchiveConfig
- type ArchivePrewarmConfig
- type AttachmentsConfig
- type CapabilityTagConfig
- type CardDAVConfig
- type Config
- type DebugConfig
- type DelegateConfig
- type DelegateProfileConfig
- type DeviceMapping
- type EmbeddingsConfig
- type EpisodicConfig
- type ExtractionConfig
- type HomeAssistantConfig
- type IdentityConfig
- type ListenConfig
- type LoggingConfig
- func (l LoggingConfig) ContentArchiveDirPath(logDir string) string
- func (l LoggingConfig) ContentArchiveDuration() time.Duration
- func (l LoggingConfig) ContentMaxLength() int
- func (l LoggingConfig) DatasetEnabled(dataset string) bool
- func (l LoggingConfig) DirPath() string
- func (l LoggingConfig) RetentionDaysDuration() time.Duration
- func (l LoggingConfig) RootPath() string
- func (l LoggingConfig) StdoutEnabled() bool
- func (l LoggingConfig) StdoutFormatValue() string
- func (l LoggingConfig) StdoutLevelValue() string
- type LoggingDatasetConfig
- type LoggingDatasetsConfig
- type LoggingStdoutConfig
- type LoopsConfig
- type MCPConfig
- type MCPServerConfig
- type MCPToolConfig
- type MQTTConfig
- type MediaConfig
- type MetacognitiveConfig
- type MetacognitiveRouterConfig
- type ModelConfig
- type ModelServerConfig
- type ModelsConfig
- type OllamaAPIConfig
- type PersonConfig
- type PlatformConfig
- type PlatformProviderConfig
- type PrewarmConfig
- type PricingEntry
- type ProvenanceConfig
- type SearchConfig
- type ShellExecConfig
- type SignalConfig
- type SignalRoutingConfig
- type StateWindowConfig
- type SubscribeConfig
- type SubscriptionConfig
- type TelemetryConfig
- type UnifiConfig
- type VisionConfig
- type WorkspaceConfig
Constants ¶
const LevelTrace = slog.Level(-8)
LevelTrace is a custom slog level below slog.LevelDebug, intended for wire-level forensics (full JSON request/response payloads). The numeric value -8 follows the convention established by OpenTelemetry and other Go projects that extend slog with a Trace level.
Use sparingly — Trace output is extremely verbose and should only be enabled when diagnosing provider-specific bugs.
Variables ¶
This section is empty.
Functions ¶
func DefaultSearchPaths ¶
func DefaultSearchPaths() []string
func FindConfig ¶
FindConfig locates a configuration file. If explicit is non-empty, that exact path must exist or an error is returned. Otherwise, the paths from DefaultSearchPaths are tried in order, and the first that exists is returned. Returns an error if no config file can be found.
func ParseLogLevel ¶
ParseLogLevel converts a case-insensitive string to an slog.Level.
Accepted values:
- "trace" → LevelTrace (wire-level payloads)
- "debug" → slog.LevelDebug (per-request detail)
- "info" or "" → slog.LevelInfo (normal operation)
- "warn" or "warning" → slog.LevelWarn
- "error" → slog.LevelError
Returns an error for unrecognized values. Leading and trailing whitespace is trimmed before matching.
func ReplaceLogLevelNames ¶
ReplaceLogLevelNames is an slog.HandlerOptions.ReplaceAttr function that renders LevelTrace as "TRACE" in log output. Without this, slog would render it as "DEBUG-4" since it doesn't know about custom levels.
Pass it as the ReplaceAttr field when constructing a handler:
slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
Level: config.LevelTrace,
ReplaceAttr: config.ReplaceLogLevelNames,
})
Types ¶
type AgentConfig ¶ added in v0.5.1
type AgentConfig struct {
// OrchestratorTools lists tool names to advertise when
// DelegationRequired is true. If empty, a sensible default set
// is applied (thane_delegate plus lightweight memory tools).
OrchestratorTools []string `yaml:"orchestrator_tools"`
// DelegationRequired enables orchestrator tool gating. When false
// (the default), all tools are available on every iteration.
DelegationRequired bool `yaml:"delegation_required"`
}
AgentConfig configures agent loop behavior. When DelegationRequired is true, the agent loop only advertises the tools listed in OrchestratorTools, steering the primary model toward delegation instead of direct tool use.
type AnalysisConfig ¶ added in v0.8.0
type AnalysisConfig struct {
// DefaultOutputPath is the base directory for analysis output when
// a feed has no per-feed output_path configured. Supports ~ expansion.
// Example: ~/Sync/Aimee/Vault/Media
DefaultOutputPath string `yaml:"default_output_path"`
// DatabasePath is the SQLite database file for engagement tracking.
// If empty, defaults to {data_dir}/media_engagement.db at startup.
DatabasePath string `yaml:"database_path"`
}
AnalysisConfig configures the media analysis pipeline that produces structured markdown output in an Obsidian-compatible vault. Each feed can override the output path; otherwise the default is used.
type AnthropicConfig ¶
type AnthropicConfig struct {
APIKey string `yaml:"api_key"`
}
AnthropicConfig configures the Anthropic (Claude) API provider.
func (AnthropicConfig) Configured ¶
func (c AnthropicConfig) Configured() bool
Configured reports whether an Anthropic API key is present.
type ArchiveConfig ¶ added in v0.3.0
type ArchiveConfig struct {
// MetadataModel is a soft preference for the LLM model used when
// generating session metadata (title, tags, summaries). Passed as a
// hint to the model router; the router has final say. This is a
// background operation where latency doesn't matter — ideal for
// local/free models. Default: uses the default model.
MetadataModel string `yaml:"metadata_model"`
// SummarizeInterval is how often (in seconds) the background
// summarizer scans for unsummarized sessions. Default: 300 (5 min).
SummarizeInterval int `yaml:"summarize_interval"`
// SummarizeTimeout is the max seconds for a single session's
// metadata LLM call. Default: 60.
SummarizeTimeout int `yaml:"summarize_timeout"`
// SessionIdleMinutes is the backstop idle timeout for the
// summarizer worker. Active sessions with no message activity
// for this many minutes are silently closed and become eligible
// for summarization. This complements the channel-specific idle
// check (e.g. signal.session_idle_minutes) which sends farewell
// messages.
//
// Pointer type distinguishes "omitted" (nil → inherit from
// signal.session_idle_minutes) from "explicitly set to 0"
// (disabled). A positive value overrides the inherited default.
SessionIdleMinutes *int `yaml:"session_idle_minutes"`
}
ArchiveConfig configures session archive behavior.
type ArchivePrewarmConfig ¶ added in v0.8.0
type ArchivePrewarmConfig struct {
// Enabled controls whether archive injection is active.
// Requires the parent Prewarm.Enabled to also be true.
// Default: false.
Enabled bool `yaml:"enabled"`
// MaxResults caps the number of archive search results injected.
// Default: 3.
MaxResults int `yaml:"max_results"`
// MaxBytes caps the formatted output in bytes to prevent context
// flooding. Default: 4000 (~1000 tokens).
MaxBytes int `yaml:"max_bytes"`
}
ArchivePrewarmConfig configures archive retrieval injection for cold-start wakes. When enabled, relevant past conversation excerpts are injected into the system prompt so the model has experiential judgment — not just knowledge — before responding.
type AttachmentsConfig ¶ added in v0.8.4
type AttachmentsConfig struct {
// StoreDir is the root directory for the content-addressed file
// store. When set, received attachments are stored by SHA-256 hash
// instead of being copied with their original filenames. The
// metadata index is stored at {data_dir}/attachments.db.
// Supports ~ expansion. This is a durable generated-artifact root,
// not a hand-edited document root. Example:
// ~/Thane/generated/attachments
StoreDir string `yaml:"store_dir"`
Vision VisionConfig `yaml:"vision"`
}
AttachmentsConfig configures content-addressed attachment storage.
type CapabilityTagConfig ¶ added in v0.7.0
type CapabilityTagConfig struct {
// Description is a human-readable summary shown in the capability
// manifest so the agent knows what activating this tag provides. For
// compiled-in tags, empty keeps the built-in description.
Description string `yaml:"description"`
// Tools lists the tool names belonging to this tag. A tool can
// appear in multiple tags; it loads when any of its tags is active.
//
// For compiled-in tags, empty keeps the built-in tool membership.
// Prefer leaving this empty for built-in or MCP-backed tags unless
// you intentionally want to replace the compiled/operator-derived
// membership with an explicit list.
Tools []string `yaml:"tools"`
// AlwaysActive tags cannot be deactivated. They are included in
// every session regardless of channel or agent requests.
AlwaysActive bool `yaml:"always_active"`
// Protected tags are reserved for runtime trust and environment
// assertions (for example an owner-authenticated conversation).
// They are visible to the model when active, but cannot be toggled
// via activate_capability or deactivate_capability.
Protected bool `yaml:"protected"`
}
CapabilityTagConfig defines a named group of tools (and optionally talents) that can be loaded together. For compiled-in tags, empty Description and Tools act as "keep the built-in defaults". Tags marked AlwaysActive are included in every session unconditionally. Tags marked Protected are runtime-asserted and cannot be manually activated or deactivated by the model.
func (CapabilityTagConfig) Validate ¶ added in v0.7.0
func (c CapabilityTagConfig) Validate(tagName string, builtin bool) error
Validate checks that the capability tag configuration is internally consistent. It ensures a description is present and the tools list is non-empty. Tag names are validated by the caller since they are map keys in the parent Config struct.
type CardDAVConfig ¶ added in v0.8.0
type CardDAVConfig struct {
Enabled bool `yaml:"enabled"`
Listen []string `yaml:"listen"` // e.g. ["127.0.0.1:8843"]
Username string `yaml:"username"` // Basic Auth username
Password string `yaml:"password"` // Basic Auth password
}
CardDAVConfig configures the optional CardDAV server for native contact app sync. When Enabled is true and credentials are set, Thane exposes a CardDAV endpoint that can be added as an account in macOS Contacts.app, iOS, Thunderbird, or any CardDAV client.
func (CardDAVConfig) Configured ¶ added in v0.8.0
func (c CardDAVConfig) Configured() bool
Configured reports whether the CardDAV server has all required settings.
type Config ¶
type Config struct {
// Listen configures the primary HTTP API server (OpenAI-compatible).
Listen ListenConfig `yaml:"listen"`
// OllamaAPI configures the optional Ollama-compatible API server,
// used for Home Assistant integration.
OllamaAPI OllamaAPIConfig `yaml:"ollama_api"`
// CardDAV configures the optional CardDAV server for native
// contact app sync (macOS Contacts.app, iOS, Thunderbird, etc.).
CardDAV CardDAVConfig `yaml:"carddav"`
// Platform configures the WebSocket endpoint for native platform
// provider connections (e.g. macOS app).
Platform PlatformConfig `yaml:"platform"`
// HomeAssistant configures the connection to a Home Assistant instance.
HomeAssistant HomeAssistantConfig `yaml:"homeassistant"`
// Models configures LLM providers, model routing, and the default model.
Models ModelsConfig `yaml:"models"`
// Anthropic configures the Anthropic (Claude) API provider.
Anthropic AnthropicConfig `yaml:"anthropic"`
// Embeddings configures vector embedding generation for semantic search.
Embeddings EmbeddingsConfig `yaml:"embeddings"`
// Workspace configures the agent's sandboxed file system access.
// The workspace root is also the anchor for Thane's fixed core
// document root at {workspace.path}/core, which holds canonical
// always-on files at stable locations such as persona.md, ego.md,
// mission.md, and metacognitive.md.
Workspace WorkspaceConfig `yaml:"workspace"`
// Paths maps named prefixes to directory paths for file resolution.
// Each entry creates a prefix (e.g., "kb" → kb:path resolves to
// the configured directory). Supports ~ expansion at resolver
// construction time.
//
// These prefixes also define the managed local document roots used by
// the documents capability. Any configured root that exists on disk is
// eligible for indexed browse/search/section retrieval via doc_* tools,
// so users can add their own custom corpora here without code changes.
// See docs/understanding/document-roots.md for the operator-facing
// contract; keep that document in sync with changes here.
//
// Typical prefixes are:
// - kb: curated knowledge / indexed documents
// - generated: model-produced durable outputs (reports, dailies)
// - scratchpad: low-integrity writable work area
// - dossiers: private dossiers or long-form reference material
//
// The core: prefix is reserved and always derived from
// {workspace.path}/core; it is not configured here.
Paths map[string]string `yaml:"paths"`
// ExtraPath lists additional directories to prepend to the process
// PATH at startup, ensuring exec.LookPath finds binaries installed
// outside the default system PATH (e.g., /opt/homebrew/bin on macOS).
// Environment variables are expanded (e.g., $HOME/bin).
ExtraPath []string `yaml:"extra_path"`
// ShellExec configures the agent's ability to run shell commands.
ShellExec ShellExecConfig `yaml:"shell_exec"`
// DataDir is the root directory for SQLite databases and other
// opaque runtime state (memory, facts, scheduler, checkpoints).
// Keep this separate from human-authored and model-authored
// document roots. Default: "./db".
DataDir string `yaml:"data_dir"`
// TalentsDir is the directory containing talent markdown files that
// extend the system prompt. In higher-integrity deployments, this is
// a curated managed root rather than a scratch workspace.
// Default: "./talents".
TalentsDir string `yaml:"talents_dir"`
// Archive configures session archive behavior.
Archive ArchiveConfig `yaml:"archive"`
// Extraction configures automatic fact extraction from conversations.
Extraction ExtractionConfig `yaml:"extraction"`
// Search configures web search providers.
Search SearchConfig `yaml:"search"`
// Episodic configures episodic memory context injection (daily
// memory files and recent conversation history).
Episodic EpisodicConfig `yaml:"episodic"`
// Agent configures agent loop behavior, including orchestrator
// tool gating for delegation-first architecture.
Agent AgentConfig `yaml:"agent"`
// Delegate configures the thane_delegate tool's split-model execution.
Delegate DelegateConfig `yaml:"delegate"`
// CapabilityTags overlays the compiled-in tool/tag baseline with
// operator-defined descriptions, tool membership overrides, and
// custom tags. Tags marked always_active are loaded
// unconditionally. Other tags are activated via
// activate_capability/deactivate_capability tools or channel-pinned
// configuration.
CapabilityTags map[string]CapabilityTagConfig `yaml:"capability_tags"`
// ChannelTags maps conversation source channels (e.g., "signal",
// "email") to lists of capability tag names that are automatically
// activated when a message arrives on that channel. This is
// additive to always-active tags and any tags the agent requests
// at runtime. Tag names must reference either compiled-in tags or
// entries in [CapabilityTags].
ChannelTags map[string][]string `yaml:"channel_tags"`
// MCP configures external MCP (Model Context Protocol) server
// connections for tool discovery. Each server provides additional
// tools that are discovered dynamically and bridged into the
// agent's tool registry.
MCP MCPConfig `yaml:"mcp"`
// MQTT configures MQTT publishing for Home Assistant device discovery
// and sensor state reporting. When Broker and DeviceName are both
// set, Thane connects to the broker and registers as an HA device.
MQTT MQTTConfig `yaml:"mqtt"`
// Person configures household member presence tracking. When Track
// contains entity IDs, the agent receives a "People & Presence"
// section in its system prompt on every wake, eliminating tool
// calls for basic presence questions.
Person PersonConfig `yaml:"person"`
// Signal configures the Signal message bridge for inbound message
// reception and response routing via a signal-mcp MCP server.
Signal SignalConfig `yaml:"signal"`
// Forge configures code forge integrations (GitHub, Gitea). When
// configured, Thane can interact with issues, pull requests, and
// code review directly without an MCP forge server subprocess.
Forge forge.Config `yaml:"forge"`
// Email configures native IMAP email access. When configured, Thane
// can list, read, search, and manage email directly without an MCP
// email server subprocess.
Email email.Config `yaml:"email"`
// Identity configures the agent's own contact identity for vCard
// export and self-referencing operations.
Identity IdentityConfig `yaml:"identity"`
// Attachments configures content-addressed attachment storage.
// When StoreDir is set, received attachments (Signal, email, etc.)
// are stored by SHA-256 hash with a SQLite metadata index for
// deduplication and provenance tracking.
Attachments AttachmentsConfig `yaml:"attachments"`
// Provenance configures git-backed file storage with SSH signature
// enforcement. Files written through a provenance store are
// automatically committed with cryptographic signatures, providing
// tamper detection, audit history, and rollback. Newer core-root
// layouts read always-on identity documents directly from
// {workspace.path}/core rather than from this store.
Provenance ProvenanceConfig `yaml:"provenance"`
// StateWindow configures the rolling window of recent Home Assistant
// state changes injected into the agent's system prompt on every run.
StateWindow StateWindowConfig `yaml:"state_window"`
// Unifi configures the UniFi network controller connection for
// room-level presence detection via wireless AP client associations.
Unifi UnifiConfig `yaml:"unifi"`
// Prewarm configures context pre-warming for cold-start loops.
// When enabled, subject-keyed facts are injected into the system
// prompt before the model sees the triggering event.
Prewarm PrewarmConfig `yaml:"prewarm"`
// Media configures the media transcript retrieval tool. When yt-dlp
// is available, the agent can fetch transcripts from YouTube, Vimeo,
// podcasts, and other sources supported by yt-dlp.
Media MediaConfig `yaml:"media"`
// Metacognitive configures the perpetual metacognitive attention loop.
// When enabled, a background goroutine monitors the environment,
// reasons via LLM, and adapts its own sleep cycle between iterations.
Metacognitive MetacognitiveConfig `yaml:"metacognitive"`
// Loops configures immutable loop definitions loaded from the config
// file. These definitions become the base layer for the loops-ng
// definition registry, with a persistent dynamic overlay applied at
// runtime.
Loops LoopsConfig `yaml:"loops"`
// Debug configures diagnostic options for inspecting the assembled
// system prompt and other internal state.
Debug DebugConfig `yaml:"debug"`
// Timezone is the IANA timezone for the household (e.g.,
// "America/Chicago"). Used in the Current Conditions system prompt
// section so the agent reasons about local time. If empty, the
// system's local timezone is used.
Timezone string `yaml:"timezone"`
// Pricing maps model names to their per-million-token costs (USD).
// When empty, built-in defaults for known Anthropic models are applied.
// Local/Ollama models not listed here default to $0.
Pricing map[string]PricingEntry `yaml:"pricing"`
// Logging configures Thane's filesystem datasets, stdout policy, and
// queryable request/log retention.
Logging LoggingConfig `yaml:"logging"`
// LogLevel is deprecated; use Logging.Level instead.
// Kept for backwards compatibility — migrated in [Config.applyDefaults].
LogLevel string `yaml:"log_level"`
// LogFormat is deprecated; use Logging.Format instead.
// Kept for backwards compatibility — migrated in [Config.applyDefaults].
LogFormat string `yaml:"log_format"`
}
Config is the top-level configuration structure for the Thane agent, loaded from a single YAML file via Load. The struct hierarchy maps directly to the YAML key hierarchy; the generated example config (examples/config.example.yaml) is derived from these struct definitions and their field comments via `go generate ./internal/config`.
After Load returns, all fields carry usable values -- callers never need empty-string checks or fallback logic. See [Config.applyDefaults] for the defaulting rules and Config.Validate for consistency checks.
func Default ¶
func Default() *Config
Default returns a configuration suitable for local development with Ollama. All defaults are applied, so the returned Config is immediately usable without calling Load.
func ExampleConfig ¶ added in v0.9.1
func ExampleConfig() *Config
ExampleConfig returns a *Config populated with realistic example values for every field. It is the single source of truth for generated config documentation — callers should not hard-code example values elsewhere.
Required sections (listen, homeassistant, models, etc.) contain real placeholder values. Optional sections contain example values that the generator emits as commented-out YAML blocks so new users can see the full configuration surface without accidentally enabling services they haven't configured.
When new fields are added to Config or any sub-struct, ExampleConfig must be updated accordingly so the generated config stays complete. The generator (go generate ./internal/config) re-runs automatically on any change to this file or to the Config struct definitions.
func Load ¶
Load reads a YAML configuration file, expands environment variables, applies defaults for any unset fields, and validates the result.
After Load returns a non-nil Config, every field is usable without additional nil or empty-string checks. The load pipeline is:
- Read the file.
- Expand environment variables (e.g., ${HOME}, ${ANTHROPIC_API_KEY}).
- Unmarshal YAML into a Config.
- Apply defaults via [Config.applyDefaults].
- Validate via Config.Validate.
func (*Config) ContextWindowForModel ¶
ContextWindowForModel returns the configured context window size for the named model. If the model is not found in [ModelsConfig.Available], defaultSize is returned. This avoids the need for callers to loop over the model list themselves.
func (*Config) CoreFile ¶ added in v0.9.1
CoreFile returns the absolute-or-relative path to a named file in the fixed core document root. When workspace.path is unset, CoreFile returns the empty string.
func (*Config) CoreInjectFiles ¶ added in v0.9.1
CoreInjectFiles returns the curated always-on files that should be re-read and injected into the system prompt on every turn.
func (*Config) CoreRoot ¶ added in v0.9.1
CoreRoot returns the fixed high-integrity core document root derived from [Workspace.Path]. When workspace.path is unset, CoreRoot returns the empty string.
func (*Config) DeprecatedFieldsUsed ¶ added in v0.8.1
DeprecatedFieldsUsed reports whether the legacy top-level log_level or log_format fields are set. Callers use this to emit deprecation warnings.
func (*Config) Validate ¶
Validate checks that the configuration is internally consistent after defaults have been applied. It returns an error describing the first problem found, or nil if the configuration is valid.
Validation checks include port ranges and log level syntax. It does not check reachability of external services (that happens at runtime).
type DebugConfig ¶ added in v0.6.0
type DebugConfig struct {
// DemoLoops spawns simulated loops covering all visual variants
// (categories, parent/child, error states, node churn) so the
// dashboard can be iterated on without real service dependencies.
DemoLoops bool `yaml:"demo_loops"`
}
DebugConfig configures diagnostic options for development and testing.
type DelegateConfig ¶ added in v0.8.4
type DelegateConfig struct {
// Profiles contains per-profile overrides. The map key is the
// profile name (e.g., "general", "ha"). Only fields that are set
// override the builtin defaults — omitted fields keep their
// compiled-in values.
Profiles map[string]DelegateProfileConfig `yaml:"profiles"`
}
DelegateConfig configures the thane_delegate tool's split-model execution behavior.
type DelegateProfileConfig ¶ added in v0.8.4
type DelegateProfileConfig struct {
// ToolTimeout is the maximum time a single tool call may run
// before being cancelled. Accepts Go duration strings (e.g.,
// "30s", "3m", "5m"). Zero keeps the builtin default (30s).
ToolTimeout time.Duration `yaml:"tool_timeout"`
// MaxDuration is the maximum wall clock time for the entire
// delegation loop. Zero keeps the builtin default (90s).
MaxDuration time.Duration `yaml:"max_duration"`
// MaxIter is the maximum number of tool-calling iterations.
// Zero keeps the builtin default (15).
MaxIter int `yaml:"max_iter"`
// MaxTokens is the maximum cumulative output tokens before
// budget exhaustion. Zero keeps the builtin default (25000).
MaxTokens int `yaml:"max_tokens"`
}
DelegateProfileConfig holds configurable overrides for a delegate profile. Zero-value fields are ignored (builtin defaults apply).
type DeviceMapping ¶ added in v0.6.0
type DeviceMapping struct {
// MAC is the device's MAC address (e.g., "AA:BB:CC:DD:EE:FF").
// Case-insensitive; normalized to lowercase at startup.
MAC string `yaml:"mac"`
}
DeviceMapping maps a MAC address to a tracked person's wireless device.
type EmbeddingsConfig ¶
type EmbeddingsConfig struct {
// Enabled controls whether Thane generates embeddings at ingest and
// lookup time for semantic search and related recall paths.
Enabled bool `yaml:"enabled"`
// Model is the embedding model name. Default: "nomic-embed-text".
Model string `yaml:"model"`
// BaseURL overrides the Ollama endpoint used for embeddings. Empty
// falls back to the default model resource/provider selection.
BaseURL string `yaml:"baseurl"`
}
EmbeddingsConfig configures vector embedding generation for semantic recall features. When Enabled is false, Thane still stores facts and contacts, but vector-backed lookup and similarity search are disabled for those stores and related ingest paths.
type EpisodicConfig ¶ added in v0.5.0
type EpisodicConfig struct {
// DailyDir is the directory containing daily memory files named
// YYYY-MM-DD.md. Supports ~ expansion. If empty, daily memory
// file injection is disabled. Prefer a generated/provenance-aware
// root (for example ~/Thane/generated/daily) over legacy shared
// application state directories.
DailyDir string `yaml:"daily_dir"`
// LookbackDays is how many days of daily memory files to include.
// Today and the previous (LookbackDays-1) days are checked.
// Default: 2 (today + yesterday).
LookbackDays int `yaml:"lookback_days"`
// HistoryTokens is the approximate token budget for recent
// conversation history injected into the system prompt.
// Default: 4000.
HistoryTokens int `yaml:"history_tokens"`
// SessionGapMinutes is the silence duration (in minutes) between
// sessions that triggers a gap annotation in the history output.
// Default: 30.
SessionGapMinutes int `yaml:"session_gap_minutes"`
}
EpisodicConfig configures episodic memory context injection. When configured, the agent receives curated daily notes and a recency-graded summary of recent conversations in its system prompt, giving it continuity across sessions.
type ExtractionConfig ¶ added in v0.5.0
type ExtractionConfig struct {
// Enabled controls whether automatic fact extraction runs.
// Default: false (opt-in).
Enabled bool `yaml:"enabled"`
// Model is the LLM model used for fact extraction. This runs async
// in the background — local/free models recommended.
// Default: falls back to archive.metadata_model, then models.default.
Model string `yaml:"model"`
// MinMessages is the minimum conversation length (in messages) before
// extraction is attempted. Very short exchanges rarely contain knowledge.
// Default: 2.
MinMessages int `yaml:"min_messages"`
// TimeoutSeconds is the maximum time allowed for a single extraction
// call. Default: 30.
TimeoutSeconds int `yaml:"timeout_seconds"`
}
ExtractionConfig configures automatic fact extraction from conversations. When enabled, the agent asynchronously analyzes each interaction after the response is delivered and persists noteworthy facts to the fact store. This is a background operation using local models — zero cost, no latency impact.
type HomeAssistantConfig ¶
type HomeAssistantConfig struct {
URL string `yaml:"url"`
Token string `yaml:"token"`
// Subscribe configures WebSocket event subscriptions for real-time
// state change monitoring. When entity_globs is non-empty, only
// matching entities are processed; when empty, all state changes
// are accepted.
Subscribe SubscribeConfig `yaml:"subscribe"`
}
HomeAssistantConfig configures the connection to a Home Assistant instance. Both URL and Token must be set for the connection to be established; see HomeAssistantConfig.Configured.
func (HomeAssistantConfig) Configured ¶
func (c HomeAssistantConfig) Configured() bool
Configured reports whether both URL and Token are set. A partial configuration (URL without token or vice versa) is treated as unconfigured — Thane will start without Home Assistant tools.
type IdentityConfig ¶ added in v0.8.0
type IdentityConfig struct {
// ContactName is the formatted name of the agent's own contact
// record. When set, export_vcf name="self" resolves to this
// contact.
ContactName string `yaml:"contact_name"`
// OwnerContactName is the formatted name of the primary human
// owner/operator contact record. When set, the owner_contact tool
// resolves directly to this contact instead of guessing from trust
// zones. When empty, owner_contact falls back to the sole admin
// contact if exactly one exists.
OwnerContactName string `yaml:"owner_contact_name"`
}
IdentityConfig configures the agent's own contact identity. The ContactName must match a contact record in the directory to enable self-referencing operations like vCard export.
type ListenConfig ¶
type ListenConfig struct {
// Address is the network address to bind to. Empty string means
// all interfaces (0.0.0.0).
Address string `yaml:"address"`
// Port is the TCP port to listen on. Default: 8080.
Port int `yaml:"port"`
}
ListenConfig configures an HTTP server's bind address and port.
type LoggingConfig ¶ added in v0.8.1
type LoggingConfig struct {
// Root is the directory where Thane writes category-partitioned JSONL
// datasets and logs.db. Relative paths are resolved from the working
// directory (typically ~/Thane). Defaults to "logs" when omitted.
// Set to an explicit empty string (root: "") to disable filesystem
// logging entirely.
Root *string `yaml:"root"`
// Dir is the deprecated alias for Root. It is kept for backwards
// compatibility with older configs.
Dir *string `yaml:"dir"`
// Level sets the minimum level retained in the structured datasets and
// SQLite log index. Valid values: trace, debug, info, warn, error.
// Default: info.
Level string `yaml:"level"`
// Format sets the stdout log format fallback when stdout.format is
// omitted. "json" produces one JSON object per line; "text" produces
// human-readable key=value pairs. Default: json.
Format string `yaml:"format"`
// Stdout configures the operator-facing stdout surface separately from
// the structured filesystem datasets.
Stdout LoggingStdoutConfig `yaml:"stdout"`
// Datasets controls which structured filesystem datasets are written
// under Root.
Datasets LoggingDatasetsConfig `yaml:"datasets"`
// RetentionDays controls how many days DEBUG and TRACE log index
// entries are kept. Entries at INFO and above are kept indefinitely.
// Default: 7. Set to 0 to disable pruning (keep everything).
RetentionDays *int `yaml:"retention_days"`
// RetainContent enables content retention in the log index database.
// When true, system prompts (deduplicated by SHA-256 hash), tool call
// arguments/results, and request/response content are persisted to
// logs.db alongside the existing log index. Default: false.
RetainContent bool `yaml:"retain_content"`
// MaxContentLength is the maximum number of characters retained per
// tool result or message body. Longer content is truncated. This
// bounds storage growth while preserving enough for diagnostics.
// Default: 4096. Set to 0 for unlimited.
MaxContentLength *int `yaml:"max_content_length"`
// ContentArchiveDays is the age threshold in days for archiving
// log_request_content rows to JSONL flat files. Rows older than
// this are exported to ContentArchiveDir and removed from logs.db.
// Default: 90. Set to 0 to disable archival.
ContentArchiveDays *int `yaml:"content_archive_days"`
// ContentArchiveDir is the directory where monthly JSONL archive
// files are written. Relative paths are resolved from the working
// directory. Defaults to {logging.root}/archive when unset.
ContentArchiveDir *string `yaml:"content_archive_dir"`
}
LoggingConfig configures Thane's structured filesystem log datasets, stdout policy, and SQLite-backed log/query retention.
func (LoggingConfig) ContentArchiveDirPath ¶ added in v0.9.1
func (l LoggingConfig) ContentArchiveDirPath(logDir string) string
ContentArchiveDirPath returns the resolved archive directory path. When ContentArchiveDir is nil (unset in YAML), it falls back to logDir/archive where logDir is the caller-supplied logging root.
func (LoggingConfig) ContentArchiveDuration ¶ added in v0.9.1
func (l LoggingConfig) ContentArchiveDuration() time.Duration
ContentArchiveDuration returns the age threshold after which retained content rows should be archived to JSONL. Defaults to 90 days when unset. A value of 0 disables archival.
func (LoggingConfig) ContentMaxLength ¶ added in v0.9.1
func (l LoggingConfig) ContentMaxLength() int
ContentMaxLength returns the maximum character count for retained content fields. Defaults to 4096 when unset. A value of 0 means unlimited; negative values are treated as misconfiguration and clamped to the default (4096).
func (LoggingConfig) DatasetEnabled ¶ added in v0.9.1
func (l LoggingConfig) DatasetEnabled(dataset string) bool
DatasetEnabled reports whether a named structured dataset should be written under logging.root.
func (LoggingConfig) DirPath ¶ added in v0.8.1
func (l LoggingConfig) DirPath() string
DirPath returns the resolved logging root path. It is kept as a compatibility alias for older callers that still speak in terms of a log directory rather than dataset root.
func (LoggingConfig) RetentionDaysDuration ¶ added in v0.8.1
func (l LoggingConfig) RetentionDaysDuration() time.Duration
RetentionDaysDuration returns the retention period for low-level log index entries. When nil (omitted in YAML), defaults to 7 days. A zero or negative value disables pruning entirely.
func (LoggingConfig) RootPath ¶ added in v0.9.1
func (l LoggingConfig) RootPath() string
RootPath returns the resolved logging root. When Root is nil and Dir is also nil, it returns the default "logs". When either is an explicit empty string, it returns "" which signals that filesystem logging is disabled.
func (LoggingConfig) StdoutEnabled ¶ added in v0.9.1
func (l LoggingConfig) StdoutEnabled() bool
StdoutEnabled returns whether the operator-facing stdout stream is on. Defaults to true when stdout.enabled is omitted.
func (LoggingConfig) StdoutFormatValue ¶ added in v0.9.1
func (l LoggingConfig) StdoutFormatValue() string
StdoutFormatValue returns the configured stdout format or falls back to the logging default format.
func (LoggingConfig) StdoutLevelValue ¶ added in v0.9.1
func (l LoggingConfig) StdoutLevelValue() string
StdoutLevelValue returns the configured stdout level or falls back to the dataset/index level.
type LoggingDatasetConfig ¶ added in v0.9.1
type LoggingDatasetConfig struct {
// Enabled controls whether the dataset is written. When omitted, each
// dataset uses its built-in default.
Enabled *bool `yaml:"enabled"`
}
LoggingDatasetConfig controls one structured JSONL dataset.
type LoggingDatasetsConfig ¶ added in v0.9.1
type LoggingDatasetsConfig struct {
Events LoggingDatasetConfig `yaml:"events"`
Requests LoggingDatasetConfig `yaml:"requests"`
Access LoggingDatasetConfig `yaml:"access"`
Loops LoggingDatasetConfig `yaml:"loops"`
Delegates LoggingDatasetConfig `yaml:"delegates"`
Envelopes LoggingDatasetConfig `yaml:"envelopes"`
}
LoggingDatasetsConfig configures the initial structured JSONL datasets written under logging.root.
type LoggingStdoutConfig ¶ added in v0.9.1
type LoggingStdoutConfig struct {
// Enabled controls whether Thane writes operator-facing logs to
// stdout. Default: true.
Enabled *bool `yaml:"enabled"`
// Level sets the minimum stdout log level. When empty, it falls back
// to Logging.Level.
Level string `yaml:"level"`
// Format sets stdout formatting. When empty, it falls back to
// Logging.Format.
Format string `yaml:"format"`
}
LoggingStdoutConfig configures the operator-facing stdout stream.
type LoopsConfig ¶ added in v0.9.1
type LoopsConfig struct {
// MaxRunning caps the number of concurrently running loops across
// the live registry. Zero means unlimited.
MaxRunning int `yaml:"max_running"`
// Definitions is the set of config-defined loop specs. These specs
// are immutable at runtime; dynamic loop creation lives in the
// persistent overlay registry instead.
Definitions []looppkg.Spec `yaml:"definitions"`
}
LoopsConfig configures immutable loops-ng definitions loaded from the config file.
type MCPConfig ¶ added in v0.6.0
type MCPConfig struct {
// Servers lists the MCP servers to connect to at startup.
Servers []MCPServerConfig `yaml:"servers"`
}
MCPConfig configures MCP (Model Context Protocol) client connections to external tool servers. Each server provides additional tools that are discovered dynamically and bridged into the agent's tool registry.
type MCPServerConfig ¶ added in v0.6.0
type MCPServerConfig struct {
// Name is a short identifier used in tool namespacing and logging
// (e.g., "home-assistant", "github"). Required.
Name string `yaml:"name"`
// Transport is the connection type: "stdio" or "http". Required.
Transport string `yaml:"transport"`
// Command is the executable to spawn (stdio transport only).
Command string `yaml:"command"`
// Args are command-line arguments for the subprocess (stdio transport only).
Args []string `yaml:"args"`
// Env are additional environment variables for the subprocess
// (stdio transport only). Format: "KEY=VALUE".
Env []string `yaml:"env"`
// URL is the MCP server endpoint (http transport only).
URL string `yaml:"url"`
// Headers are additional HTTP headers sent with every request
// (http transport only). Useful for authentication tokens.
Headers map[string]string `yaml:"headers"`
// IncludeTools is an optional allowlist of MCP tool names to
// bridge. When non-empty, only tools in this list are registered.
// Cannot be used together with ExcludeTools.
IncludeTools []string `yaml:"include_tools"`
// ExcludeTools is an optional blocklist of MCP tool names to skip.
// Cannot be used together with IncludeTools.
ExcludeTools []string `yaml:"exclude_tools"`
// DefaultTags lists tool tags/toolsets assigned to bridged MCP tools
// from this server unless a per-tool override replaces them. Prefer
// using this to attach MCP tools to existing capability/toolbox
// groups instead of hand-maintaining every bridged tool name inside
// capability_tags.*.tools.
DefaultTags []string `yaml:"default_tags"`
// Tools contains optional metadata overrides keyed by the raw MCP tool
// name reported by the server.
Tools map[string]MCPToolConfig `yaml:"tools"`
}
MCPServerConfig describes a single MCP server endpoint. Each server is identified by a short name used for tool namespacing and logging.
type MCPToolConfig ¶ added in v0.9.1
type MCPToolConfig struct {
// Enabled controls whether the tool is bridged. Nil keeps the default
// include/exclude behavior.
Enabled *bool `yaml:"enabled"`
// Tags replaces the server default tags for this tool when non-empty.
Tags []string `yaml:"tags"`
// Description overrides the description reported by the MCP server.
Description string `yaml:"description"`
}
MCPToolConfig configures operator-supplied metadata for a bridged MCP tool.
type MQTTConfig ¶ added in v0.5.3
type MQTTConfig struct {
// Broker is the MQTT broker URL (e.g., "mqtts://host:8883"
// or "mqtt://host:1883").
Broker string `yaml:"broker"`
// Username for MQTT broker authentication.
Username string `yaml:"username"`
// Password for MQTT broker authentication.
Password string `yaml:"password"`
// DiscoveryPrefix is the Home Assistant MQTT discovery topic
// prefix. Default: "homeassistant".
DiscoveryPrefix string `yaml:"discovery_prefix"`
// DeviceName drives MQTT topic paths and HA entity IDs. Example:
// "aimee-thane" produces sensor.aimee_thane_uptime in HA.
DeviceName string `yaml:"device_name"`
// PublishIntervalSec is how often (in seconds) sensor states are
// re-published to the broker. Default: 60. Minimum: 10.
PublishIntervalSec int `yaml:"publish_interval"`
// Subscriptions lists MQTT topics to subscribe to for ambient
// awareness. Messages are received and logged but not autonomously
// acted upon. Supports MQTT wildcard characters (+ and #).
Subscriptions []SubscriptionConfig `yaml:"subscriptions"`
// Telemetry configures operational metric publishing. When enabled,
// a separate mqtt-telemetry loop publishes system health, token usage,
// loop states, and other operational data as native HA sensors.
Telemetry TelemetryConfig `yaml:"telemetry"`
}
MQTTConfig configures the MQTT connection for Home Assistant device discovery and sensor state publishing. When MQTTConfig.Configured returns true, Thane connects to the broker at startup and registers as an HA device with availability tracking and runtime sensors.
func (MQTTConfig) Configured ¶ added in v0.5.3
func (c MQTTConfig) Configured() bool
Configured reports whether both Broker and DeviceName are set. A partial configuration is treated as unconfigured — Thane will start without MQTT publishing.
type MediaConfig ¶ added in v0.7.1
type MediaConfig struct {
// YtDlpPath is the explicit path to the yt-dlp binary. If empty,
// the binary is located via exec.LookPath at startup.
YtDlpPath string `yaml:"yt_dlp_path"`
// CookiesFile is an optional path to a Netscape-format cookie file
// for accessing auth-required content (e.g., age-restricted videos).
// Mutually exclusive with CookiesFromBrowser.
CookiesFile string `yaml:"cookies_file"`
// CookiesFromBrowser extracts cookies directly from an installed
// browser, eliminating the need for manual cookie file export.
// Value is passed to yt-dlp's --cookies-from-browser flag.
// Examples: "chrome", "firefox", "chrome:Profile 1".
// Mutually exclusive with CookiesFile.
CookiesFromBrowser string `yaml:"cookies_from_browser"`
// SubtitleLanguage is the preferred subtitle language code.
// Default: "en".
SubtitleLanguage string `yaml:"subtitle_language"`
// MaxTranscriptChars limits the transcript text returned in-context.
// Longer transcripts are truncated. Default: 50000.
MaxTranscriptChars int `yaml:"max_transcript_chars"`
// WhisperModel is the Ollama model name for audio transcription
// fallback when no subtitles are available. Default: "large-v3".
WhisperModel string `yaml:"whisper_model"`
// TranscriptDir is the directory for durable transcript storage.
// Each transcript is saved as a markdown file with YAML frontmatter.
// If empty, transcripts are returned in-context only (not persisted).
// This is typically a generated/artifact root rather than a curated
// knowledge root.
TranscriptDir string `yaml:"transcript_dir"`
// SummarizeModel is the preferred model for transcript summarization.
// When set, it is passed as a routing hint (soft preference, not
// override). If empty, the router selects an appropriate local model.
SummarizeModel string `yaml:"summarize_model"`
// FeedCheckInterval is how often (in seconds) to poll followed RSS/Atom
// feeds for new entries. Set to a positive value to enable polling (e.g.,
// 3600 for hourly). Default: 0 (disabled). No default is applied —
// users must opt in by setting a positive interval.
FeedCheckInterval int `yaml:"feed_check_interval"`
// MaxFeeds limits the number of feeds that can be followed.
// Default: 50.
MaxFeeds int `yaml:"max_feeds"`
// Analysis configures the structured media analysis pipeline
// that writes analysis output to an Obsidian-compatible vault.
Analysis AnalysisConfig `yaml:"analysis"`
}
MediaConfig configures the media transcript retrieval tool and RSS/Atom feed monitoring.
type MetacognitiveConfig ¶ added in v0.7.1
type MetacognitiveConfig struct {
// Enabled controls whether the metacognitive loop starts. Default: false.
Enabled bool `yaml:"enabled"`
// MinSleep is the minimum allowed sleep duration between iterations.
// The LLM cannot request a shorter sleep via set_next_sleep.
// Default: "2m". Parsed as a Go duration string.
MinSleep string `yaml:"min_sleep"`
// MaxSleep is the maximum allowed sleep duration between iterations.
// Default: "30m".
MaxSleep string `yaml:"max_sleep"`
// DefaultSleep is used when the LLM does not call set_next_sleep.
// Default: "10m".
DefaultSleep string `yaml:"default_sleep"`
// Jitter is the sleep randomization factor (0.0–1.0). A value of
// 0.2 means the actual sleep varies by ±20% of the computed
// duration. Default: 0.2. Set to 0.0 for deterministic timing.
Jitter float64 `yaml:"jitter"`
// SupervisorProbability is the chance (0.0–1.0) that each wake
// uses a frontier model with supervisor-augmented prompt.
// Default: 0.1. Set to 0.0 to disable supervisor iterations.
SupervisorProbability float64 `yaml:"supervisor_probability"`
// Router configures model routing for normal (non-supervisor)
// iterations.
Router MetacognitiveRouterConfig `yaml:"router"`
// SupervisorRouter configures model routing for supervisor
// iterations (frontier model with augmented prompt).
SupervisorRouter MetacognitiveRouterConfig `yaml:"supervisor_router"`
}
MetacognitiveConfig configures the self-regulating metacognitive loop. The loop runs perpetually in a background goroutine, using LLM calls to reason about the environment and self-determine its sleep duration between iterations. See issue #319.
type MetacognitiveRouterConfig ¶ added in v0.7.1
type MetacognitiveRouterConfig struct {
// QualityFloor is the minimum quality rating (1–10) for model
// selection. Default: 3 for normal iterations, 8 for supervisor.
QualityFloor int `yaml:"quality_floor"`
}
MetacognitiveRouterConfig holds routing hints for metacognitive iterations.
type ModelConfig ¶
type ModelConfig struct {
Name string `yaml:"name"` // Model identifier (e.g., "claude-opus-4-20250514")
Provider string `yaml:"provider"` // Provider name: ollama, anthropic, lmstudio. Defaults to ollama when no resource is set
Resource string `yaml:"resource"` // Named provider resource from models.resources for this deployment
SupportsTools bool `yaml:"supports_tools"` // Optional per-deployment tool-use override. When omitted, runtime/provider capability is used.
SupportsStreaming *bool `yaml:"supports_streaming"` // Optional per-deployment streaming override. Nil inherits observed runtime/provider capability.
ContextWindow int `yaml:"context_window"` // Optional per-deployment context-window override. Zero inherits observed runtime metadata.
Speed int `yaml:"speed"` // Relative speed rating, 1 (slow) to 10 (fast)
Quality int `yaml:"quality"` // Relative quality rating, 1 (low) to 10 (high)
CostTier int `yaml:"cost_tier"` // 0=local/free, 1=cheap, 2=moderate, 3=expensive
MinComplexity string `yaml:"min_complexity"` // Minimum task complexity: simple, moderate, complex
// contains filtered or unexported fields
}
ModelConfig describes a single LLM model's identity and capabilities. The model router uses these fields to select the best model for each request.
func (ModelConfig) SupportsToolsOverride ¶ added in v0.9.1
func (m ModelConfig) SupportsToolsOverride() (*bool, bool)
SupportsToolsOverride reports whether supports_tools was explicitly set in config, returning the configured value when present.
func (*ModelConfig) UnmarshalYAML ¶ added in v0.9.1
func (m *ModelConfig) UnmarshalYAML(node *yaml.Node) error
UnmarshalYAML preserves whether optional override fields were explicitly authored in config so later layers can distinguish operator policy from omitted defaults.
type ModelServerConfig ¶ added in v0.9.1
type ModelServerConfig struct {
URL string `yaml:"url"`
// Provider name for this resource. Default: ollama.
Provider string `yaml:"provider"`
// APIKey is an optional bearer/API key for providers that require auth.
APIKey string `yaml:"api_key"`
// IdleTTLSeconds asks supported local runners to keep models warm for
// this many idle seconds after an inference request. LM Studio honors
// this via the native `ttl` request field on inference endpoints.
// Zero lets the runner use its default behavior.
IdleTTLSeconds int `yaml:"idle_ttl_seconds"`
}
ModelServerConfig describes a named model provider resource.
type ModelsConfig ¶
type ModelsConfig struct {
// Default is the model name used when no specific model is requested.
Default string `yaml:"default"`
// OllamaURL is backward-compatible shorthand for a default Ollama
// resource. It is used when Resources is empty. When Resources is
// populated, callers should prefer the normalized resource catalog.
OllamaURL string `yaml:"ollama_url"`
// Resources defines named model provider resources such as Ollama
// instances running on different machines. When empty, OllamaURL is
// treated as a synthetic resource named "default".
Resources map[string]ModelServerConfig `yaml:"resources"`
// LocalFirst prefers local (cost_tier=0) models over cloud models
// when routing decisions are made by the model router.
LocalFirst bool `yaml:"local_first"`
// RecoveryModel is a fast, cheap model used to generate summaries
// when the primary model times out after completing tool calls.
// When empty, timeout recovery falls back to a static message
// listing the tools that were used.
RecoveryModel string `yaml:"recovery_model"`
// Available lists all models that Thane can route to. Each entry
// maps a model name to a provider and declares its capabilities.
Available []ModelConfig `yaml:"available"`
}
ModelsConfig configures LLM model routing. Each model in the Available list is mapped to a provider; requests are routed based on the model name. Unknown models fall through to Ollama.
func (ModelsConfig) PreferredOllamaURL ¶ added in v0.9.1
func (c ModelsConfig) PreferredOllamaURL() string
PreferredOllamaURL returns the best available Ollama URL for callers that still need one local endpoint outside the routed model catalog. Preference order is: a resource named "default", then the first configured Ollama resource by name, then the legacy OllamaURL field.
type OllamaAPIConfig ¶
type OllamaAPIConfig struct {
Enabled bool `yaml:"enabled"`
Address string `yaml:"address"` // Bind address; empty = all interfaces
Port int `yaml:"port"` // Default: 11434
}
OllamaAPIConfig configures the optional Ollama-compatible API server. When Enabled is true, Thane exposes an additional HTTP server that speaks the Ollama wire protocol, allowing Home Assistant's built-in Ollama integration to use Thane as a drop-in backend.
type PersonConfig ¶ added in v0.6.0
type PersonConfig struct {
// Track is a list of Home Assistant person entity IDs to monitor
// (e.g., ["person.nugget", "person.dan"]). Each entry must begin
// with "person.". An empty list disables person tracking.
Track []string `yaml:"track"`
// Devices maps tracked person entity IDs to their wireless device
// MAC addresses. Used by the UniFi poller to determine which person
// a wireless client belongs to for room-level presence.
Devices map[string][]DeviceMapping `yaml:"devices"`
// APRooms maps AP names (e.g., "ap-hor-office") to human-readable
// room names (e.g., "office"). Only APs listed here contribute to
// room presence; unlisted APs are ignored.
APRooms map[string]string `yaml:"ap_rooms"`
}
PersonConfig configures household member presence tracking. When Track contains entity IDs, the person tracker maintains in-memory state from Home Assistant and injects a presence summary into the agent's system prompt on every wake.
type PlatformConfig ¶ added in v0.9.1
type PlatformConfig struct {
Enabled bool `yaml:"enabled"`
Providers map[string]PlatformProviderConfig `yaml:"providers"`
}
PlatformConfig configures the WebSocket endpoint for native platform provider connections (e.g. macOS app). When enabled, providers can connect and register capabilities for bidirectional service dispatch.
Each entry in Providers maps an account name (e.g. "nugget", "aimee") to a set of per-device tokens. Multiple devices under the same account share an identity but are independently addressable by client_id.
func (PlatformConfig) Configured ¶ added in v0.9.1
func (c PlatformConfig) Configured() bool
Configured reports whether the platform provider endpoint is enabled and has at least one provider with at least one token.
func (PlatformConfig) TokenIndex ¶ added in v0.9.1
func (c PlatformConfig) TokenIndex() map[string]string
TokenIndex builds a map from token → account name for O(1) auth lookups.
func (PlatformConfig) Validate ¶ added in v0.9.1
func (c PlatformConfig) Validate() error
Validate checks platform configuration for internal consistency. When enabled, at least one provider must have a non-empty token, and tokens must not be shared across accounts.
type PlatformProviderConfig ¶ added in v0.9.1
type PlatformProviderConfig struct {
Tokens []string `yaml:"tokens"`
}
PlatformProviderConfig defines the tokens for a single account identity. Each token typically corresponds to a different device running a platform agent (e.g. thane-agent-macos on a laptop vs desktop).
type PrewarmConfig ¶ added in v0.7.1
type PrewarmConfig struct {
// Enabled controls whether subject-keyed fact injection is active.
// Default: false.
Enabled bool `yaml:"enabled"`
// MaxFacts caps the number of subject-matched facts injected per
// wake. Default: 10.
MaxFacts int `yaml:"max_facts"`
// Archive configures Phase 2 pre-warming: injecting relevant past
// conversation excerpts alongside Layer 1 knowledge. See issue #404.
Archive ArchivePrewarmConfig `yaml:"archive"`
}
PrewarmConfig configures context pre-warming for cold-start loops. When enabled, subject-keyed facts are injected into the system prompt before the model sees the triggering event. This reduces wasted iterations where the model discovers facts it should already have. See issue #338.
type PricingEntry ¶ added in v0.7.1
type PricingEntry struct {
InputPerMillion float64 `yaml:"input_per_million"`
OutputPerMillion float64 `yaml:"output_per_million"`
}
PricingEntry defines per-million-token costs for a model in USD.
type ProvenanceConfig ¶ added in v0.8.4
type ProvenanceConfig struct {
// Path is the directory for the provenance git repository.
// Supports ~ expansion. This is a legacy seam toward future
// integrity-tracked document roots and no longer defines the fixed
// workspace/core locations of always-on identity files. Example:
// ~/Thane/core
Path string `yaml:"path"`
// SigningKey is the path to an SSH private key used to sign
// commits. The key is loaded at startup and held in memory.
// Supports ~ expansion. Example: ~/.ssh/id_ed25519
SigningKey string `yaml:"signing_key"`
}
ProvenanceConfig configures git-backed file storage with SSH signature enforcement. When both Path and SigningKey are set, files are automatically committed with cryptographic signatures on every write.
func (ProvenanceConfig) Configured ¶ added in v0.8.4
func (c ProvenanceConfig) Configured() bool
Configured reports whether the provenance store has both a path and signing key set.
type SearchConfig ¶ added in v0.2.3
type SearchConfig struct {
// Default is the provider name to use when the agent doesn't
// specify one. If empty, the first configured provider is used.
Default string `yaml:"default"`
// SearXNG configures the self-hosted SearXNG meta-search provider.
SearXNG search.SearXNGConfig `yaml:"searxng"`
// Brave configures the Brave Search API provider.
Brave search.BraveConfig `yaml:"brave"`
}
SearchConfig configures web search providers. At least one provider must be configured for the web_search tool to be available.
func (SearchConfig) Configured ¶ added in v0.2.3
func (c SearchConfig) Configured() bool
Configured reports whether at least one search provider is configured.
type ShellExecConfig ¶
type ShellExecConfig struct {
// Enabled must be true for the agent to execute any shell commands.
Enabled bool `yaml:"enabled"`
// WorkingDir is the working directory for command execution. If
// empty, the process's current directory is used.
WorkingDir string `yaml:"working_dir"`
// DeniedPatterns are substrings that cause a command to be rejected.
// Checked before AllowedPrefixes. Example: "rm -rf /".
DeniedPatterns []string `yaml:"denied_patterns"`
// AllowedPrefixes restricts commands to those whose first token
// matches one of these prefixes. An empty list means all commands
// are allowed (subject to DeniedPatterns).
AllowedPrefixes []string `yaml:"allowed_prefixes"`
// DefaultTimeoutSec is the maximum wall-clock time a command may
// run before being killed. Default: 30.
DefaultTimeoutSec int `yaml:"default_timeout_sec"`
}
ShellExecConfig configures the agent's ability to execute shell commands on the host. Disabled by default for safety. When enabled, commands are filtered through allow and deny lists before execution.
type SignalConfig ¶ added in v0.7.0
type SignalConfig struct {
// Enabled controls whether the Signal bridge starts.
Enabled bool `yaml:"enabled"`
// Command is the signal-cli executable path (e.g., "signal-cli").
Command string `yaml:"command"`
// Account is the phone number to use (e.g., "+15124232707").
// Passed as the -a flag to signal-cli.
Account string `yaml:"account"`
// Args are additional command-line arguments appended after the
// standard "-a ACCOUNT jsonRpc" arguments.
Args []string `yaml:"args"`
// RateLimitPerMinute caps how many inbound messages per sender
// are processed per minute. Zero disables rate limiting.
// Default: 10.
RateLimitPerMinute int `yaml:"rate_limit_per_minute"`
// SessionIdleMinutes is the idle timeout in minutes for session
// rotation. When a new Signal message arrives and the last message
// from that sender was more than this many minutes ago, the
// previous session is ended (triggering background summarization)
// and a fresh one begins on the next agent loop call. Zero or
// omitted disables idle rotation.
SessionIdleMinutes int `yaml:"session_idle_minutes"`
// Routing configures how Signal messages are routed to LLM models.
// All fields are optional; defaults preserve the original hardcoded
// behavior (quality_floor=6, mission=conversation, delegation_gating=disabled).
Routing SignalRoutingConfig `yaml:"routing"`
// AttachmentSourceDir is the directory where signal-cli stores
// downloaded attachments. Defaults to
// ~/.local/share/signal-cli/attachments when empty.
AttachmentSourceDir string `yaml:"attachment_source_dir"`
// AttachmentDir is the workspace subdirectory where received
// attachments are copied for agent access. Defaults to
// {workspace}/signal-attachments when empty and workspace is set.
AttachmentDir string `yaml:"attachment_dir"`
// MaxAttachmentSize is the maximum attachment size in bytes that
// will be processed. Attachments exceeding this are described but
// not copied. Zero means no limit.
MaxAttachmentSize int64 `yaml:"max_attachment_size"`
// HandleTimeout bounds how long a single inbound message may be
// processed (agent loop + response send). This needs to be long
// enough to cover tool execution (e.g., media_transcript) plus
// the subsequent LLM response. Default: 10m.
HandleTimeout time.Duration `yaml:"handle_timeout"`
}
SignalConfig configures the native Signal message bridge using signal-cli's jsonRpc mode over stdin/stdout.
func (SignalConfig) Configured ¶ added in v0.7.0
func (c SignalConfig) Configured() bool
Configured reports whether the Signal bridge has the minimum required configuration (enabled with a command and account).
type SignalRoutingConfig ¶ added in v0.7.0
type SignalRoutingConfig struct {
// Model sets an explicit model for Signal messages. When non-empty,
// the router is bypassed entirely. Empty means use the router with
// the hint-based defaults below.
Model string `yaml:"model"`
// QualityFloor is the minimum model quality rating (1-10) passed
// to the router. Default: "6".
QualityFloor string `yaml:"quality_floor"`
// Mission describes the task context for routing. Default: "conversation".
Mission string `yaml:"mission"`
// DelegationGating controls whether delegation-first tool gating
// is active. Default: "disabled".
DelegationGating string `yaml:"delegation_gating"`
}
SignalRoutingConfig controls model selection for Signal messages. When Model is set, the router is bypassed entirely and the named model handles every Signal message. The remaining fields are passed as routing hints when the router is active.
func (SignalRoutingConfig) LoopProfile ¶ added in v0.9.1
func (c SignalRoutingConfig) LoopProfile() router.LoopProfile
LoopProfile converts the Signal routing config into the shared LoopProfile representation used by wake-style entrypoints.
It intentionally maps only the fields exposed by SignalRoutingConfig. LoopProfile-only fields such as ExcludeTools and InitialTags are omitted until Signal grows explicit config for them.
type StateWindowConfig ¶ added in v0.7.0
type StateWindowConfig struct {
// MaxEntries is the circular buffer capacity. When the buffer is
// full, the oldest entry is overwritten. Default: 50.
MaxEntries int `yaml:"max_entries"`
// MaxAgeMinutes controls how long entries remain visible. Entries
// older than this are excluded from the context output at read
// time. Default: 30.
MaxAgeMinutes int `yaml:"max_age_minutes"`
}
StateWindowConfig configures the rolling window of recent Home Assistant state changes injected into the agent's system prompt.
type SubscribeConfig ¶ added in v0.6.0
type SubscribeConfig struct {
// EntityGlobs is a list of glob patterns (using path.Match syntax)
// that select which entity IDs to process. Examples: "person.*",
// "binary_sensor.*door*", "light.living_room". An empty list
// means all entities are accepted.
EntityGlobs []string `yaml:"entity_globs"`
// RateLimitPerMinute caps how many state changes per entity are
// forwarded per minute. Zero means no rate limiting.
RateLimitPerMinute int `yaml:"rate_limit_per_minute"`
}
SubscribeConfig configures entity-level filtering and rate limiting for Home Assistant WebSocket state_changed event subscriptions.
type SubscriptionConfig ¶ added in v0.5.4
type SubscriptionConfig struct {
// Topic is the MQTT topic filter (e.g., "homeassistant/+/+/state",
// "frigate/events"). Supports MQTT wildcard characters.
Topic string `yaml:"topic"`
// Wake, when non-nil, enables agent wake on this topic. Messages
// arriving on the topic trigger an agent conversation using the
// profile's routing configuration. When nil, messages are received
// for ambient awareness only (debug-logged, not acted upon).
Wake *router.LoopProfile `yaml:"wake,omitempty"`
}
SubscriptionConfig describes a single MQTT topic subscription. Each entry is subscribed on every broker (re-)connect. Wildcards (+ and #) are supported per the MQTT specification.
type TelemetryConfig ¶ added in v0.8.4
type TelemetryConfig struct {
// Enabled activates the mqtt-telemetry loop. Requires MQTT to be
// configured (broker + device_name).
Enabled bool `yaml:"enabled"`
// Interval is how often (in seconds) telemetry metrics are
// collected and published. Default: 60. Minimum: 10.
Interval int `yaml:"interval"`
}
TelemetryConfig configures MQTT telemetry publishing. When Enabled is true and MQTT is configured, a dedicated loop publishes operational metrics (DB sizes, token usage, loop states, etc.) as native Home Assistant sensors via MQTT Discovery.
type UnifiConfig ¶ added in v0.6.0
type UnifiConfig struct {
// URL is the base URL of the UniFi controller
// (e.g., "https://192.168.1.1").
URL string `yaml:"url"`
// APIKey is the API key for UniFi controller authentication.
// Sent as X-API-KEY header.
APIKey string `yaml:"api_key"`
// PollIntervalSec is how often (in seconds) to poll for wireless
// client station data. Default: 30. Minimum: 10.
PollIntervalSec int `yaml:"poll_interval"`
}
UnifiConfig configures the UniFi network controller connection for room-level presence detection via AP client associations.
func (UnifiConfig) Configured ¶ added in v0.6.0
func (c UnifiConfig) Configured() bool
Configured reports whether both URL and APIKey are set, indicating the UniFi integration should be enabled.
type VisionConfig ¶ added in v0.8.4
type VisionConfig struct {
Enabled bool `yaml:"enabled"` // enable auto-analysis on image ingest
Model string `yaml:"model"` // vision model name (must be in models.available)
Prompt string `yaml:"prompt"` // custom analysis prompt; empty uses default
Timeout string `yaml:"timeout"` // per-image timeout (Go duration); empty → 30s
}
VisionConfig configures automatic vision analysis of image attachments. When enabled, images are analyzed on ingest using a vision-capable LLM and the resulting description is cached in the attachment metadata index.
func (VisionConfig) ParsedTimeout ¶ added in v0.8.4
func (v VisionConfig) ParsedTimeout() time.Duration
ParsedTimeout returns the configured timeout as a time.Duration, defaulting to 30 seconds when empty. Invalid durations are caught by Config.Validate; this method assumes the value is already validated and falls back to the default on any parse error.
type WorkspaceConfig ¶
type WorkspaceConfig struct {
// Path is the root directory for file operations. If empty, file
// tools are disabled entirely. In multi-root setups, this should be
// the common writable parent that contains Thane-owned roots such as
// core/, talents/, knowledge/, generated/, and scratchpad/.
Path string `yaml:"path"`
// ReadOnlyDirs are additional directories the agent can read from
// but not write to. Useful for compatibility or reference roots that
// must remain outside Thane's writable authority, such as a legacy
// workspace or an external vault mirror.
ReadOnlyDirs []string `yaml:"read_only_dirs"`
}
WorkspaceConfig configures the agent's sandboxed file system access. When Path is set, the agent can read and write files within that directory. All paths passed to file tools are resolved relative to Path and cannot escape it.