Documentation
¶
Overview ¶
Package configuration: command categorization and force-flag detection. (split from config_risk_subagent.go)
Package configuration: git-command classification and risk-pattern matching. (split from config_risk_subagent.go)
Package configuration: heredoc / quoted-string stripping for risk classifiers. (split from config_risk_subagent.go)
Package configuration: risk-profile resolution and rule factory. (split from config_risk_subagent.go)
Package configuration: core risk-level and risk-profile types. (split from config_risk_subagent.go)
Package configuration: SubagentType struct, IsCriticalOperation, and risk evaluation. (split from config_risk_subagent.go)
Package configuration: low-context mode (LCM) abstraction. ContextProfile is the resolved shape every downstream call site reads.
Package configuration provides high-level configuration and credential resolution for providers, including support for custom providers and stored credentials.
Index ¶
- Constants
- func AllowedSkillsPath(projectRoot string) string
- func BootstrapIsolatedConfig(configDir string) error
- func CanonicalizeCustomProviderName(name string) (string, error)
- func CredentialsDir() (string, error)
- func CredentialsDirFromDir(base string) (string, error)
- func DebugPrintConfig(config *Config, apiKeys *APIKeys)
- func DeleteCustomProvider(name string) error
- func EffectiveContextCapErrorf(got int) error
- func EnsureProviderAPIKey(provider string, apiKeys *APIKeys) error
- func EnsureWorkspaceConfigDir(workspaceRoot string) error
- func GetAPIKeysPath() (string, error)
- func GetAvailableProviders() []string
- func GetConfigDir() (string, error)
- func GetConfigLocalPath() (string, error)
- func GetConfigPath() (string, error)
- func GetCustomProviderPath(name string) (string, error)
- func GetEnvSimple(suffix string) string
- func GetGlobalProvidersDir() (string, error)
- func GetProviderDisplayName(provider string) string
- func GetProviderEnvVarName(provider string) string
- func GetProvidersDir() (string, error)
- func GetWorkspaceConfigPath(workspaceRoot string) string
- func HasProviderAuth(provider string) bool
- func Initialize() (*Config, *APIKeys, error)
- func IsConfigConflict(err error) bool
- func IsCriticalOperation(command string) bool
- func IsValidRiskProfile(s string) bool
- func IsValidRiskProfileWithConfig(s string, cfg *Config) bool
- func IsWorkspaceConfigPresent(workspaceRoot string) bool
- func KnownProviderNames() []string
- func LoadCustomProviders() (map[string]CustomProviderConfig, error)
- func LoadCustomProvidersFromDir(providersDir string) (map[string]CustomProviderConfig, error)
- func LookupEnv(suffix string) (string, bool)
- func MapProviderStringToClientType(cfg *Config, raw string) (api.ClientType, error)
- func MigrateCommandPolicies(cfg *Config)
- func MigrateConfig(raw map[string]interface{}, targetVersion string) (map[string]interface{}, error)
- func MigrateConfigFileAPIKeys(configPath string) error
- func MigrateEmbeddedAPIKeys(providers map[string]CustomProviderConfig) error
- func MigrateLegacyCustomProviders(cfg *Config) (map[string]CustomProviderConfig, error)
- func NeedsMigration() bool
- func PromptForAPIKey(provider string) (string, error)
- func ReadAllowedSkills(projectRoot string) map[string]bool
- func RegisterMigration(from, to string, fn MigrationFunc)deprecated
- func RequiresAPIKey(provider string) bool
- func ResolveEffectiveContextCap(cfg *Config, nativeContextWindow int) (int, error)
- func ResolveProviderModel(cfg *Config, explicitProvider, explicitModel string) (api.ClientType, string, error)
- func ResolveWorkspaceConfigFile(dir string, dirIsHome bool) string
- func RunMigration() error
- func SaveAPIKeys(keys *APIKeys) error
- func SaveAPIKeysToDir(keys *APIKeys, configDir string) error
- func SaveCustomProvider(cfg CustomProviderConfig) error
- func SelectProvider(currentProvider string, apiKeys *APIKeys) (string, error)
- func SetEnv(suffix, value string) error
- func SetProviderConfigLookup(fn ProviderConfigLookupFunc)
- func SetProviderDisplayNameLookup(fn ProviderDisplayNameLookupFunc)
- func SetProviderNamesLookup(fn ProviderNamesLookupFunc)
- func SetValidateAndSaveAPIKeyValidation(skip bool)
- func ShowNextSteps(provider, configDir string)
- func ShowWelcomeMessage()
- func UnsetEnv(suffix string)
- func ValidateAndSaveAPIKey(provider, key string) (int, error)
- func ValidateCustomProviderEndpoint(raw string) error
- func WorkspaceConfigDir(workspaceRoot string) string
- func WorkspaceConfigWritePath(workspaceRoot string) string
- func WorkspaceEmbeddingIndexEnabled(workspaceRoot string) bool
- func WriteAllowedSkills(projectRoot string, ids []string) error
- type APIKeys
- func (a APIKeys) Get(provider string) string
- func (keys *APIKeys) GetAPIKey(provider string) string
- func (keys *APIKeys) HasAPIKey(provider string) bool
- func (keys *APIKeys) PopulateFromEnvironment() bool
- func (keys *APIKeys) PopulateFromJSONEnv() bool
- func (a *APIKeys) Set(provider, key string)
- func (keys *APIKeys) SetAPIKey(provider, key string)
- type APITimeoutConfig
- type AutoApproveRules
- type ChangeTrackingConfig
- type CombinedAssessment
- type CommandPolicies
- type CommandPolicyAction
- type CommandRule
- type ComputerUseConfig
- type Config
- func (c *Config) ExplicitKeyPaths() []string
- func (c *Config) GetAllEnabledSkills() map[string]Skill
- func (c *Config) GetCommitModel() string
- func (c *Config) GetCommitProvider() string
- func (c *Config) GetCompletionModel() string
- func (c *Config) GetCompletionProvider() string
- func (c *Config) GetMCPTimeout() time.Duration
- func (c *Config) GetModelForProvider(provider string) string
- func (c *Config) GetRefreshSystemPromptOnModelChange() bool
- func (c *Config) GetReviewModel() string
- func (c *Config) GetReviewProvider() string
- func (c *Config) GetSkill(id string) *Skill
- func (c *Config) GetSkillPath(id string) string
- func (c *Config) GetSubagentMaxDepth() int
- func (c *Config) GetSubagentMaxParallel() int
- func (c *Config) GetSubagentModel() string
- func (c *Config) GetSubagentParallelEnabled() bool
- func (c *Config) GetSubagentProvider() string
- func (c *Config) GetSubagentType(id string) *SubagentType
- func (c *Config) GetSubagentTypeModel(id string) string
- func (c *Config) GetSubagentTypeProvider(id string) string
- func (c *Config) IsExplicitlySet(path string) bool
- func (c *Config) IsPersonaDisabled(id string) bool
- func (c *Config) Save() error
- func (c *Config) SaveToDir(dir string) error
- func (c *Config) SaveToDirAs(dir, fileName string) error
- func (c *Config) SectionExplicitlySet(section string) bool
- func (c *Config) SetCommitModel(model string)
- func (c *Config) SetCommitProvider(provider string)
- func (c *Config) SetCompletionModel(model string)
- func (c *Config) SetCompletionProvider(provider string)
- func (c *Config) SetModelForProvider(provider, model string)
- func (c *Config) SetPersonaDisabled(id string, disabled bool)
- func (c *Config) SetReviewModel(model string)
- func (c *Config) SetReviewProvider(provider string)
- func (c *Config) SetSubagentModel(model string)
- func (c *Config) SetSubagentProvider(provider string)
- func (c *Config) Validate() error
- type ConfigConflictError
- type ContextMode
- type ContextProfile
- type CustomProviderConfig
- type EditApprovalConfig
- type EmbeddingIndexConfig
- func (e *EmbeddingIndexConfig) IsAutoIndex() bool
- func (e *EmbeddingIndexConfig) IsEnabled() bool
- func (e *EmbeddingIndexConfig) IsExperimental() bool
- func (e *EmbeddingIndexConfig) SetAutoIndex(v bool)
- func (e *EmbeddingIndexConfig) SetEnabled(v bool)
- func (e *EmbeddingIndexConfig) SetExperimental(v bool)
- type KnownProviderInfo
- type LanguageServerOverride
- type Manager
- func NewManager() (*Manager, error)
- func NewManagerSilent() (*Manager, error)
- func NewManagerWithConfig(cfg *Config, apiKeys *APIKeys) *Manager
- func NewManagerWithDir(configDir string) (*Manager, error)
- func NewManagerWithLayers(globalDir, workspaceDir string) (*Manager, error)
- func NewTestManager(t *testing.T) (*Manager, func())
- func (m *Manager) AddMCPServer(name string, server mcp.MCPServerConfig) error
- func (m *Manager) EnrichCustomProviders()
- func (m *Manager) EnsureAPIKey(clientType api.ClientType) error
- func (m *Manager) GetAPIKeyForProvider(clientType api.ClientType) string
- func (m *Manager) GetAPIKeys() *APIKeys
- func (m *Manager) GetAvailableProviders() []api.ClientType
- func (m *Manager) GetConfig() *Config
- func (m *Manager) GetConfigDir() string
- func (m *Manager) GetMCPConfig() mcp.MCPConfig
- func (m *Manager) GetModelForProvider(clientType api.ClientType) string
- func (m *Manager) GetProvider() (api.ClientType, error)
- func (m *Manager) HasAPIKey(clientType api.ClientType) bool
- func (m *Manager) MapStringToClientType(s string) (api.ClientType, error)
- func (m *Manager) RefreshAPIKeys() error
- func (m *Manager) Reload() error
- func (m *Manager) ResolveProviderModel(explicitProvider, explicitModel string) (api.ClientType, string, error)
- func (m *Manager) SaveAPIKeys() errordeprecated
- func (m *Manager) SaveConfig() error
- func (m *Manager) SelectNewProvider() (api.ClientType, error)
- func (m *Manager) SetMCPEnabled(enabled bool) error
- func (m *Manager) SetModelForProvider(clientType api.ClientType, model string) error
- func (m *Manager) SetProvider(clientType api.ClientType) error
- func (m *Manager) UpdateConfig(mutator func(*Config) error) error
- func (m *Manager) UpdateConfigNoSave(mutator func(*Config) error) error
- type MigrationFunc
- type NotificationsConfig
- type PersistentContextConfig
- type ProviderAuthMetadata
- type ProviderConfigLookupFunc
- type ProviderDiscoveryModel
- type ProviderDisplayNameLookupFunc
- type ProviderNamesLookupFunc
- type RevisionRetentionConfig
- type RiskLevel
- type RiskProfile
- type SecurityPolicy
- type SecurityPolicyAction
- type SecurityRule
- type ShellConfig
- type ShellPattern
- type Skill
- type SubagentType
- type TrainingConfig
- type VisionConfig
- type WakeupConfig
- type WorkspaceOverlayConfig
Constants ¶
const ( ConfigVersion = "2.0" ConfigDirName = ".sprout" ConfigFileName = "config.json" // WorkspaceConfigFileName is the per-workspace config file. Deliberately // different from ConfigFileName to avoid a collision when the workspace // root is $HOME (both layers would otherwise share the same directory). WorkspaceConfigFileName = "workspace.json" // ConfigLocalFileName is the user-scope machine-local override file. // Same schema as ConfigFileName, higher precedence, never committed. // Lives in the config dir alongside config.json. ConfigLocalFileName = "config.local.json" // WorkspaceLocalFileName is the workspace-scope personal override file. // Same schema as WorkspaceConfigFileName, higher precedence within the // workspace layer, gitignored. WorkspaceLocalFileName = "workspace.local.json" APIKeysFileName = "api_keys.json" OutputVerbosityCompact = "compact" OutputVerbosityDefault = "default" OutputVerbosityVerbose = "verbose" )
const ContextFloor = 8_000
ContextFloor is the minimum context window at which sprout will start. Below this, the agent is unusable.
const CredentialsDirName = "credentials"
CredentialsDirName is the subdirectory under the config dir that holds sensitive files (api_keys.json, key.age, mode files). Created 0700.
const EffectiveContextCapMinimum = 1024
EffectiveContextCapMinimum is the minimum cap a user may set via Config.MaxContextTokens.
const ProvidersDirName = "providers"
Variables ¶
This section is empty.
Functions ¶
func AllowedSkillsPath ¶
AllowedSkillsPath returns the full path to the allowed_skills file for a given project root.
func BootstrapIsolatedConfig ¶
BootstrapIsolatedConfig initializes an isolated config directory by cloning the user's main config.
func CanonicalizeCustomProviderName ¶
CanonicalizeCustomProviderName lowercases and trims the input, then rejects anything outside the [a-z0-9_-] character set. Empty input is also rejected.
func CredentialsDir ¶ added in v0.17.17
CredentialsDir returns the credentials subdirectory under the config root, creating it with mode 0700 if missing.
func CredentialsDirFromDir ¶ added in v0.17.17
CredentialsDirFromDir returns the credentials subdirectory under an explicit base, creating it with mode 0700 if missing.
func DebugPrintConfig ¶
DebugPrintConfig prints current configuration for debugging
func DeleteCustomProvider ¶
func EffectiveContextCapErrorf ¶ added in v0.17.7
EffectiveContextCapErrorf builds the error message returned when a user-configured cap falls below the minimum.
func EnsureProviderAPIKey ¶
EnsureProviderAPIKey ensures the provider has an API key, prompting if needed
func EnsureWorkspaceConfigDir ¶ added in v0.17.17
EnsureWorkspaceConfigDir creates the workspace .sprout/ directory if missing and ships a .gitignore covering personal overrides and state directories. Idempotent — safe to call on every startup.
func GetAPIKeysPath ¶
GetAPIKeysPath returns the full path to the API keys file
func GetAvailableProviders ¶
func GetAvailableProviders() []string
GetAvailableProviders returns all supported providers
func GetConfigDir ¶
GetConfigDir returns the configuration directory path
func GetConfigLocalPath ¶ added in v0.17.17
GetConfigLocalPath returns the path to the user-scope local override file (config.local.json). The file may not exist; callers should stat before reading.
func GetConfigPath ¶
GetConfigPath returns the full path to the config file
func GetCustomProviderPath ¶
GetCustomProviderPath returns the path where a custom provider JSON file is stored. Always resolves to the global providers directory (~/.config/sprout/providers/) so that save, load, and delete all agree on the same location regardless of SPROUT_CONFIG.
func GetEnvSimple ¶
GetEnvSimple checks SPROUT_* for a variable name suffix. E.g., GetEnvSimple("CONFIG") checks SPROUT_CONFIG.
func GetGlobalProvidersDir ¶ added in v0.17.11
GetGlobalProvidersDir returns the global providers directory (~/.config/sprout/providers/). Custom providers are always stored here regardless of SPROUT_CONFIG — they are user-global resources, not project-scoped. This prevents a split-brain where a provider saved from a scoped session is invisible to another scope and can't be deleted from a different scope.
Uses getDefaultConfigDir (HOME-based, ignores SPROUT_CONFIG) so the location is stable across all sessions.
func GetProviderDisplayName ¶ added in v0.16.12
GetProviderDisplayName returns a user-friendly name for the provider. Lookup chain:
- Static display-name map (generated from embedded configs — fastest and the common case for built-ins).
- Runtime factory (covers remote-only providers published to GitHub Pages whose display_name isn't baked into the static map).
- CustomProviders (user-defined local providers in config.json).
- Raw provider ID as a last resort.
func GetProviderEnvVarName ¶
func GetProvidersDir ¶
GetProvidersDir returns the global providers directory. Kept as an alias for GetGlobalProvidersDir for backward compatibility with callers that display or reference the providers directory (e.g. cmd/diag.go).
func GetWorkspaceConfigPath ¶
GetWorkspaceConfigPath returns the workspace-level config file to READ, or "" when there is no workspace layer (empty root, or $HOME).
Resolution: workspace.json if present, else the legacy config.json, else the workspace.json path (so callers can stat it and find nothing). Existing workspaces keep working untouched; nothing is moved or rewritten.
func HasProviderAuth ¶
HasProviderAuth checks whether a provider has a configured credential. For providers that don't require an API key (local providers), always returns true.
func Initialize ¶
Initialize loads or creates configuration with first-run setup
func IsConfigConflict ¶
IsConfigConflict is a convenience predicate so callers don't have to type-assert by hand. Returns false for nil errors.
func IsCriticalOperation ¶
IsCriticalOperation reports whether a command matches a pattern that is NEVER allowed regardless of profile, persona, or interactive approval. Reserved for operations that can permanently destroy the system or leave it in an unrecoverable state.
This is the single source of truth for "critical" across every security gate: the static classifier (agent_tools.isCriticalSystemOperation delegates here) and the persona risk cascade (EvaluateOperationRisk) both consult it, so the two gates can't disagree on what's critical.
Covers:
- rm -rf targeting root or the current dir (`/`, `/*`, `~`, `$HOME`, `.`, `*`)
- Classic fork-bomb pattern `:(){:|:&};:`
- Filesystem creation / raw disk overwrite (mkfs, dd to a block device)
- Mass process kills (killall -9 / -KILL)
- chmod 000 on a system root path
- Overwriting critical auth/system files (/etc/shadow, /etc/passwd, …)
Tokenized matching matches invokesCommand semantics so a benign substring inside a path or argument can't trigger a false positive.
func IsValidRiskProfile ¶
IsValidRiskProfile reports whether s names a known profile. User-defined profiles (added via Config.RiskProfiles) are NOT considered "valid" by this predicate — it only covers the baked-in names. Callers that need to accept user-defined profiles should check Config.RiskProfiles directly via ResolveRiskProfileRules.
func IsValidRiskProfileWithConfig ¶ added in v0.17.17
IsValidRiskProfileWithConfig reports whether s names a profile that is usable in the given config context: a baked-in profile name OR a user-defined name present in cfg.RiskProfiles. cfg may be nil, in which case only the built-in names are accepted.
func IsWorkspaceConfigPresent ¶
IsWorkspaceConfigPresent checks if a workspace config file exists
func KnownProviderNames ¶ added in v0.16.12
func KnownProviderNames() []string
KnownProviderNames returns the union of the compile-time provider list and whatever the runtime factory has registered (which includes embedded + filesystem + remote configs once pkg/factory.init has wired SetProviderNamesLookup). Static entries keep their generated order; runtime-only additions are appended in sorted order so the result is deterministic across calls.
func LoadCustomProviders ¶
func LoadCustomProviders() (map[string]CustomProviderConfig, error)
LoadCustomProviders loads all custom provider configs. It merges providers from the SPROUT_CONFIG-resolved directory and the global home directory (~/.config/sprout/providers/), with the global home directory winning on name conflicts.
This matches the layered manager's effective behavior (which only reads from global) and ensures the factory's /provider switch path resolves the same providers the /provider listing shows. Without the merge, a user running sprout from a project workspace with SPROUT_CONFIG overridden sees custom providers listed (via the layered manager) but receives a "not registered as a custom provider" error when trying to switch to one (via LoadCustomProviders, which would otherwise only see the SPROUT_CONFIG-resolved dir).
func LoadCustomProvidersFromDir ¶
func LoadCustomProvidersFromDir(providersDir string) (map[string]CustomProviderConfig, error)
LoadCustomProvidersFromDir loads all custom provider JSON files from the given directory. Used by LoadCustomProviders for both the SPROUT_CONFIG-resolved dir and the global home dir (see the merge behavior in LoadCustomProviders for context).
func MapProviderStringToClientType ¶
func MapProviderStringToClientType(cfg *Config, raw string) (api.ClientType, error)
MapProviderStringToClientType converts a provider string to ClientType, including built-in providers, custom providers from config, and factory-backed dynamic providers.
Inputs accepted in priority order:
- Built-in ClientType IDs ("openai", "ollama-local", "zai-coding", ...)
- Custom providers from cfg.CustomProviders
- Factory-loaded embedded provider configs
- Display names ("OpenAI", "Ollama (Local)", ...) — reverse lookup against api.GetProviderName output. Provided for backward compatibility with sessions that persisted display names before the SP-034-fix.
func MigrateCommandPolicies ¶ added in v0.17.5
func MigrateCommandPolicies(cfg *Config)
MigrateCommandPolicies converts legacy approved_shell_commands and approved_shell_command_patterns fields into the unified CommandPolicies format. It is a no-op when cfg.CommandPolicies is already non-nil.
After migration, the old fields remain in the config for backward compatibility but are no longer consulted by the policy engine.
func MigrateConfig ¶
func MigrateConfig(raw map[string]interface{}, targetVersion string) (map[string]interface{}, error)
MigrateConfig applies all necessary migration steps to bring raw config up to the target version. It takes a raw JSON map, determines the current version, and runs each step in order. Returns the migrated raw config or an error if a step fails or the chain cannot reach the target.
func MigrateConfigFileAPIKeys ¶
MigrateConfigFileAPIKeys migrates any api_key values found in config.json's custom_providers entries into the unified credential store, then strips the keys from the config file. Called on every Load() but exits immediately if the migration marker exists.
This is necessary because the CustomProviderConfig struct no longer has an APIKey field, so json.Unmarshal would silently drop these values.
func MigrateEmbeddedAPIKeys ¶
func MigrateEmbeddedAPIKeys(providers map[string]CustomProviderConfig) error
MigrateEmbeddedAPIKeys moves any api_key values found in custom provider JSON files into the unified credential store, then strips the key from the file. Called on every Load() but exits immediately if the migration marker exists.
func MigrateLegacyCustomProviders ¶
func MigrateLegacyCustomProviders(cfg *Config) (map[string]CustomProviderConfig, error)
MigrateLegacyCustomProviders copies any custom_providers entries that lived inline in config.json into the new file-per-provider format under ~/.config/sprout/providers/. Existing files win — the inline entry is only promoted when no file with the same name already exists.
func NeedsMigration ¶ added in v0.17.17
func NeedsMigration() bool
NeedsMigration returns true when a legacy ~/.sprout directory exists and no migration marker is present in the state dir.
func PromptForAPIKey ¶
PromptForAPIKey prompts the user for an API key with helpful guidance
func ReadAllowedSkills ¶
ReadAllowedSkills reads the .sprout/allowed_skills file from the given project root (cwd). Returns a set of allowed skill IDs. If the file does not exist, returns nil (meaning all skills are allowed).
func RegisterMigration
deprecated
func RegisterMigration(from, to string, fn MigrationFunc)
RegisterMigration adds a migration step to the global registry. It panics if a migration from the same source version is already registered (at-most-one-step-per-source prevents ambiguous chains).
Deprecated: Use registerMigration (returns an error) for new code. This wrapper is retained for backward compatibility with external callers and tests that expect the panic behavior.
func RequiresAPIKey ¶
RequiresAPIKey checks if a provider requires an API key.
func ResolveEffectiveContextCap ¶ added in v0.17.7
ResolveEffectiveContextCap returns the user's effective context cap: the smaller of the model's native window and the configured MaxContextTokens. Nil/zero cap means "no cap" (returns native window).
func ResolveProviderModel ¶
func ResolveProviderModel(cfg *Config, explicitProvider, explicitModel string) (api.ClientType, string, error)
ResolveProviderModel resolves provider and model using one canonical precedence path: 1) Explicit provider flag/arg 2) Explicit model in provider:model format (only when prefix is a valid provider) 3) SPROUT_PROVIDER env (with SPROUT_PROVIDER backward-compat) 4) SPROUT_MODEL env (provider:model format only when prefix is a valid provider, SPROUT_MODEL backward-compat) 5) Config last_used_provider 6) Auto-detected provider via DetermineProvider
Model precedence: 1) Explicit model (trimmed to model segment when provider:model format is recognized) 2) SPROUT_MODEL env (same parsing rule, SPROUT_MODEL backward-compat) 3) Config provider model default
func ResolveWorkspaceConfigFile ¶ added in v0.17.17
ResolveWorkspaceConfigFile picks the workspace config file inside dir.
dirIsHome disables the legacy fallback. This is load-bearing: `.sprout` is the per-directory sprout folder AND ~/.sprout is the user-level state directory, so at $HOME the legacy config.json is the user's global config, not a workspace config. Falling back to it there is precisely the aliasing this split exists to remove — and every existing install has that file, so without this guard the split would fix nothing for current users.
A user who deliberately runs with $HOME as the workspace still gets a real workspace layer; it just has to be an explicit ~/.sprout/workspace.json.
func RunMigration ¶ added in v0.17.17
func RunMigration() error
RunMigration moves each category of data from the legacy ~/.sprout directory to its new root. It is idempotent: already-moved files are skipped, and a marker prevents re-entry.
The legacy directory is left in place (empty of moved content) so a failed migration is diagnosable.
func SaveAPIKeys ¶
SaveAPIKeys saves API keys to the active backend. When keyring is active, each key is stored via SetToActiveBackend, keys that are no longer in the map are deleted from the keyring, and keys that are now in the keyring are cleaned from the encrypted file store. When file is active, it uses the existing file-based Save behavior.
Uses GetStorageBackend() (not GetStorageMode()) for consistent resolution.
func SaveAPIKeysToDir ¶
SaveAPIKeysToDir saves API keys to a specific config directory. This is like SaveAPIKeys() but routes file-backend saves through credentials.SaveToDir() instead of credentials.Save().
func SaveCustomProvider ¶
func SaveCustomProvider(cfg CustomProviderConfig) error
func SelectProvider ¶
SelectProvider allows user to select a provider interactively
func SetProviderConfigLookup ¶ added in v0.16.8
func SetProviderConfigLookup(fn ProviderConfigLookupFunc)
SetProviderConfigLookup registers a runtime config lookup. pkg/factory wires this to GlobalFactory().GetProviderConfig in its init() so that GetProviderAuthMetadata sees providers added by refreshFromRemote — not just embedded ones. The pattern mirrors credentials.SetProviderInfoFunc and exists to avoid a configuration → factory import cycle.
func SetProviderDisplayNameLookup ¶ added in v0.16.8
func SetProviderDisplayNameLookup(fn ProviderDisplayNameLookupFunc)
SetProviderDisplayNameLookup registers a runtime display-name lookup. pkg/factory wires this to GlobalFactory().GetProviderConfig in its init(); the callback returns cfg.DisplayName so providers published only to the remote registry surface their friendly label in the onboarding menu, model picker, and other UI surfaces.
func SetProviderNamesLookup ¶ added in v0.16.8
func SetProviderNamesLookup(fn ProviderNamesLookupFunc)
SetProviderNamesLookup registers a runtime provider-names lookup. pkg/factory wires this to GlobalFactory().GetAvailableProviders in its init() so onboarding / credential-enumeration loops surface providers added by refreshFromRemote.
func SetValidateAndSaveAPIKeyValidation ¶ added in v0.16.2
func SetValidateAndSaveAPIKeyValidation(skip bool)
SetValidateAndSaveAPIKeyValidation enables or disables the test-mode skip. Call with true in tests that need to store keys without network validation.
func ShowNextSteps ¶
func ShowNextSteps(provider, configDir string)
ShowNextSteps displays helpful next steps after successful setup
func ShowWelcomeMessage ¶
func ShowWelcomeMessage()
ShowWelcomeMessage displays a comprehensive welcome message for new users
func ValidateAndSaveAPIKey ¶
ValidateAndSaveAPIKey validates a new API key before storing it. If validation fails, the old key is preserved and an error is returned. Returns the number of models available if validation succeeds.
func ValidateCustomProviderEndpoint ¶ added in v0.16.25
ValidateCustomProviderEndpoint checks that raw is a syntactically valid http or https URL with a host. It runs before NormalizeCustomProviderConfig (which auto-appends /v1/chat/completions), so it catches typos that would otherwise produce a config that silently fails model discovery.
Returns nil for empty input — that's allowed at this layer so the wizard can detect "user pressed enter on an empty prompt" separately from "user typed garbage".
func WorkspaceConfigDir ¶ added in v0.17.17
WorkspaceConfigDir returns the .sprout directory for a workspace root, or "" when the workspace root is the user's home directory.
$HOME never gets a workspace layer. It is not a project, and the global config already *is* the config for a user working out of their home — a second layer there adds no expressiveness and one large footgun: NewManagerWithLayers makes the workspace dir the SAVE target, so a daemon running with workspace=$HOME (which is exactly what `sprout service install` produces) writes the full merged config to ~/.sprout/workspace.json on the first settings save. On the next start that machine-written file is read back as a deliberate per-workspace opt-in — which is how a global "embeddings: on" preference turned into indexing the entire home directory.
Returning "" here makes every caller degrade to "no workspace layer", which NewManagerWithLayers already handles by saving to the global dir instead.
func WorkspaceConfigWritePath ¶ added in v0.17.17
WorkspaceConfigWritePath returns where workspace-level config is written, or "" when there is no workspace layer (empty root, or $HOME — see WorkspaceConfigDir). Always the new filename — legacy files are read but never written back to.
func WorkspaceEmbeddingIndexEnabled ¶ added in v0.17.17
WorkspaceEmbeddingIndexEnabled reports whether the workspace's stored config explicitly opts into the embedding index (enabled && experimental), decoded tolerantly like RestoreEmbeddingIndex: missing, unreadable, or malformed config is not enabled. SPROUT_EXPERIMENTAL_EMBEDDINGS default-on is deliberately NOT consulted here — the env var is an operator escape hatch for daemon-side restore/socket hosting, while explicit tool operations must require the workspace's own opt-in.
func WriteAllowedSkills ¶
WriteAllowedSkills writes the .sprout/allowed_skills file in the given project root. IDs are sorted for deterministic output.
Types ¶
type APIKeys ¶
func LoadAPIKeys ¶
LoadAPIKeys loads API keys from the active backend. When keyring is active, it loads from both keyring (tracked providers) and the file store (for backward compatibility with keys stored before keyring was enabled). When file is active, it uses the existing file-based Load behavior.
Uses GetStorageBackend() (not GetStorageMode()) to ensure consistent resolution on first run — the auto-detection logic runs exactly once and persists the mode, so subsequent Load/Save calls see the same backend.
func LoadAPIKeysFromDir ¶
LoadAPIKeysFromDir loads API keys from a specific config directory. This is like LoadAPIKeys() but takes an explicit config directory instead of reading from environment variables. It's useful for test environments and other scenarios where you want to load from a specific location without mutating process state.
When keyring is active, it loads from both keyring (tracked providers) and the file store at the specified configDir (for backward compatibility with keys stored before keyring was enabled). When file is active, it uses LoadFromDir.
func (*APIKeys) HasAPIKey ¶
HasAPIKey checks if a provider has an API key set. Checks the in-memory map first, then falls back to the active backend (keyring or file store) for credentials not in the map.
func (*APIKeys) PopulateFromEnvironment ¶
PopulateFromEnvironment populates API keys from environment variables This is called on startup only to detect whether environment credentials are available.
func (*APIKeys) PopulateFromJSONEnv ¶
PopulateFromJSONEnv populates API keys from the SPROUT_API_KEYS_JSON environment variable. The value must be a JSON object mapping provider names to API key strings, e.g. {"openrouter":"sk-...","deepinfra":"di-..."}. This is designed for containerized/SaaS environments (e.g. Sprout Foundry) where keys are injected at runtime rather than stored in config files.
type APITimeoutConfig ¶
type APITimeoutConfig struct {
ConnectionTimeoutSec int `json:"connection_timeout_sec,omitempty"` // Time to establish connection (default: 300)
FirstChunkTimeoutSec int `json:"first_chunk_timeout_sec,omitempty"` // Time to receive first response (default: 600)
ChunkTimeoutSec int `json:"chunk_timeout_sec,omitempty"` // Max time between streaming chunks (default: 600)
OverallTimeoutSec int `json:"overall_timeout_sec,omitempty"` // Total request timeout (default: 1800)
CommitMessageTimeoutSec int `json:"commit_message_timeout_sec,omitempty"` // Timeout for commit message generation (default: 300)
}
APITimeoutConfig represents timeout settings for API calls
type AutoApproveRules ¶
type AutoApproveRules struct {
LowRiskOps []string `json:"low_risk,omitempty"` // Operations auto-approved by EA
MediumRiskOps []string `json:"medium_risk,omitempty"` // Operations the EA reasons about
HighRiskNever []string `json:"high_risk_never,omitempty"` // Pattern names always gated (rm_recursive, force_flag, ...)
// DefaultRisk is the level returned for operations that don't
// match any of the above. Default (empty) is "medium" — the
// classic EA behavior. Cautious profiles set this to "high"
// so unrecognized commands route to a prompt. Permissive /
// unrestricted set it to "low" so common operations auto-approve.
DefaultRisk RiskLevel `json:"default_risk,omitempty"`
}
AutoApproveRules controls the EA's sliding risk cascade for operation approvals.
func AutoApproveRulesForProfile ¶
func AutoApproveRulesForProfile(profile RiskProfile) AutoApproveRules
AutoApproveRulesForProfile returns the rules baked into each named profile. Unknown profiles fall back to RiskProfileDefault.
func DefaultAutoApproveRules ¶
func DefaultAutoApproveRules() AutoApproveRules
DefaultAutoApproveRules returns the default risk cascade rules for the EA persona.
func ResolveRiskProfileRules ¶
func ResolveRiskProfileRules(cfg *Config, profile RiskProfile) AutoApproveRules
ResolveRiskProfileRules returns the AutoApproveRules that should apply for the given profile name, honoring user overrides in Config.RiskProfiles before falling back to the baked-in defaults.
Resolution order:
- cfg.RiskProfiles[name] — user override (replaces builtins entirely; the user is the source of truth for any profile name they list, including the five named built-ins).
- AutoApproveRulesForProfile(name) — baked-in defaults for the known profile names. Unknown names fall through to the Default profile here.
cfg may be nil (no config loaded); in that case the baked-in rules are always returned.
type ChangeTrackingConfig ¶
type ChangeTrackingConfig struct {
// Enabled controls whether the change tracking subsystem is active. Default: true.
Enabled *bool `json:"enabled,omitempty"`
// ShellWalkEnabled controls whether the per-shell_command snapshot walk runs. Default: true.
ShellWalkEnabled *bool `json:"shell_walk_enabled,omitempty"`
// MaxFiles caps the number of files visited in a single walk. Default: 50000.
MaxFiles int `json:"max_files,omitempty"`
// MaxTotalBytes caps cumulative content bytes captured per walk. Default: 32 MiB.
MaxTotalBytes int64 `json:"max_total_bytes,omitempty"`
// MaxDurationMs is the wall-clock budget for a single walk. Default: 500ms.
MaxDurationMs int `json:"max_duration_ms,omitempty"`
// AutoSkipFileCountThreshold is the per-directory child count that triggers auto-skip. Default: 1500.
AutoSkipFileCountThreshold int `json:"auto_skip_file_count_threshold,omitempty"`
// RevisionRetention controls how the persistent revision store is compacted.
RevisionRetention *RevisionRetentionConfig `json:"revision_retention,omitempty"`
}
ChangeTrackingConfig gates and tunes the ChangeTracker.
func (*ChangeTrackingConfig) Resolve ¶
func (c *ChangeTrackingConfig) Resolve() ChangeTrackingConfig
Resolve fills in defaults for zero-value fields. Safe to call on nil.
type CombinedAssessment ¶
type CombinedAssessment struct {
ClassifierRisk int // SecurityRisk value (0=safe, 1=caution, 2=dangerous)
ClassifierBlocked bool // ShouldBlock from the classifier
ClassifierPrompt bool // ShouldPrompt from the classifier
ClassifierCategory string // RiskCategory string
PolicyAction SecurityPolicyAction
PolicyRule *SecurityRule // the matched rule, if any
PathAllowed bool
CommandDenied bool
OverrideAction string // reason if policy overrides classifier
}
CombinedAssessment holds the result of combining the security classifier with the workspace security policy.
IMPORTANT: To avoid a circular import (configuration -> agent_tools -> configuration), the ClassifierResult field is a generic map instead of a typed reference to agent_tools.SecurityResult. Callers that need the typed result should use CombinedSecurityAssessmentWithClassifier below.
func CombinedSecurityAssessment ¶
func CombinedSecurityAssessment( toolName string, args map[string]interface{}, policy *SecurityPolicy, classifierRisk int, classifierBlocked bool, classifierPrompt bool, classifierCategory string, ) *CombinedAssessment
CombinedSecurityAssessment evaluates a tool call against the security policy.
classifierRisk is the risk level from the security classifier (0=safe, 1=caution, 2=dangerous). classifierBlocked indicates whether the classifier says the call should be blocked. classifierPrompt indicates whether the classifier says the call should prompt the user. classifierCategory is the risk category string from the classifier (e.g. "read-only", "file-write").
This interface avoids a circular import between configuration and agent_tools.
type CommandPolicies ¶ added in v0.17.5
type CommandPolicies struct {
Rules []CommandRule `json:"rules"`
}
CommandPolicies is the top-level config structure for user command policies (SP-123). Rules are evaluated first-match-wins before the classifier and risk profile. The three actions (allow/ask/deny) override the default approval cascade.
type CommandPolicyAction ¶ added in v0.17.5
type CommandPolicyAction string
CommandPolicyAction determines what happens when a command matches a rule.
const ( // CommandPolicyAllow auto-approves the command, skipping classifier, risk // profile, and interactive prompt. Does not override Critical-tier blocks. CommandPolicyAllow CommandPolicyAction = "allow" // CommandPolicyAsk forces an interactive prompt, skipping allowlist and // classifier auto-approve. Classifier risk is still computed for display. CommandPolicyAsk CommandPolicyAction = "ask" // CommandPolicyDeny hard-blocks the command, returning an error immediately. CommandPolicyDeny CommandPolicyAction = "deny" )
type CommandRule ¶ added in v0.17.5
type CommandRule struct {
// Pattern is a glob pattern (Go path.Match syntax) to match against
// shell commands. For example: "git push*", "rm -rf /tmp/*".
Pattern string `json:"pattern"`
// Action determines the behavior when a command matches this rule.
Action CommandPolicyAction `json:"action"`
// Reason is an optional user note explaining why this rule exists.
Reason string `json:"reason,omitempty"`
}
CommandRule is a single user-defined command policy rule.
type ComputerUseConfig ¶ added in v0.16.12
type ComputerUseConfig struct {
// Enabled is the master switch. When false the computer_user tools are never registered.
Enabled bool `json:"enabled,omitempty"`
// MaxActionsPerMinute caps the action rate. Default: 60. Set to 0 to disable.
MaxActionsPerMinute int `json:"max_actions_per_minute,omitempty"`
// AuditLogDir is where per-session JSONL action logs are written.
// Default: ~/.config/sprout/computer_use_log when empty.
AuditLogDir string `json:"audit_log_dir,omitempty"`
// WorkspaceAllowlist lists workspace roots where computer use is auto-approved.
WorkspaceAllowlist []string `json:"workspace_allowlist,omitempty"`
// PanicKeyChord is the key chord that triggers the panic key. Defaults to "ctrl+shift+escape".
PanicKeyChord string `json:"panic_key_chord,omitempty"`
// DestructiveAppGate controls whether the destructive-app denylist gate is active. Default: true.
DestructiveAppGate bool `json:"destructive_app_gate,omitempty"`
// OverrideFilePath is an optional override of the denylist override file location.
OverrideFilePath string `json:"denylist_override_file,omitempty"`
}
ComputerUseConfig gates the computer_user persona's desktop-control tools. Off by default.
func (*ComputerUseConfig) Resolve ¶ added in v0.16.12
func (c *ComputerUseConfig) Resolve() ComputerUseConfig
Resolve returns a copy with defaults filled in for zero-value fields.
type Config ¶
type Config struct {
Version string `json:"version"`
// Provider and Model Configuration
LastUsedProvider string `json:"last_used_provider"`
ProviderModels map[string]string `json:"provider_models"`
ProviderPriority []string `json:"provider_priority"`
// Language Server Override Configuration
LanguageServers []LanguageServerOverride `json:"language_servers,omitempty"`
// MCP Configuration
MCP mcp.MCPConfig `json:"mcp"`
// Preferences
Preferences map[string]interface{} `json:"preferences,omitempty"`
// DisableCoordinatorAutoActivate opts out of the automatic activation of the
// coordinator persona (formerly Executive Assistant) when sprout starts in
// the user's $HOME directory. When true, no persona is auto-activated and
// the user must select one explicitly. Default false (auto-activate).
DisableCoordinatorAutoActivate bool `json:"disable_coordinator_auto_activate,omitempty"`
// AllowGitHistoryRewrite allows history-rewriting git commands
// (reset --hard, rebase, branch -D, tag -d) via shell_command
// without the git tool's approval flow. Default: false (gated).
AllowGitHistoryRewrite bool `json:"allow_git_history_rewrite,omitempty"`
// UnifiedRiskResolver enables the unified risk resolver. When true,
// gating uses a single ResolveToolRisk assessment instead of the
// legacy dual-gate path. Default: true.
UnifiedRiskResolver bool `json:"unified_risk_resolver,omitempty"`
// DaemonMultiSession enables concurrent browser windows in daemon mode.
// Each connection gets its own chat session and agent. Default: true.
DaemonMultiSession bool `json:"daemon_multi_session,omitempty"`
// ResourceDirectory stores captured web/vision resources relative to the current working directory.
// This can be overridden at runtime with --resource-directory.
ResourceDirectory string `json:"resource_directory,omitempty"`
// ReasoningEffort sets a global default reasoning effort for chat requests.
// Valid values: "low", "medium", "high". Empty means automatic selection.
ReasoningEffort string `json:"reasoning_effort,omitempty"`
// DisableThinking disables thinking/reasoning mode for thinking-capable models.
DisableThinking bool `json:"disable_thinking,omitempty"`
// SystemPromptText overrides the main agent system prompt inline.
// Empty means use the embedded default prompt.
SystemPromptText string `json:"system_prompt_text,omitempty"`
// RefreshSystemPromptOnModelChange re-derives the agent's system prompt
// on every provider/model swap. Defaults to false.
RefreshSystemPromptOnModelChange bool `yaml:"refresh_system_prompt_on_model_change,omitempty" json:"refresh_system_prompt_on_model_change,omitempty"`
// SkipPrompt - for non-interactive mode
SkipPrompt bool `json:"skip_prompt,omitempty"`
// RiskProfile selects a named preset for the shell-command risk cascade:
// readonly / cautious / default / permissive / unrestricted.
RiskProfile string `json:"risk_profile,omitempty"`
// ContextMode selects a named context-engine preset: "" (full default) | "full" | "low_context".
ContextMode ContextMode `json:"context_mode,omitempty"`
// RiskProfiles allows the user to override the baked-in rules for any named profile.
RiskProfiles map[string]AutoApproveRules `json:"risk_profiles,omitempty"`
// ApprovedShellCommands is the user's persistent allowlist of literal
// shell command strings that auto-approve through the high-risk cascade.
ApprovedShellCommands []string `json:"approved_shell_commands,omitempty"`
// ApprovedShellCommandPatterns is the user's persistent allowlist of glob
// patterns for shell commands that auto-approve through the high-risk cascade.
ApprovedShellCommandPatterns []string `json:"approved_shell_command_patterns,omitempty"`
// CommandPolicies is the unified command policy layer with three actions:
// allow (auto-approve), ask (force prompt), deny (hard block).
CommandPolicies *CommandPolicies `json:"command_policies,omitempty"`
// DismissedPrompts tracks which one-time prompts the user has dismissed.
DismissedPrompts map[string]bool `json:"dismissed_prompts,omitempty"`
// API Timeout Configuration (in seconds)
APITimeouts *APITimeoutConfig `json:"api_timeouts,omitempty"`
// Custom Providers Configuration
CustomProviders map[string]CustomProviderConfig `json:"custom_providers,omitempty"`
// Command History Configuration
CommandHistoryByPath map[string][]string `json:"command_history_by_path,omitempty"`
HistoryIndexByPath map[string]int `json:"history_index_by_path,omitempty"`
// Change History Configuration
HistoryScope string `json:"history_scope,omitempty"` // "project" or "global"
// Subagent Configuration
SubagentProvider string `json:"subagent_provider,omitempty"` // Provider for subagents (defaults to LastUsedProvider)
SubagentModel string `json:"subagent_model,omitempty"` // Model for subagents (defaults to provider's default model)
// SubagentTypes is hydrated from the embedded catalog at config load time.
// It is NOT persisted (json:"-"): personas are catalog-fixed and user
// customization is intentionally not supported. Use DisabledPersonas to
// hide specific personas from /persona list and from subagent spawning.
SubagentTypes map[string]SubagentType `json:"-"`
// DisabledPersonas holds canonical persona IDs the user has hidden via
// `/persona <id> disable`. The catalog entries themselves are never
// mutated; resolution checks this list and treats disabled IDs as absent.
DisabledPersonas []string `json:"disabled_personas,omitempty"`
// DefaultSubagentPersona is the persona ID used when run_subagent is called
// without a persona argument. Defaults to "general" if unset. Setting this
// lets users redirect default spawns without editing the catalog.
DefaultSubagentPersona string `json:"default_subagent_persona,omitempty"`
SubagentMaxParallel int `json:"subagent_max_parallel,omitempty"` // Maximum number of parallel subagents (default: 2)
SubagentParallelEnabled *bool `json:"subagent_parallel_enabled,omitempty"` // Enable/disable parallel subagent execution (default: true)
SubagentMaxDepth int `json:"subagent_max_depth,omitempty"` // Maximum subagent nesting depth (default: 2)
// Commit Configuration
CommitProvider string `json:"commit_provider,omitempty"` // Provider for commit message generation (defaults to LastUsedProvider)
CommitModel string `json:"commit_model,omitempty"` // Model for commit message generation (defaults to provider's default model)
// Review Configuration
ReviewProvider string `json:"review_provider,omitempty"` // Provider for review commands (defaults to LastUsedProvider)
ReviewModel string `json:"review_model,omitempty"` // Model for review commands (defaults to provider's default model)
// Completion Configuration
CompletionProvider string `json:"completion_provider,omitempty"` // Provider for code completions (defaults to LastUsedProvider)
CompletionModel string `json:"completion_model,omitempty"` // Model for code completions (defaults to provider's default model)
// VisionFallbackToOCR enables transparent fallback to the OCR model when
// the primary vision model fails after retries. Default: true.
VisionFallbackToOCR bool `json:"vision_fallback_to_ocr,omitempty"`
// PDF OCR Configuration
PDFOCREnabled bool `json:"pdf_ocr_enabled,omitempty"` // Enable PDF OCR processing
PDFOCRProvider string `json:"pdf_ocr_provider,omitempty"` // Provider for PDF OCR (e.g., "ollama", "openai", "deepinfra")
PDFOCRModel string `json:"pdf_ocr_model,omitempty"` // Model for PDF OCR (e.g., "glm-ocr", "llama3.2-vision")
PDFOCRDownloaded bool `json:"pdf_ocr_downloaded,omitempty"` // Whether the model has been downloaded
// Embedding Index Configuration
EmbeddingIndex *EmbeddingIndexConfig `json:"embedding_index,omitempty"`
// Persistent Context Configuration
PersistentContext *PersistentContextConfig `json:"persistent_context,omitempty"`
// ComputerUse gates the computer_user persona's desktop-control tools. Off by default.
ComputerUse *ComputerUseConfig `json:"computer_use,omitempty"`
// Vision controls vision-pipeline runtime: parallel workers, concurrency cap, and batching.
Vision *VisionConfig `json:"vision,omitempty"`
// ChangeTracking gates the ChangeTracker shell-mutation snapshot walk.
ChangeTracking *ChangeTrackingConfig `json:"change_tracking,omitempty"`
// Skills Configuration
Skills map[string]Skill `json:"skills,omitempty"` // Agent Skills that can be loaded into context
// Zsh Command Execution
EnableZshCommandDetection bool `json:"enable_zsh_command_detection"` // Enable zsh-aware command detection (default: true)
AutoExecuteDetectedCommands bool `json:"auto_execute_detected_commands"` // Auto-execute detected commands without prompting (default: true)
// Security Policy Configuration
SecurityPolicy *SecurityPolicy `json:"security_policy,omitempty"`
// Shell is the user-configurable shell permission policy.
Shell ShellConfig `json:"shell,omitempty"`
// MaxContextTokens caps the effective context window. Nil or 0 means no cap.
MaxContextTokens *int `json:"max_context_tokens,omitempty"`
// Notifications controls how the agent notifies the user when long-running turns complete.
Notifications *NotificationsConfig `json:"notifications,omitempty"`
// EditApproval controls the per-hunk diff approval gate for agent file writes.
EditApproval *EditApprovalConfig `json:"edit_approval,omitempty"`
// OutputVerbosity controls how much inter-tool-call narration and
// streaming detail the UI shows. Valid values: "compact" (hide
// interim model messages, show only tool results and final text),
// "default" (show tool calls with results, show streaming final
// text), "verbose" (show everything including interim narration).
// Empty defaults to "default".
OutputVerbosity string `json:"output_verbosity,omitempty"`
// ShowToolInvocations controls whether the UI expands per-tool
// invocation details in the conversation output. When false, tool
// calls are collapsed/hidden. Defaults to true.
ShowToolInvocations bool `json:"show_tool_invocations,omitempty"`
// Wakeup controls auto-resume behavior for background task completions.
Wakeup WakeupConfig `json:"wakeup,omitempty"`
// Training controls opt-in session recording for training data collection. OFF by default.
Training TrainingConfig `json:"training,omitempty"`
// Other flags
FromAgent bool `json:"-"` // Internal flag, not persisted
// contains filtered or unexported fields
}
Config represents the unified application configuration
func LoadConfigWithLayers ¶
func LoadConfigWithLayers(globalPath, workspacePath, sessionPath, globalDir string) (*Config, error)
LoadConfigWithLayers loads configuration from three layers: globalPath -> workspacePath -> sessionPath (each overrides previous) Each layer is optional; missing layers are skipped. globalDir is the directory for global providers (used when globalPath is empty but custom providers need loading).
Local override files (config.local.json / workspace.local.json) are automatically loaded as a higher-precedence sibling within their scope: global config.json is overridden by global config.local.json, workspace workspace.json by workspace.local.json. The session layer (highest) remains unchanged.
func LoadOrInitConfig ¶
func MergeConfig ¶
MergeConfig merges two configs, with override taking precedence over base. The override config typically contains only changed fields (deltas). Returns a new config without modifying either input.
func NewConfig ¶
func NewConfig() *Config
NewConfig creates a new configuration with sensible defaults
func RedactConfig ¶
RedactConfig returns a copy of the configuration with all credential values redacted. The MCP server configs have their env vars and credentials maps redacted. This should be used for any display/export/diagnostic output where the config is shown to the user or logged.
func (*Config) ExplicitKeyPaths ¶ added in v0.17.17
ExplicitKeyPaths lists the recorded paths, sorted-insensitive. Exposed for tests and provenance reporting.
func (*Config) GetAllEnabledSkills ¶
GetAllEnabledSkills returns all enabled skills
func (*Config) GetCommitModel ¶
GetCommitModel returns the configured model for commit message generation If not explicitly set, falls back to the provider's default model
func (*Config) GetCommitProvider ¶
GetCommitProvider returns the configured provider for commit message generation. Returns an empty string if no explicit commit provider is set; callers should surface this and offer interactive provider selection.
func (*Config) GetCompletionModel ¶ added in v0.17.17
GetCompletionModel returns the configured model for code completions. If not explicitly set, falls back to the provider's default model.
func (*Config) GetCompletionProvider ¶ added in v0.17.17
GetCompletionProvider returns the configured provider for code completions. Returns an empty string if no explicit completion provider is set; callers should fall back to the main provider.
func (*Config) GetMCPTimeout ¶
GetMCPTimeout returns the MCP timeout as a time.Duration
func (*Config) GetModelForProvider ¶
GetModelForProvider returns the configured model for a provider. Returns an empty string if no model is configured for the provider; callers handle this by running model selection against the live provider API.
func (*Config) GetRefreshSystemPromptOnModelChange ¶ added in v0.17.7
GetRefreshSystemPromptOnModelChange (Spec B) reports whether the agent should re-derive its system prompt on every provider/model swap instead of freezing it at agent-creation time.
When false (the default), the prompt is set once in initAgentFromResolvedProvider and never touched on subsequent SetProvider/SetModel calls. This preserves bit-identical behavior for existing sessions that may have observed and relied on a particular prompt composition.
When true, the agent's refreshSystemPrompt() (called from setClient) re-runs GetEmbeddedSystemPromptForProfile against the active provider and context window. The configured SystemPromptText override still wins — only the embedded portion is re-derived.
This getter exists alongside the field so call sites read through a stable accessor (and so future migrations can change the storage shape without touching every read site). A nil-safe return of false matches the field's zero-value default and keeps bare Config literals in tests working without explicit initialization.
func (*Config) GetReviewModel ¶
GetReviewModel returns the configured model for review commands If not explicitly set, falls back to the provider's default model
func (*Config) GetReviewProvider ¶
GetReviewProvider returns the configured provider for review commands. Returns an empty string if no explicit review provider is set; callers should surface this and offer interactive provider selection.
func (*Config) GetSkill ¶
GetSkill retrieves a skill configuration by ID Returns nil if the skill doesn't exist or is disabled
func (*Config) GetSkillPath ¶
GetSkillPath returns the full path to a skill directory
func (*Config) GetSubagentMaxDepth ¶
GetSubagentMaxDepth returns the maximum subagent nesting depth. Defaults to 2 if not configured or set to 0.
func (*Config) GetSubagentMaxParallel ¶
GetSubagentMaxParallel returns the maximum number of parallel subagents Defaults to 2 if not configured or set to 0
func (*Config) GetSubagentModel ¶
GetSubagentModel returns the configured model for subagents If not explicitly set, falls back to the provider's default model
func (*Config) GetSubagentParallelEnabled ¶
GetSubagentParallelEnabled returns whether parallel subagent execution is enabled Defaults to true if not explicitly set (nil pointer)
func (*Config) GetSubagentProvider ¶
GetSubagentProvider returns the configured provider for subagents. Returns an empty string if no explicit subagent provider is set; callers inherit from the parent agent's provider or treat empty as a signal that no explicit subagent provider is configured.
func (*Config) GetSubagentType ¶
func (c *Config) GetSubagentType(id string) *SubagentType
GetSubagentType retrieves a subagent type configuration by ID or alias. Personas are catalog-fixed (loaded from pkg/personas/configs/*.json at startup) — there is no user-override merge path. Returns nil if the persona does not exist or has been disabled via Config.DisabledPersonas.
func (*Config) GetSubagentTypeModel ¶
GetSubagentTypeModel returns the model for a specific subagent type Falls back to the general subagent model if not specified
func (*Config) GetSubagentTypeProvider ¶
GetSubagentTypeProvider returns the provider for a specific subagent type Falls back to the general subagent provider if not specified
func (*Config) IsExplicitlySet ¶ added in v0.17.17
IsExplicitlySet reports whether a dotted JSON path was present in the layer this config was decoded from. Configs built in memory (tests, defaults) carry no provenance and always report false.
func (*Config) IsPersonaDisabled ¶ added in v0.16.4
IsPersonaDisabled reports whether the given persona ID has been disabled by the user (via /persona <id> disable). The canonical ID after alias resolution is matched; a disabled persona is returned as nil by GetSubagentType and filtered from GetAvailablePersonaIDs.
func (*Config) SaveToDir ¶
SaveToDir saves the configuration to a specific directory, bypassing GetConfigPath() (which reads the SPROUT_CONFIG/SPROUT_CONFIG env vars). Use this when a Manager has an explicit configDir so that saves go to the correct location even after the env var has been restored. SaveToDir writes the config as ConfigFileName inside dir.
func (*Config) SaveToDirAs ¶ added in v0.17.17
SaveToDirAs writes the config to dir under an explicit filename. Workspace layers use WorkspaceConfigFileName so they can never collide with the user-level config.json when the workspace root is $HOME.
func (*Config) SectionExplicitlySet ¶ added in v0.17.17
SectionExplicitlySet reports whether any field under a section was named by the layer, e.g. SectionExplicitlySet("embedding_index").
func (*Config) SetCommitModel ¶
SetCommitModel sets the model for commit message generation
func (*Config) SetCommitProvider ¶
SetCommitProvider sets the provider for commit message generation
func (*Config) SetCompletionModel ¶ added in v0.17.17
SetCompletionModel sets the model for code completions
func (*Config) SetCompletionProvider ¶ added in v0.17.17
SetCompletionProvider sets the provider for code completions
func (*Config) SetModelForProvider ¶
SetModelForProvider sets the model for a specific provider. The test provider is silently rejected to prevent it from leaking into the persisted config via direct Config access.
func (*Config) SetPersonaDisabled ¶ added in v0.16.4
SetPersonaDisabled adds or removes a persona ID from DisabledPersonas. Idempotent; aliases are normalized to the canonical form before storage.
func (*Config) SetReviewModel ¶
SetReviewModel sets the model for review commands
func (*Config) SetReviewProvider ¶
SetReviewProvider sets the provider for review commands
func (*Config) SetSubagentModel ¶
SetSubagentModel sets the model for subagents
func (*Config) SetSubagentProvider ¶
SetSubagentProvider sets the provider for subagents
type ConfigConflictError ¶
type ConfigConflictError struct {
// Path is the absolute path of the config file.
Path string
// LoadedModTime / LoadedSize is what the in-memory Config remembers
// from when it was last loaded.
LoadedModTime time.Time
LoadedSize int64
// CurrentModTime / CurrentSize is what's on disk right now.
CurrentModTime time.Time
CurrentSize int64
}
ConfigConflictError is returned from Config.Save when the on-disk config file has been modified since the in-memory Config was last loaded. Indicates another writer (another agent process, another webui tab, a hand-edit) changed the file out from under us.
SP-034-4b. Callers should surface this to the user with a "Settings changed on disk" prompt and offer to reload before retrying.
func (*ConfigConflictError) Error ¶
func (e *ConfigConflictError) Error() string
Error satisfies the error interface. Format is stable and parsed by the webui's `config_conflict` error mapper — don't reorganize fields without coordinating with `pkg/webui/websocket_message_handlers.go`.
type ContextMode ¶ added in v0.17.7
type ContextMode string
ContextMode is the user-facing context-engine selector. Empty string defaults to "full".
const ( // ContextModeFull is the default: all tools, full prompt, proactive context enabled. ContextModeFull ContextMode = "full" // ContextModeLowContext activates the LCM levers: curated 12-tool allowlist, lite prompt, etc. ContextModeLowContext ContextMode = "low_context" )
type ContextProfile ¶ added in v0.17.7
type ContextProfile struct {
Mode ContextMode `json:"mode,omitempty"`
ToolAllowlist []string `json:"tool_allowlist,omitempty"`
SystemPromptPath string `json:"system_prompt_path,omitempty"`
SkipProactiveContext bool `json:"skip_proactive_context,omitempty"`
CompactionTriggerFraction float64 `json:"compaction_trigger_fraction,omitempty"`
RecentTurnsToPreserve int `json:"recent_turns_to_preserve,omitempty"`
RepoMapDefaultDepth int `json:"repo_map_default_depth,omitempty"`
}
ContextProfile is the resolved shape of every context-engine lever. A zero value means "use all defaults" (full mode, no overrides).
func ResolveContextProfile ¶ added in v0.17.7
func ResolveContextProfile(cfg *Config, modelContextWindow int) (ContextProfile, error)
ResolveContextProfile picks the effective profile from the user's config plus the detected model context window. Precedence: hard floor > explicit ContextMode > auto-detect by window > full default.
type CustomProviderConfig ¶
type CustomProviderConfig struct {
Name string `json:"name"`
Endpoint string `json:"endpoint"`
ModelName string `json:"model_name"`
ContextSize int `json:"context_size"` // Default context size for provider
ModelContextSizes map[string]int `json:"model_context_sizes,omitempty"` // Per-model context sizes (e.g., "my-model": 131072)
ReasoningEffort string `json:"reasoning_effort,omitempty"` // Optional provider-specific reasoning effort override
Temperature *float64 `json:"temperature,omitempty"` // Optional default temperature
TopP *float64 `json:"top_p,omitempty"` // Optional default top_p
Parameters map[string]interface{} `json:"parameters,omitempty"` // Optional provider-specific default parameters
RequiresAPIKey bool `json:"requires_api_key"`
ToolCalls []string `json:"tool_calls,omitempty"` // Optional explicit tool allowlist; when set, only these tools are exposed
EnvVar string `json:"env_var,omitempty"` // Environment variable name for API key
ChunkTimeoutMs int `json:"chunk_timeout_ms,omitempty"` // Streaming chunk timeout in milliseconds
Conversion providers.MessageConversion `json:"message_conversion,omitempty"` // Message conversion configuration
SupportsVision bool `json:"supports_vision,omitempty"` // Whether this provider supports vision requests
VisionModel string `json:"vision_model,omitempty"` // Vision-capable model for this provider
VisionFallbackProvider string `json:"vision_fallback_provider,omitempty"` // Optional fallback provider for vision
VisionFallbackModel string `json:"vision_fallback_model,omitempty"` // Optional fallback model for vision provider
BillingType string `json:"billing_type,omitempty"` // Billing model: pay_per_token (default), subscription, free
}
CustomProviderConfig represents a custom model provider configuration
func NormalizeCustomProviderConfig ¶
func NormalizeCustomProviderConfig(cfg CustomProviderConfig) (CustomProviderConfig, error)
NormalizeCustomProviderConfig fills in defaults, trims whitespace, and rejects ill-formed values. Called by Save and Load paths so a hand-edited JSON file cannot put the runtime into a state where discovery silently fails later.
func (CustomProviderConfig) ModelsEndpoint ¶
func (c CustomProviderConfig) ModelsEndpoint() string
func (CustomProviderConfig) ToProviderConfig ¶
func (c CustomProviderConfig) ToProviderConfig() (*providers.ProviderConfig, error)
type EditApprovalConfig ¶ added in v0.16.12
type EditApprovalConfig struct {
Mode string `json:"mode,omitempty"`
Paths []string `json:"paths,omitempty"`
// ShellCommand enables per-part shell approval prompts (SP-093-2).
// When true, a multi-part shell command is split and each part is
// approved individually via Agent.RequestShellApproval. Default: false,
// which preserves the existing 4-option prompt for the whole command.
ShellCommand bool `json:"shell_command,omitempty" yaml:"shell_command,omitempty"`
}
EditApprovalConfig controls the per-hunk diff approval gate.
func (*EditApprovalConfig) Resolve ¶ added in v0.16.12
func (c *EditApprovalConfig) Resolve() EditApprovalConfig
func (*EditApprovalConfig) ShouldGate ¶ added in v0.16.12
func (c *EditApprovalConfig) ShouldGate(path string) bool
type EmbeddingIndexConfig ¶
type EmbeddingIndexConfig struct {
// Enabled controls whether the embedding index is active.
// nil means "not specified at this layer" — inherit from a broader layer.
Enabled *bool `json:"enabled,omitempty"`
// Experimental is a second, independent gate required alongside Enabled.
// Off by default, and — critically — a workspace config persisted before
// this field existed has no "experimental" key at all, so it decodes to
// nil (off) regardless of what Enabled was set to. That's deliberate:
// full-workspace auto-indexing was found to cause severe, unbounded
// native-memory growth (multi-GB spikes, outside what Go's own memory
// accounting or limits can see or bound — see pkg/embedding/index.go).
// Existing users with Enabled already true from before this gate must
// explicitly opt in again rather than silently keep auto-indexing.
Experimental *bool `json:"experimental,omitempty"`
// IndexDir is the directory where the embedding index JSONL files are stored.
// If empty, uses ~/.config/sprout/embeddings/
IndexDir string `json:"index_dir,omitempty"`
// MaxResults is the maximum number of duplicate candidates to return.
// Default: 3
MaxResults int `json:"max_results,omitempty"`
// AutoIndex controls whether the index is built automatically on first use.
// nil means "not specified at this layer" — inherit from a broader layer.
AutoIndex *bool `json:"auto_index,omitempty"`
// ExcludePaths is a list of additional paths to exclude from indexing.
ExcludePaths []string `json:"exclude_paths,omitempty"`
}
EmbeddingIndexConfig configures the embedding-based duplicate detection and semantic search. Enabled and AutoIndex are *bool so a layer can distinguish "off" from "unspecified".
func (*EmbeddingIndexConfig) IsAutoIndex ¶ added in v0.17.17
func (e *EmbeddingIndexConfig) IsAutoIndex() bool
IsAutoIndex reports whether the index builds automatically. Unspecified is off.
func (*EmbeddingIndexConfig) IsEnabled ¶ added in v0.17.17
func (e *EmbeddingIndexConfig) IsEnabled() bool
IsEnabled reports whether the embedding index is on. Requires both Enabled and Experimental — unspecified (nil) is off for either.
func (*EmbeddingIndexConfig) IsExperimental ¶ added in v0.17.17
func (e *EmbeddingIndexConfig) IsExperimental() bool
IsExperimental reports whether the experimental embedding-index opt-in is set. Unspecified is off.
func (*EmbeddingIndexConfig) SetAutoIndex ¶ added in v0.17.17
func (e *EmbeddingIndexConfig) SetAutoIndex(v bool)
func (*EmbeddingIndexConfig) SetEnabled ¶ added in v0.17.17
func (e *EmbeddingIndexConfig) SetEnabled(v bool)
SetEnabled, SetExperimental, and SetAutoIndex record an explicit value.
func (*EmbeddingIndexConfig) SetExperimental ¶ added in v0.17.17
func (e *EmbeddingIndexConfig) SetExperimental(v bool)
type KnownProviderInfo ¶ added in v0.16.25
type KnownProviderInfo struct {
// Source identifies where the metadata came from.
// "custom" = user's ~/.config/sprout/providers/<name>.json
// "factory" = embedded config in pkg/agent_providers/configs/
// or upserted via refreshFromRemote
Source string
// Name is the canonical provider name (lowercase, trimmed).
Name string
// DisplayName is the friendly label shown in UI surfaces.
DisplayName string
// EnvVar is the environment variable the provider expects for
// authentication (e.g. "OPENAI_API_KEY"). Empty when no auth
// is required.
EnvVar string
// RequiresAPIKey reports whether the provider needs an API key.
RequiresAPIKey bool
// Endpoint is the chat endpoint URL when known.
Endpoint string
// DefaultModel is the configured default model when known.
DefaultModel string
// ContextSize is the configured default context size in tokens.
ContextSize int
}
KnownProviderInfo describes a provider the runtime already knows about, either from the user's custom provider config or the embedded factory. The `sprout custom add` wizard uses this to detect when a user is "registering credentials for an existing provider" rather than "registering a brand-new OpenAI-compatible endpoint".
func LookupKnownProvider ¶ added in v0.16.25
func LookupKnownProvider(name string) (info KnownProviderInfo, ok bool)
LookupKnownProvider returns metadata for a provider the runtime knows about, checking both the user's custom provider config and the embedded factory. Returns ok=false when the name doesn't match any known provider — in which case the wizard should run the full URL/discovery flow.
The factory lookup uses GetProviderAuthMetadata, which the runtime factory populates via SetProviderConfigLookup. This is safe to call from the wizard because configuration init() ensures the package compiles even without a registered factory.
type LanguageServerOverride ¶
type LanguageServerOverride struct {
ID string `json:"id" yaml:"id"` // Unique server ID (e.g. "go", "typescript")
Binary string `json:"binary" yaml:"binary"` // Path to the binary (e.g. "gopls", "typescript-language-server")
Args []string `json:"args,omitempty" yaml:"args,omitempty"` // Command-line arguments (e.g. ["--stdio"])
LanguageIDs []string `json:"language_ids,omitempty" yaml:"language_ids,omitempty"` // Language IDs this server handles (e.g. ["go"])
InstallHint string `json:"install_hint,omitempty" yaml:"install_hint,omitempty"` // Installation instructions
}
LanguageServerOverride allows users to customize or add language server configurations beyond the built-in defaults. When a matching ID exists in the default set, this override replaces it entirely. New IDs are appended to the merged list.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager manages configuration state with safe concurrent access.
func NewManager ¶
NewManager creates a new configuration manager
func NewManagerSilent ¶
NewManagerSilent creates a new configuration manager without showing welcome messages
func NewManagerWithConfig ¶
NewManagerWithConfig creates a new configuration manager from an explicit Config and optional API key set. The manager will persist saves to the same location that config.Save()/Load() would use for the current env (when configDir is empty) or to configDir (when non-empty). Pass nil for apiKeys to skip key loading.
func NewManagerWithDir ¶
NewManagerWithDir creates a configuration Manager fully backed by configDir. If no config file exists in configDir a fresh default one is written so that subsequent Load/Save calls operate deterministically.
This is intended for tests and tooling that need a hermetic config environment without touching the caller's real ~/.config/sprout.
func NewManagerWithLayers ¶
NewManagerWithLayers creates a configuration manager using layered config. globalDir is the directory containing global config (~/.config/sprout/). workspaceDir is the directory containing workspace config ({workspace}/.sprout/). Each layer is optional - missing layers are skipped. Settings writes go to the workspace dir if provided, otherwise global.
func NewTestManager ¶
NewTestManager builds a configuration Manager backed by an isolated temp directory so that tests never read, modify, or create files in the caller's real ~/.config/sprout config. Sets SPROUT_CONFIG + SPROUT_CONFIG to that temp dir via t.Setenv so any code path that uses GetConfigPath() (rather than the Manager) lands in the temp dir too.
Returns the Manager and a cleanup func (no-op today; reserved for future hooks like Layer 5 detection). Callers should defer cleanup unconditionally to keep the contract stable as the helper grows.
Usage:
mgr, cleanup := configuration.NewTestManager(t) defer cleanup()
This helper lives in a non-_test.go file so packages outside `configuration` can import it. Inside the configuration package it is treated like any other helper.
func (*Manager) AddMCPServer ¶
func (m *Manager) AddMCPServer(name string, server mcp.MCPServerConfig) error
AddMCPServer adds an MCP server configuration
func (*Manager) EnrichCustomProviders ¶
func (m *Manager) EnrichCustomProviders()
EnrichCustomProviders loads custom provider files from the global providers directory into the config. This is needed before provider name lookups because config.json never stores custom providers directly.
func (*Manager) EnsureAPIKey ¶
func (m *Manager) EnsureAPIKey(clientType api.ClientType) error
EnsureAPIKey ensures a provider has an API key, prompting if needed
func (*Manager) GetAPIKeyForProvider ¶
func (m *Manager) GetAPIKeyForProvider(clientType api.ClientType) string
GetAPIKeyForProvider returns the API key for a provider
func (*Manager) GetAPIKeys ¶
GetAPIKeys returns the current API keys
func (*Manager) GetAvailableProviders ¶
func (m *Manager) GetAvailableProviders() []api.ClientType
GetAvailableProviders returns all providers that can be used
func (*Manager) GetConfigDir ¶
GetConfigDir returns the stored config directory for this manager. Returns empty string if the manager uses the default (env-based) location.
func (*Manager) GetMCPConfig ¶
GetMCPConfig returns the MCP configuration
func (*Manager) GetModelForProvider ¶
func (m *Manager) GetModelForProvider(clientType api.ClientType) string
GetModelForProvider returns the model for the given provider
func (*Manager) GetProvider ¶
func (m *Manager) GetProvider() (api.ClientType, error)
GetProvider returns the currently selected provider as ClientType
func (*Manager) HasAPIKey ¶
func (m *Manager) HasAPIKey(clientType api.ClientType) bool
HasAPIKey checks if a provider has an API key
func (*Manager) MapStringToClientType ¶
func (m *Manager) MapStringToClientType(s string) (api.ClientType, error)
MapStringToClientType converts string to ClientType, handling custom providers
func (*Manager) RefreshAPIKeys ¶
RefreshAPIKeys reloads API keys from the backend into the in-memory cache. This must be called after any external mutation of the credential backend (e.g., ValidateAndSaveAPIKey) to keep the Manager's in-memory map in sync.
func (*Manager) Reload ¶
Reload re-reads the on-disk configuration and API keys into the in-memory cache. It is intended to be called from a SIGHUP handler so that config changes made externally (e.g., editing config.yaml) take effect without restarting the daemon. Running agents and tools are NOT affected — only subsequent queries will see the new configuration.
func (*Manager) ResolveProviderModel ¶
func (m *Manager) ResolveProviderModel(explicitProvider, explicitModel string) (api.ClientType, string, error)
ResolveProviderModel resolves provider+model selection using canonical precedence.
func (*Manager) SaveAPIKeys
deprecated
SaveAPIKeys saves the API keys to disk.
Deprecated: This performs a blind write with no validation. Use ValidateAndSaveAPIKey instead, which validates the key before saving and preserves the old key on validation failure. This method is retained for backward compatibility only.
func (*Manager) SaveConfig ¶
SaveConfig saves the configuration to disk
func (*Manager) SelectNewProvider ¶
func (m *Manager) SelectNewProvider() (api.ClientType, error)
SelectNewProvider allows interactive provider selection
func (*Manager) SetMCPEnabled ¶
SetMCPEnabled enables or disables MCP
func (*Manager) SetModelForProvider ¶
func (m *Manager) SetModelForProvider(clientType api.ClientType, model string) error
SetModelForProvider sets the model for a provider
func (*Manager) SetProvider ¶
func (m *Manager) SetProvider(clientType api.ClientType) error
SetProvider sets the current provider
func (*Manager) UpdateConfig ¶
UpdateConfig mutates the live config under lock and persists it to disk.
type MigrationFunc ¶
MigrationFunc transforms a raw JSON config from one version to the next. It receives and returns a map[string]interface{} representing the parsed config JSON. The function should set "version" to the target version on success.
type NotificationsConfig ¶ added in v0.16.12
type NotificationsConfig struct {
// CLIBell emits a terminal bell (\a) on completion.
CLIBell bool `json:"cli_bell,omitempty"`
// OSNotify fires an OS-level desktop notification on completion.
OSNotify bool `json:"os_notify,omitempty"`
// Browser fires a browser notification (used by WebUI).
Browser bool `json:"browser,omitempty"`
// MinSeconds is the minimum turn duration before a notification is sent. Default: 10.
MinSeconds float64 `json:"min_seconds,omitempty"`
}
NotificationsConfig controls how the agent notifies the user when long-running turns complete.
func (*NotificationsConfig) Resolve ¶ added in v0.16.12
func (c *NotificationsConfig) Resolve() NotificationsConfig
Resolve returns a copy with defaults filled in for zero-value fields.
type PersistentContextConfig ¶
type PersistentContextConfig struct {
// ProactiveContextEnabled controls whether the system primes new sessions with relevant past work. Default: true.
ProactiveContextEnabled bool `json:"proactiveContextEnabled,omitempty"`
// MaxContextualResults is the maximum number of past turns to retrieve. Default: 5.
MaxContextualResults int `json:"maxContextualResults,omitempty"`
// MinRelevanceScore is the minimum time-decayed cosine similarity score. Range: 0.0–1.0. Default: 0.50.
MinRelevanceScore float64 `json:"minRelevanceScore,omitempty"`
// MaxContextChars is the hard cap on total injected character count. Default: 4000.
MaxContextChars int `json:"maxContextChars,omitempty"`
// WorkspaceScopedRetrieval restricts retrieval to the current workspace. Default: false.
WorkspaceScopedRetrieval bool `json:"workspaceScopedRetrieval,omitempty"`
// DriftDetectionEnabled controls whether conversational drift detection is active. Default: true.
DriftDetectionEnabled bool `json:"driftDetectionEnabled,omitempty"`
// DriftThreshold is the cosine similarity threshold below which drift is flagged. Default: 0.60.
DriftThreshold float64 `json:"driftThreshold,omitempty"`
// DriftCheckInterval is the number of turns between drift checks. Default: 5.
DriftCheckInterval int `json:"driftCheckInterval,omitempty"`
// RetentionDays controls how many days to keep persistent context entries. Default: 0 (never expire).
RetentionDays int `json:"retentionDays,omitempty"`
}
PersistentContextConfig configures persistent conversational context and memory retrieval.
func (*PersistentContextConfig) Resolve ¶
func (c *PersistentContextConfig) Resolve() PersistentContextConfig
Resolve fills in defaults for zero-value fields. Safe to call on nil.
type ProviderAuthMetadata ¶
type ProviderAuthMetadata struct {
Provider string
DisplayName string
RequiresAPIKey bool
EnvVar string
AuthType string
}
func GetProviderAuthMetadata ¶
func GetProviderAuthMetadata(provider string) (ProviderAuthMetadata, error)
type ProviderConfigLookupFunc ¶ added in v0.16.8
ProviderConfigLookupFunc returns the env-var and auth-type for a runtime provider config — typically backed by the global provider factory, which merges embedded, filesystem, and remote (GitHub Pages) sources. Returns ok=false when the provider is not present in the runtime view.
type ProviderDiscoveryModel ¶
type ProviderDiscoveryModel struct {
ID string `json:"id"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
ContextLength int `json:"context_length,omitempty"`
Tags []string `json:"tags,omitempty"`
}
func DiscoverCustomProviderModels ¶
func DiscoverCustomProviderModels(cfg CustomProviderConfig) ([]ProviderDiscoveryModel, error)
type ProviderDisplayNameLookupFunc ¶ added in v0.16.8
ProviderDisplayNameLookupFunc returns the user-facing label for a provider — sourced from the runtime config's display_name field. Returns ok=false when the provider is unknown to the runtime view or when its display_name is blank (so callers fall through to the static map / raw-id fallback).
type ProviderNamesLookupFunc ¶ added in v0.16.8
type ProviderNamesLookupFunc func() []string
ProviderNamesLookupFunc returns the full set of provider names known to the runtime — typically the global factory's view, which includes embedded, filesystem, and remote (GitHub Pages) registrations.
type RevisionRetentionConfig ¶
type RevisionRetentionConfig struct {
// HotCount: most recent N revisions kept verbatim. Default: 200.
HotCount int `json:"hot_count,omitempty"`
// WarmCount: next M revisions with conversation.json dropped. Default: 500.
WarmCount int `json:"warm_count,omitempty"`
// MaxDirBytes is the cap on total revisions+changes disk usage. Default: 1 GiB.
MaxDirBytes int64 `json:"max_dir_bytes,omitempty"`
// ArchiveFrozen: if true, dropped revisions are moved to _frozen/ instead of deleted. Default: false.
ArchiveFrozen bool `json:"archive_frozen,omitempty"`
// MaxChangesPerRevision caps the number of change records kept per revision. Default: 10000.
MaxChangesPerRevision int `json:"max_changes_per_revision,omitempty"`
// MaxChangesAgeDays drops change records older than this many days. Default: 30. Negative to disable.
MaxChangesAgeDays int `json:"max_changes_age_days,omitempty"`
}
RevisionRetentionConfig controls the quantity-based compaction of the persistent revision history.
func (*RevisionRetentionConfig) Resolve ¶
func (c *RevisionRetentionConfig) Resolve() RevisionRetentionConfig
Resolve fills in defaults for zero-value fields. Safe to call on nil.
type RiskLevel ¶
type RiskLevel string
RiskLevel represents the risk classification of an operation for the EA approval cascade.
const ( RiskLevelLow RiskLevel = "low" // Auto-approve (git status, read operations) RiskLevelMedium RiskLevel = "medium" // Reason and decide (git commit, git push) RiskLevelHigh RiskLevel = "high" // Prompt the user when interactive; reject when not RiskLevelCritical RiskLevel = "critical" // Never approvable: rm -rf root, fork bombs )
func MoreRestrictiveRiskLevel ¶ added in v0.16.7
MoreRestrictiveRiskLevel returns whichever of a or b gates harder. Ties return a. This is the combinator the unified resolver uses to fold multiple risk sources (classifier, persona cascade, git/fs/workspace gates) into one verdict.
func (RiskLevel) IsAtLeast ¶ added in v0.16.7
IsAtLeast reports whether r is at least as severe as other.
func (RiskLevel) Rank ¶ added in v0.16.7
Rank returns the severity ordering of a risk level:
Low(0) < Medium(1) < High(2) < Critical(3)
An empty or unrecognized level ranks as Medium — the same safe default the persona cascade already applies to unmatched operations — so an unknown value can never silently sort below a known one.
type RiskProfile ¶
type RiskProfile string
RiskProfile names a preset risk-cascade configuration. The active profile resolves to an AutoApproveRules via AutoApproveRulesForProfile. Persona-specified rules always take precedence over the profile.
const ( // RiskProfileReadonly — strictest. ONLY read operations (git // status / log / diff, read_file) are permitted. Every write, // edit, shell command, or destructive op is BLOCKED outright // (no prompt path) by promoting to the Critical tier. Use for // audits, code review, or sandboxed inspection where the agent // should never mutate anything. RiskProfileReadonly RiskProfile = "readonly" // RiskProfileCautious — most operations prompt the user. Suitable // for sensitive workspaces or unfamiliar agents. Low-risk reads // auto-approve; everything else gets routed to a prompt. RiskProfileCautious RiskProfile = "cautious" // RiskProfileDefault — sane defaults matching the historical EA // cascade. Reads auto-approve, common edits/commits auto-approve, // destructive operations (force flags, rm -rf, lossy git) prompt. RiskProfileDefault RiskProfile = "default" // RiskProfilePermissive — high trust. Almost everything passes // without prompting; only truly destructive patterns route to a // prompt. Use when the agent is well-trusted and the workspace // is recoverable (clean checkout, throwaway dir). RiskProfilePermissive RiskProfile = "permissive" // RiskProfileUnrestricted — no risk cascade gating at all. Only // the Critical tier (rm -rf root, fork bombs) blocks. Use with // extreme care; intended for sandboxed / disposable environments. RiskProfileUnrestricted RiskProfile = "unrestricted" )
type SecurityPolicy ¶
type SecurityPolicy struct {
DefaultAction string `json:"default_action,omitempty"`
Rules []SecurityRule `json:"rules,omitempty"`
AllowedPaths []string `json:"allowed_paths,omitempty"`
DeniedPaths []string `json:"denied_paths,omitempty"`
DeniedCommands []string `json:"denied_commands,omitempty"`
MaxRiskLevel string `json:"max_risk_level,omitempty"`
}
SecurityPolicy defines workspace-level security rules
func DefaultSecurityPolicy ¶
func DefaultSecurityPolicy() *SecurityPolicy
DefaultSecurityPolicy returns a conservative default policy
func LoadSecurityPolicy ¶
func LoadSecurityPolicy(workspaceRoot string) (*SecurityPolicy, error)
LoadSecurityPolicy reads .sprout/security-policy.json from workspaceRoot. If the file doesn't exist, returns nil, nil (no error). Validates that actions and risk levels are recognized values, and normalizes AllowedPaths and DeniedPaths via filepath.Clean.
func (*SecurityPolicy) Evaluate ¶
func (p *SecurityPolicy) Evaluate(command string) SecurityPolicyAction
Evaluate checks the command against rules in order. For each rule, it first tries matching the full command string, then the base command (first word). First matching rule wins. This means a rule like {Pattern: "git", Action: "deny"} will match "git commit" via base matching before a later rule {Pattern: "git commit*", Action: "allow"} gets evaluated.
Place more specific patterns before broader ones to get the intended behavior. If no rule matches, returns DefaultAction converted to SecurityPolicyAction. If DefaultAction is empty, defaults to "prompt".
func (*SecurityPolicy) IsCommandDenied ¶
func (p *SecurityPolicy) IsCommandDenied(command string) bool
IsCommandDenied checks if the base command (first word) matches any entry in DeniedCommands (case-insensitive).
func (*SecurityPolicy) IsPathAllowed ¶
func (p *SecurityPolicy) IsPathAllowed(path string) bool
IsPathAllowed checks whether the given path is allowed by this policy. If AllowedPaths is empty, returns true (no restrictions). Otherwise checks if the cleaned path starts with any of the allowed paths. Also ensures the path is not in DeniedPaths.
NOTE: This uses filepath.Clean for normalization but does NOT resolve symlinks. A symlink from an allowed path to a system directory could bypass this check.
func (*SecurityPolicy) IsPathDenied ¶
func (p *SecurityPolicy) IsPathDenied(path string) bool
IsPathDenied checks if the cleaned path starts with any of the denied paths. Handles parent traversal: if "/etc" is denied, "/etc/passwd" is also denied.
NOTE: This uses filepath.Clean for normalization but does NOT resolve symlinks. A symlink from an allowed path to a system directory could bypass this check.
func (*SecurityPolicy) MaxAllowedRisk ¶
func (p *SecurityPolicy) MaxAllowedRisk() int
MaxAllowedRisk converts MaxRiskLevel string to SecurityRisk int equivalent. "safe"=0, "caution"=1, "dangerous"=2. Defaults to 0 if empty or unrecognized.
type SecurityPolicyAction ¶
type SecurityPolicyAction string
SecurityPolicyAction represents the action to take when a rule matches
const ( PolicyAllow SecurityPolicyAction = "allow" PolicyDeny SecurityPolicyAction = "deny" PolicyPrompt SecurityPolicyAction = "prompt" )
func (SecurityPolicyAction) IsValid ¶
func (a SecurityPolicyAction) IsValid() bool
IsValid returns true if the action is a recognized SecurityPolicyAction value: "allow", "deny", "prompt", or empty string.
type SecurityRule ¶
type SecurityRule struct {
Pattern string `json:"pattern"`
Action string `json:"action"`
Reason string `json:"reason,omitempty"`
}
SecurityRule defines a pattern-based rule for command evaluation
type ShellConfig ¶ added in v0.16.12
type ShellConfig struct {
UserSafePatterns []ShellPattern `json:"user_safe_patterns,omitempty"`
UserDangerousPatterns []ShellPattern `json:"user_dangerous_patterns,omitempty"`
WorkspaceOverlay WorkspaceOverlayConfig `json:"workspace_overlay,omitempty"`
}
ShellConfig holds user-configurable shell permission policy.
func (*ShellConfig) Validate ¶ added in v0.16.12
func (sc *ShellConfig) Validate() error
Validate checks the ShellConfig values and normalizes invalid ones. Returns an error for values that can't be normalized.
type ShellPattern ¶ added in v0.16.12
type ShellPattern struct {
Match string `json:"match"` // prefix string or regex pattern
Kind string `json:"kind"` // "prefix" or "regex"
Reason string `json:"reason,omitempty"` // optional human-readable note
}
ShellPattern is a single user-defined shell classification pattern.
type Skill ¶
type Skill struct {
ID string `json:"id"` // Unique identifier (e.g., "go-best-practices")
Name string `json:"name"` // Human-readable name
Description string `json:"description"` // What this skill provides and when to use it
Path string `json:"path"` // Relative path to skill directory
Enabled bool `json:"enabled"` // Whether this skill is available
Metadata map[string]string `json:"metadata"` // Optional metadata (author, version, etc.)
AllowedTools string `json:"allowed_tools"` // Optional space-delimited list of pre-approved tools
}
Skill defines an Agent Skill that can be loaded into context
type SubagentType ¶
type SubagentType struct {
ID string `json:"id"` // Unique identifier (e.g., "coder", "tester", "debugger")
Name string `json:"name"` // Human-readable name (e.g., "Coder", "Tester")
Description string `json:"description"` // What this subagent specializes in
Provider string `json:"provider"` // Provider for this subagent type (optional, falls back to SubagentProvider)
Model string `json:"model"` // Model for this subagent type (optional, falls back to SubagentModel)
SystemPrompt string `json:"system_prompt"` // Relative path to system prompt file (e.g., "subagent_prompts/coder.md")
SystemPromptText string `json:"system_prompt_text,omitempty"` // Optional inline system prompt text (replaces base prompt entirely)
SystemPromptAppend string `json:"system_prompt_append,omitempty"` // Optional inline text appended to the base or loaded system prompt (for composition)
AllowedTools []string `json:"allowed_tools,omitempty"` // Optional explicit tool allowlist for focused persona behavior
Aliases []string `json:"aliases,omitempty"` // Optional aliases (e.g., "web-scraper")
Enabled bool `json:"enabled"` // Catalog-only: every shipped persona sets this true. Runtime "is this persona usable?" is determined by Config.DisabledPersonas (user) + LocalOnly (env). Kept for catalog hygiene + defense-in-depth in case a future variant ships with a deliberately-disabled entry.
LocalOnly bool `json:"local_only,omitempty"` // Only available in local mode (not cloud)
Delegatable bool `json:"delegatable,omitempty"` // Whether this persona can be used as a subagent (default: true for worker personas, false for orchestrator personas)
AutoApproveRules *AutoApproveRules `json:"auto_approve_rules,omitempty"` // Risk cascade rules for the runtime auto-approve check
// Capabilities is an explicit list of agency grants this persona holds
// (e.g. "git_write"). Replaces sniffing AutoApproveRules to infer what a
// persona is allowed to do. Use HasCapability to query.
Capabilities []string `json:"capabilities,omitempty"`
// CanSpawnNonDelegatable lists otherwise-undelegatable persona IDs that
// this persona may spawn. Replaces the hardcoded EA-spawn-authority
// special-case. The coordinator carries ["orchestrator"] to enable the
// canonical coordinator→orchestrator→specialist chain.
CanSpawnNonDelegatable []string `json:"can_spawn_non_delegatable,omitempty"`
}
SubagentType defines a specialized subagent persona with its own configuration
func (*SubagentType) EvaluateOperationRisk ¶
func (st *SubagentType) EvaluateOperationRisk(command string) RiskLevel
EvaluateOperationRisk determines the risk level of a shell operation based on the persona's auto-approve rules. Returns RiskLevelCritical for absolute-block patterns, otherwise RiskLevelLow, RiskLevelMedium, or RiskLevelHigh per the rules.
func (*SubagentType) GetAutoApproveRules ¶
func (st *SubagentType) GetAutoApproveRules() AutoApproveRules
GetAutoApproveRules returns the auto-approve rules for this persona, falling back to defaults if none are configured. Callers MUST NOT modify the returned struct's slice fields, as they may share backing arrays with the original config.
func (*SubagentType) HasCapability ¶ added in v0.16.4
func (st *SubagentType) HasCapability(name string) bool
HasCapability reports whether the persona declares the given capability name. Comparison is case-insensitive and whitespace-tolerant.
type TrainingConfig ¶ added in v0.17.5
type TrainingConfig struct {
// Endpoint is the URL to push training data to (e.g. http://localhost:8190).
// Sessions are POSTed to {Endpoint}/sessions as JSON.
Endpoint string `json:"endpoint,omitempty"`
// Enabled controls whether training data is collected and pushed.
// ALWAYS false by default — must be explicitly enabled.
Enabled bool `json:"enabled,omitempty"`
// ExcludePaths is a list of working directory prefixes to exclude from
// training data. Sessions whose working directory starts with any of
// these paths are silently skipped.
ExcludePaths []string `json:"exclude_paths,omitempty"`
}
TrainingConfig controls opt-in session recording for training data collection. When enabled, PII-redacted conversation states are pushed to the configured endpoint after each session save.
type VisionConfig ¶ added in v0.16.19
type VisionConfig struct {
// ParallelWorkers caps concurrent in-flight vision requests per session. Default: 3. Range: 1..32.
ParallelWorkers int `json:"parallel_workers,omitempty"`
// MaxParallelRequests caps the global number of in-flight vision
// API calls across the entire process. This is independent of
// ParallelWorkers (which is per-session). Default: 8.
MaxParallelRequests int `json:"max_parallel_requests,omitempty"`
// EnableBatchProcessing toggles the multi-image batching layer. Default: true.
EnableBatchProcessing bool `json:"enable_batch_processing,omitempty"`
// MaxBatchSize is the maximum number of images sent in a single batched call. Default: 4. Range: 1..8.
MaxBatchSize int `json:"max_batch_size,omitempty"`
}
VisionConfig controls vision-pipeline runtime: parallel workers, concurrency cap, and batching.
func GetVisionConfig ¶ added in v0.16.19
func GetVisionConfig() VisionConfig
GetVisionConfig returns the raw VisionConfig from the on-disk config file.
func (*VisionConfig) Resolve ¶ added in v0.16.19
func (c *VisionConfig) Resolve() VisionConfig
Resolve returns a copy with defaults filled in for zero-value fields.
type WakeupConfig ¶ added in v0.16.19
type WakeupConfig struct {
Enabled bool `json:"enabled"` // Master switch; default true
MaxTokensPerSession int `json:"max_tokens_per_session"` // Hard cap on auto-resume token spend; default 5000
MaxResumesPerSession int `json:"max_resumes_per_session"` // Max auto-resumes before requiring user input; default 10
}
WakeupConfig controls auto-resume behavior for background task completions.
func DefaultWakeupConfig ¶ added in v0.16.19
func DefaultWakeupConfig() WakeupConfig
DefaultWakeupConfig returns conservative defaults.
type WorkspaceOverlayConfig ¶ added in v0.16.12
type WorkspaceOverlayConfig struct {
// Mode: "tighten_only" (default — only user_dangerous_patterns honored),
// "trusted" (full overlay honored after `sprout policy trust`), or
// "ignore" (workspace policy never loaded).
Mode string `json:"mode,omitempty"`
}
WorkspaceOverlayConfig controls how workspace-rooted policy files are loaded.
Source Files
¶
- api_keys.go
- config.go
- config_api_timeout.go
- config_command_categorize.go
- config_command_policy.go
- config_commit_review.go
- config_domain.go
- config_explicit_keys.go
- config_git_classify.go
- config_heredoc_strip.go
- config_load_save.go
- config_merge.go
- config_migration.go
- config_paths.go
- config_provider_custom.go
- config_risk_profile.go
- config_risk_subagent.go
- config_shell.go
- config_skills.go
- config_subagent.go
- config_subagent_type.go
- config_system_prompt.go
- context_profile.go
- custom_provider_migration.go
- custom_provider_normalize.go
- custom_provider_registry.go
- env.go
- errors.go
- init.go
- isolated_config.go
- manager.go
- manager_load.go
- manager_mcp.go
- manager_provider.go
- manager_save.go
- migration_sp133.go
- provider_auth.go
- provider_ordering.go
- provider_resolution.go
- redact.go
- resolve.go
- risk_level.go
- security_policy.go
- skills_allowlist.go
- status_prefix.go
- testing_isolation.go