Documentation
¶
Overview ¶
Package config loads fairpeer's runtime configuration from TOML. Resolution order: flag > project ./fairpeer.toml > user ~/.config/fairpeer/config.toml > built-in defaults. Secrets come from the environment via api_key_env and are never stored in config files.
fetch.go — model auto-discovery via the OpenAI-compatible GET /models API.
Index ¶
- Constants
- Variables
- func ArchiveDir() string
- func BuildModelFetchURLs(baseURL, override string) ([]string, error)
- func CacheDir() string
- func CanonicalDesktopOfficialProviderName(name string) string
- func CanonicalSkillPath(path string) string
- func CommandDirs() []string
- func CommandDirsForRoot(root string) []string
- func CoworkPromptAddon(disabledSkills []string) string
- func EffectiveEffort(e *ProviderEntry) string
- func EffortDisplay(e *ProviderEntry) string
- func ExpandVars(s string) string
- func ImportCCSwitchMCP() (total, added, updated int, err error)
- func ImportCCSwitchMCPEntries(entries []PluginEntry) (total, added, updated int, err error)
- func IsLikelyChatModel(model string) bool
- func IsValidSkillName(name string) bool
- func MemoryUserDir() string
- func ModelRefsProvider(ref, name string) bool
- func NormalizeEffort(e *ProviderEntry, raw string) (string, error)
- func NormalizeLegacyDesktopProviderAccess(c *Config)
- func PluginAllowedByProfile(p *Profile, pluginName string) bool
- func ProfileNameKey(name string) string
- func ProjectSessionDir(workspaceRoot string) string
- func ProjectSessionDirFor(workspaceRoot, profile string) string
- func ReasoningProtocolForEntry(e *ProviderEntry) string
- func RenderTOML(c *Config) string
- func RenderTOMLForScope(c *Config, scope RenderScope) string
- func SaveMinimalProjectAutoPlan(path, mode string) (string, error)
- func SessionDir() string
- func SessionDirFor(profile string) string
- func SkillNameKey(name string) string
- func SourcePath() string
- func SourcePathForRoot(root string) string
- func UserConfigPath() string
- func UserCredentialsPath() string
- func WorkspaceSlug(absPath string) string
- type AgentConfig
- type BotAllowlist
- type BotConfig
- type BotConnectionConfig
- type BotConnectionCredential
- type BotConnectionSessionMapping
- type BuiltInMCPConfig
- type CodegraphConfig
- type Config
- func (c *Config) AddPermissionRule(list, rule string) error
- func (c *Config) AddSkillPath(path string) error
- func (c *Config) AutoStartPlugins() []PluginEntry
- func (c *Config) BashMode() string
- func (c *Config) BashTimeoutSeconds() int
- func (c *Config) ClearPluginAuthentication(name string) (PluginEntry, bool, error)
- func (c *Config) DesktopCheckUpdates() bool
- func (c *Config) DesktopCloseBehavior() string
- func (c *Config) DesktopDisplayMode() string
- func (c *Config) DesktopLanguage() string
- func (c *Config) DesktopMetrics() bool
- func (c *Config) DesktopTelemetry() bool
- func (c *Config) DesktopTheme() string
- func (c *Config) DesktopThemeStyle() string
- func (c *Config) DisabledSkillNames() []string
- func (c *Config) ExcludeSkillPath(path string) error
- func (c *Config) IsProfileKnown(name string) bool
- func (c *Config) IsSkillDisabled(name string) bool
- func (c *Config) NetworkProxyMode() string
- func (c *Config) NetworkProxySpec() netclient.ProxySpec
- func (c *Config) Provider(name string) (*ProviderEntry, bool)
- func (c *Config) ReadRoots() []string
- func (c *Config) RemovePermissionRule(list, rule string) (bool, error)
- func (c *Config) RemovePlugin(name string) bool
- func (c *Config) RemoveProvider(name string) error
- func (c *Config) RemoveSkillPath(path string) (bool, error)
- func (c *Config) ResolveModel(ref string) (*ProviderEntry, bool)
- func (c *Config) ResolveModelWithFallback(ref string) (resolvedRef string, fallback bool, ok bool)
- func (c *Config) ResolveProfile(name string) (*Profile, error)
- func (c *Config) ResolveSystemPrompt() (string, error)
- func (c *Config) RestoreSkillPath(path string) error
- func (c *Config) Save() error
- func (c *Config) SaveForRoot(root string) error
- func (c *Config) SaveTo(path string) error
- func (c *Config) SaveToScope(path string, scope RenderScope) error
- func (c *Config) SetAutoPlan(mode string) error
- func (c *Config) SetDefaultModel(name string) error
- func (c *Config) SetDesktopAppearance(theme, style string) error
- func (c *Config) SetDesktopCheckUpdates(enabled bool) error
- func (c *Config) SetDesktopCloseBehavior(mode string) error
- func (c *Config) SetDesktopDisplayMode(mode string) error
- func (c *Config) SetDesktopLanguage(lang string) error
- func (c *Config) SetDesktopMetrics(enabled bool) error
- func (c *Config) SetDesktopTelemetry(enabled bool) error
- func (c *Config) SetDreamEnabled(enabled bool)
- func (c *Config) SetDreamIntervals(dreamDays, distillDays int) error
- func (c *Config) SetExpandThinking(on bool) error
- func (c *Config) SetLanguage(lang string) error
- func (c *Config) SetNetwork(n NetworkConfig) error
- func (c *Config) SetPermissionMode(mode string) error
- func (c *Config) SetPlannerModel(name string) error
- func (c *Config) SetProviderEffort(name, effort string) error
- func (c *Config) SetProviderThinking(name, thinking string) error
- func (c *Config) SetShowReasoning(on bool) error
- func (c *Config) SetSkillEnabled(name string, enabled bool) error
- func (c *Config) SetUICloseBehavior(mode string) error
- func (c *Config) SetUIShortcutLayout(layout string) error
- func (c *Config) SkillCustomPaths() []string
- func (c *Config) SkillExcludedPaths() []string
- func (c *Config) SkillMaxDepth() int
- func (c *Config) UICloseBehavior() string
- func (c *Config) UIShortcutLayout() string
- func (c *Config) UITheme() string
- func (c *Config) UIThemeStyle() string
- func (c *Config) UpsertPlugin(e PluginEntry) error
- func (c *Config) UpsertProvider(e ProviderEntry) error
- func (c *Config) Validate(model string) error
- func (c *Config) WriteFile(path string) error
- func (c *Config) WriteRoots() []string
- func (c *Config) WriteRootsForRoot(fallbackRoot string) []string
- type CoworkConfig
- type DesktopConfig
- type DreamConfig
- type EffortCapability
- type EmailAccount
- type FeishuBotConfig
- type IMAPConfig
- type LLMConfig
- type LSPConfig
- type LSPServer
- type MCPImportCandidate
- type MigrationResult
- type NetworkConfig
- type NetworkProxyConfig
- type NotificationsConfig
- type PermissionsConfig
- type PluginEntry
- type Profile
- type ProviderEntry
- func (e *ProviderEntry) APIKey() string
- func (e *ProviderEntry) ChatModelList() []string
- func (e *ProviderEntry) Configured() bool
- func (e *ProviderEntry) DefaultModel() string
- func (e *ProviderEntry) FetchModels(ctx context.Context) ([]string, error)
- func (e *ProviderEntry) HasModel(m string) bool
- func (e *ProviderEntry) ModelList() []string
- type QQBotConfig
- type RenderScope
- type SMTPConfig
- type SandboxConfig
- type SearchConfig
- type SkillsConfig
- type StatuslineConfig
- type ToolsConfig
- type UIConfig
- type WeixinBotConfig
Constants ¶
const ( ReasoningProtocolAuto = "auto" ReasoningProtocolOpenAI = "openai" ReasoningProtocolNone = "none" )
const ( EffortAuto = "auto" EffortLow = "low" EffortMedium = "medium" EffortHigh = "high" )
Canonical effort levels. All providers use this unified vocabulary. Provider-specific translation happens at the wire layer (openai.go, anthropic.go).
const ( // ProfileDev is the built-in coding mode. It mirrors the unprofiled behaviour // exactly (empty overrides), so a config with no [[profiles]] is effectively // always in dev. Resolving "dev" therefore always succeeds and never mutates // the Controller's tool/skill/plugin set beyond what config already declares. ProfileDev = "dev" // ProfileCowork is the office mode. For Phase 0 it is intentionally a thin // shell: a prompt addon that biases the model toward office tasks, but the // SAME tool/skill/plugin set as dev. Real coWork capabilities (browser, // desktop automation) arrive in later phases and turn on here. ProfileCowork = "cowork" )
const DefaultDistillInterval = 30
DefaultDistillInterval is the Distill run cadence when [dream].distill_interval is unset.
const DefaultDreamInterval = 7
DefaultDreamInterval is the Dream run cadence when [dream].dream_interval is unset.
const DefaultFastTaskModel = ""
DefaultFastTaskModel is the model dream/distill/rag-extract run on. Empty means unconfigured — at runtime an empty agent.fast_task_model falls back to the default model; this constant no longer hardcodes a vendor model. Phase 3 will resolve it from the configured provider's fast_model role.
const DefaultIdleMinutes = 10
DefaultIdleMinutes is how long the user must be inactive before an idle Dream run may fire, when [dream].idle_minutes is unset. Dream is meant to run in the user's downtime, not while they are actively working — this bounds resource contention and matches the "consolidate when idle" intent.
const DefaultSkillColdDays = 90
DefaultSkillColdDays is the inactivity threshold for skill retirement when [dream].skill_cold_days is unset: 90 days mirrors memory's ColdDays default.
const DefaultSystemPrompt = `` /* 1464-byte string literal not displayed */
DefaultSystemPrompt is used when config provides none.
const LanguagePolicy = `Reply in the same language the user is using in their most recent message: ` +
`if they write in Chinese answer in Chinese, in English answer in English, and switch ` +
`whenever they switch. Let this also guide the language you think in. Always keep code, ` +
`identifiers, file paths, shell commands, and technical terms in their original form — never translate them.`
LanguagePolicy is the auto fallback appended to the system prompt when no concrete UI language is resolved. It is static English text, so it stays part of the cache-stable prefix and avoids per-turn language injection.
Variables ¶
var ConventionDirs = []string{".fairpeer", ".agents", ".agent", ".claude"}
ConventionDirs are the parent directories scanned for agent assets (skills, commands), in canonical-first order. .fairpeer is ours; .agents / .agent / .claude let users drop in assets authored for other agent tools without moving files. Shared so skills (internal/skill) and commands (CommandDirs) discover the same set. Note: hooks are NOT scanned across these — a .claude/settings.json uses a different hook schema that can't be parsed as ours, so hooks stay in .fairpeer/settings.json (see internal/hook).
Functions ¶
func ArchiveDir ¶
func ArchiveDir() string
ArchiveDir is where compacted conversation history is archived for traceability (one timestamped .jsonl per compaction). Empty if the user config directory cannot be resolved, in which case archiving is skipped.
func BuildModelFetchURLs ¶
BuildModelFetchURLs derives likely OpenAI-compatible model-list endpoints. It keeps fairpeer's historical {base}/models path first, then tries the common {base}/v1/models shape used by many aggregators.
func CacheDir ¶
func CacheDir() string
CacheDir is the per-user cache root for derived/regenerable artefacts: MCP handshake snapshots, plugin startup-latency telemetry. Lives beside the existing dirs (UserConfigDir/fairpeer/...) so the whole fairpeer state tree shares one root the user can wipe in a single rm. Empty when the OS dir is unavailable — callers must tolerate that (caching is best-effort).
func CanonicalDesktopOfficialProviderName ¶
CanonicalDesktopOfficialProviderName returns the Settings Center provider ID for built-in official provider aliases.
func CanonicalSkillPath ¶
CanonicalSkillPath expands env vars, ~ and relative segments to an absolute cleaned path for comparing skill roots. On Windows it folds case so paths that differ only in casing dedupe. Use only for comparison, never as stored config.
func CommandDirs ¶
func CommandDirs() []string
CommandDirs returns the directories scanned for custom slash commands, lowest priority first, so a later (more specific) directory overrides an earlier one on a name clash. Order: home-dir convention dirs (~/.claude/commands … ~/.fairpeer/commands), the legacy XDG user dir (~/.config/fairpeer/commands), then the project's convention dirs (.claude/commands … .fairpeer/commands). Scanning the .claude / .agents / .agent dirs lets commands authored for other agent tools (same .md + frontmatter format) work here unchanged.
func CommandDirsForRoot ¶
CommandDirsForRoot is like CommandDirs but resolves the project convention dirs under root instead of the current working directory. Global (home/XDG) dirs are unchanged — they are always user-scoped.
func CoworkPromptAddon ¶
CoworkPromptAddon returns the cowork system-prompt add-on with capability routing rows for disabled skills REMOVED. Passing nil/empty yields the full add-on verbatim (no rows dropped) — the historical static behaviour.
The cowork prompt hard-codes a "for task X, call run_skill("Y")" routing table. Without this filter, disabling a skill (e.g. ppt-auto) still leaves the prompt instructing the model to call it, so the model repeatedly tries a disabled skill instead of telling the user to re-enable it. Dropping the row removes that instruction entirely.
disabledSkills is the effective disabled-name set (config + profile + whitelist-excluded, already name-keyed upstream). Names are compared via SkillNameKey so casing/platform differences don't let a row survive.
func EffectiveEffort ¶
func EffectiveEffort(e *ProviderEntry) string
EffectiveEffort resolves the provider-visible effort value. Explicit ProviderEntry.Effort wins; otherwise a configured SupportedEfforts list makes DefaultEffort (or the first supported level) the runtime default. Empty means provider default / omit the provider-specific effort field.
func EffortDisplay ¶
func EffortDisplay(e *ProviderEntry) string
EffortDisplay returns the selected /effort level, using "auto" for provider default.
func ExpandVars ¶
ExpandVars substitutes ${VAR} / ${VAR:-default} references from the process environment. An unset variable with no default expands to "" (matching the MCP / Claude Code convention), so a missing secret yields an empty header rather than a literal "${TOKEN}" leaking onto the wire.
func ImportCCSwitchMCP ¶
ImportCCSwitchMCP upserts cc-switch's Codex-enabled MCP servers into the active fairpeer config and saves it.
func ImportCCSwitchMCPEntries ¶
func ImportCCSwitchMCPEntries(entries []PluginEntry) (total, added, updated int, err error)
func IsLikelyChatModel ¶
IsLikelyChatModel reports whether a model ID looks like a chat/completion model rather than a specialised audio/vision/embedding model. It applies a conservative name-based heuristic — the OpenAI-compatible /models API does not return capability/modality metadata, so this is the most reliable fallback until providers add such fields.
The heuristic works in two passes:
- Multi-word substring check for compound terms that span separators (e.g. "text-embedding", "text-to-speech").
- Token-level check: the model ID is split on common separators (- _ . / :) and each token is compared against a set of known non-chat keywords.
"voice" is intentionally absent from the non-chat set because it is too broad — legitimate future chat models may include it in their name.
func IsValidSkillName ¶
IsValidSkillName reports whether name is a usable skill identifier.
func MemoryUserDir ¶
func MemoryUserDir() string
MemoryUserDir returns the fairpeer user config root (…/fairpeer), under which the user-global fairpeer.md and the per-project auto-memory store live. Empty when the user config dir can't be resolved, which disables user-scoped memory.
func ModelRefsProvider ¶
ModelRefsProvider reports whether ref targets the named provider. It matches both bare provider names ("openai") and "provider/model" refs.
func NormalizeEffort ¶
func NormalizeEffort(e *ProviderEntry, raw string) (string, error)
NormalizeEffort maps a user-supplied /effort level into the value stored in config. Empty means auto/provider default. All providers use the unified low/medium/high vocabulary. Legacy values are migrated:
- max/xhigh → high
- adaptive → high
- disabled/off → low
func NormalizeLegacyDesktopProviderAccess ¶
func NormalizeLegacyDesktopProviderAccess(c *Config)
NormalizeLegacyDesktopProviderAccess seeds the desktop provider-access list for configs written before Settings tracked explicit provider access. Callers should only use this when they know the TOML did not declare provider_access; an explicit empty list means the user removed all access entries.
func PluginAllowedByProfile ¶
PluginAllowedByProfile reports whether pluginName is visible under profile p. An empty p.Plugins list means "all plugins allowed" (the dev default), so a profile that does not opt into plugin filtering keeps the full MCP set. When p is nil (no profile), all plugins are allowed.
func ProfileNameKey ¶
ProfileNameKey normalizes a profile identifier for comparisons. Profile names are case- and whitespace-insensitive so "Cowork" / "COWORK" / "cowork" all resolve the same. Empty stays empty (resolved to ProfileDev upstream).
func ProjectSessionDir ¶
ProjectSessionDir is the per-workspace session directory the desktop sidebar lists: <config root>/projects/<slug>/sessions. Empty when either the config root or workspaceRoot doesn't resolve.
func ProjectSessionDirFor ¶
ProjectSessionDirFor returns the per-workspace session directory partitioned by profile: <config root>/projects/<slug>/<profileKey>/sessions. The default profile (empty/"dev") is backward compatible with ProjectSessionDir — it returns the un-profiled path. Empty when the workspace root doesn't resolve. Used by desktopSessionDirFor so each tab's session lands in its own profile partition and dev/cowork conversations don't mix.
func ReasoningProtocolForEntry ¶
func ReasoningProtocolForEntry(e *ProviderEntry) string
ReasoningProtocolForEntry resolves the provider request shape for reasoning controls. Explicit per-provider config wins. With no URL-based fallback, an empty result means the provider uses standard OpenAI-compatible request shape unless it declares reasoning_protocol = "openai" explicitly.
func RenderTOML ¶
RenderTOML renders the config as annotated TOML in the `fairpeer setup` house style: comments preserved, system_prompt as a multi-line string, helpful hints. The output round-trips back through Load (see render_test.go).
func RenderTOMLForScope ¶
func RenderTOMLForScope(c *Config, scope RenderScope) string
RenderTOMLForScope renders an annotated TOML file for a specific persistence target. User configs can carry desktop and account-level preferences; project fairpeer.toml stays focused on project behavior and intentionally excludes desktop-only preferences.
func SaveMinimalProjectAutoPlan ¶
SaveMinimalProjectAutoPlan writes a new project config that only overrides [agent].auto_plan. It is intentionally minimal so toggling a project-local auto-plan preference in an otherwise unconfigured workspace does not pin default_model or providers from built-in defaults.
func SessionDir ¶
func SessionDir() string
SessionDir is where chat sessions are persisted (one .jsonl per session). Used by `fairpeer chat --continue` / `--resume` to find the recent ones. Empty if the user config dir can't be resolved — sessions then aren't saved.
func SessionDirFor ¶
SessionDirFor returns the session directory for a given profile. The default profile (empty name, or the builtin "dev"/"default") shares the top-level <userDir>/sessions so existing --continue/--resume history stays intact; a named profile (e.g. "cowork") partitions under <userDir>/sessions/<key> so its transcripts don't mix with the default's. Returns "" when the user dir can't be resolved. boot.go uses this so --profile cowork lands in its own partition.
func SkillNameKey ¶
SkillNameKey normalizes a skill identifier for config comparisons.
func SourcePath ¶
func SourcePath() string
SourcePath returns the highest-priority config file that exists, or "" if none.
func SourcePathForRoot ¶
SourcePathForRoot returns the highest-priority config file that exists under root, or "" if none. Equivalent to SourcePath() when root is ".".
func UserConfigPath ¶
func UserConfigPath() string
UserConfigPath is the user-global config file (~/.config/fairpeer/config.toml), or "" when the user config dir can't be resolved.
func UserCredentialsPath ¶
func UserCredentialsPath() string
UserCredentialsPath is the fairpeer-owned global secrets file, beside config.toml in the user config dir (e.g. ~/.config/fairpeer/credentials). It holds KEY=value lines loaded into the environment by loadDotEnv. The setup wizard writes API keys here, deliberately NOT named .env: keys never land in a project's own .env (which can't be selectively gitignored), never get committed, and resolve from any working directory. "" when the user config dir can't be resolved.
func WorkspaceSlug ¶
WorkspaceSlug flattens an absolute workspace path into the directory name used under <config root>/projects.
Types ¶
type AgentConfig ¶
type AgentConfig struct {
SystemPrompt string `toml:"system_prompt"`
SystemPromptFile string `toml:"system_prompt_file"`
MaxSteps int `toml:"max_steps"` // tool-call rounds per turn; 0 = unlimited
PlannerMaxSteps int `toml:"planner_max_steps"` // planner read-only tool-call rounds; 0 = unlimited
Temperature float64 `toml:"temperature"`
PlannerModel string `toml:"planner_model"`
SubagentModel string `toml:"subagent_model"`
SubagentModels map[string]string `toml:"subagent_models"`
SubagentEffort string `toml:"subagent_effort"`
SubagentEfforts map[string]string `toml:"subagent_efforts"`
FastTaskModel string `toml:"fast_task_model"` // lightweight model for dream/distill background tasks
// OutputStyle selects a persona/tone block folded into the system prompt at
// startup (a built-in like "explanatory"/"learning"/"concise", or a custom
// .fairpeer/output-styles/<name>.md). Empty = the unmodified prompt.
OutputStyle string `toml:"output_style"`
// AutoPlan controls whether interactive turns that look multi-step start in
// plan mode automatically: "off" keeps plan mode manual, "on" enables the
// approval gate. Legacy "ask" is treated as "on".
AutoPlan string `toml:"auto_plan"`
// AutoPlanClassifier optionally names a provider/model used to classify
// borderline auto-plan decisions. Empty keeps the zero-cost heuristic path.
AutoPlanClassifier string `toml:"auto_plan_classifier"`
// Compaction window fractions: soft = notice only, compact = trigger, force = hard ceiling.
SoftCompactRatio float64 `toml:"soft_compact_ratio"`
CompactRatio float64 `toml:"compact_ratio"`
CompactForceRatio float64 `toml:"compact_force_ratio"`
// ContextBudgetPercent caps the effective window for compaction decisions
// (SPEC v2 §3.6). 0/100 = full window (default, zero config). E.g. 80 =
// compact as if the window were 80% of its real size — saves cost on input
// pricing tiers and avoids quality drop near the window edge.
ContextBudgetPercent int `toml:"context_budget_percent"`
}
AgentConfig configures the harness loop. PlannerModel is optional: when set to another provider's name it enables two-model collaboration, where the planner handles low-frequency planning in its own session (kept separate so each model's prompt prefix stays cache-stable; some providers do not report cache tokens). SubagentModel is the optional default for runAs=subagent skills; SubagentModels overrides it per skill name.
type BotAllowlist ¶
type BotAllowlist struct {
Enabled bool `toml:"enabled"`
AllowAll bool `toml:"allow_all"`
Mode string `toml:"mode"` // "open"(默认,自动加入)| "review"(需管理员审批)
QQUsers []string `toml:"qq_users"`
FeishuUsers []string `toml:"feishu_users"`
WeixinUsers []string `toml:"weixin_users"`
QQGroups []string `toml:"qq_groups"`
FeishuGroups []string `toml:"feishu_groups"`
WeixinGroups []string `toml:"weixin_groups"`
}
BotAllowlist 控制哪些用户可以使用 bot。
type BotConfig ¶
type BotConfig struct {
Enabled bool `toml:"enabled"`
Model string `toml:"model"` // 用于 bot 的模型名,空则用 default_model
MaxSteps int `toml:"max_steps"`
DebounceMs int `toml:"debounce_ms"` // 消息合并窗口,毫秒
Allowlist BotAllowlist `toml:"allowlist"`
QQ QQBotConfig `toml:"qq"`
Feishu FeishuBotConfig `toml:"feishu"`
Weixin WeixinBotConfig `toml:"weixin"`
Connections []BotConnectionConfig `toml:"connections"`
}
BotConfig 控制多渠道 IM bot 消息网关。
type BotConnectionConfig ¶
type BotConnectionConfig struct {
ID string `toml:"id"`
Provider string `toml:"provider"` // qq|feishu|weixin
Domain string `toml:"domain"` // feishu|lark|weixin|qq
Label string `toml:"label"`
Enabled bool `toml:"enabled"`
Status string `toml:"status"` // disconnected|pending|connected|error
Model string `toml:"model"`
WorkspaceRoot string `toml:"workspace_root"`
Credential BotConnectionCredential `toml:"credential"`
SessionMappings []BotConnectionSessionMapping `toml:"session_mappings"`
LastError string `toml:"last_error"`
CreatedAt string `toml:"created_at"`
UpdatedAt string `toml:"updated_at"`
}
BotConnectionConfig is the desktop-friendly connection record for IM bot channels. It keeps install/runtime state separate from legacy per-provider knobs so the UI can expose a simple "connect first" flow while old configs keep working.
type BotConnectionCredential ¶
type BuiltInMCPConfig ¶
type BuiltInMCPConfig struct {
Context7Enabled bool `toml:"context7_enabled"`
}
BuiltInMCPConfig controls which built-in MCP servers are enabled. Each server has a corresponding *_enabled boolean. Default is off for servers that require external dependencies (e.g. npx for Context7).
func (BuiltInMCPConfig) Enabled ¶
func (c BuiltInMCPConfig) Enabled(name string) bool
Enabled reports whether the named built-in MCP server is enabled.
func (BuiltInMCPConfig) EnabledNames ¶
func (c BuiltInMCPConfig) EnabledNames() []string
EnabledNames returns the names of all enabled built-in MCP servers.
func (*BuiltInMCPConfig) SetEnabled ¶
func (c *BuiltInMCPConfig) SetEnabled(name string, enabled bool) bool
SetEnabled sets the enabled flag for the named built-in MCP server. Returns false if the name is unknown.
type CodegraphConfig ¶
type CodegraphConfig struct {
Enabled bool `toml:"enabled"`
AutoInstall bool `toml:"auto_install"`
Path string `toml:"path"` // local binary path (skips download)
Tier string `toml:"tier"`
DownloadURL string `toml:"download_url"` // custom download base URL for air-gapped/intranet (replaces GitHub default)
}
CodegraphConfig governs the built-in CodeGraph MCP server — symbol/call-graph code intelligence (tree-sitter + SQLite) that gives the agent codegraph_* search / context / explore / trace / node tools. Enabled defaults to true so upgrades keep it for existing configs; first-run scaffolds write enabled = false so only brand-new users start without it. AutoInstall (default true) lets fairpeer fetch the CodeGraph runtime into its cache when CodeGraph is enabled but missing; set false to require an explicit `fairpeer codegraph install` (e.g. for air-gapped or headless runs). Path overrides binary resolution; empty resolves the cache, then a `codegraph` on PATH, then a bundle beside the executable. CodeGraph always starts in the background when enabled; legacy tier values are ignored and removed during config load.
func (CodegraphConfig) ResolvedTier ¶
func (c CodegraphConfig) ResolvedTier() string
func (CodegraphConfig) ShouldAutoStart ¶
func (c CodegraphConfig) ShouldAutoStart() bool
type Config ¶
type Config struct {
ConfigVersion int `toml:"config_version"`
DefaultModel string `toml:"default_model"`
Language string `toml:"language"` // ui/model language tag (e.g. "zh"); empty = auto-detect from $LANG / $FAIRPEER_LANG
// ReasoningLanguage steers ONLY the visible thinking/reasoning text language
// (auto|zh|en), independent of the final-answer language. Default "auto" leaves
// it to the provider. It is injected as a transient per-turn block, never into
// the cache-stable system prompt prefix.
ReasoningLanguage string `json:"-" toml:"reasoning_language"` // auto|zh|en; empty = auto
UI UIConfig `toml:"ui"`
Desktop DesktopConfig `toml:"desktop"`
Notifications NotificationsConfig `toml:"notifications"`
Agent AgentConfig `toml:"agent"`
Providers []ProviderEntry `toml:"providers"`
Tools ToolsConfig `toml:"tools"`
Permissions PermissionsConfig `toml:"permissions"`
Sandbox SandboxConfig `toml:"sandbox"`
Network NetworkConfig `toml:"network"`
Plugins []PluginEntry `toml:"plugins"`
Skills SkillsConfig `toml:"skills"`
Codegraph CodegraphConfig `toml:"codegraph"`
BuiltInMCP BuiltInMCPConfig `toml:"builtin_mcp"`
Dream DreamConfig `toml:"dream"`
Statusline StatuslineConfig `toml:"statusline"`
LSP LSPConfig `toml:"lsp"`
Bot BotConfig `toml:"bot"`
// Cowork holds coWork (office) profile settings — currently just the browser
// path override. Empty means auto-detect; a non-empty path is tried first
// (and the user is guided to set it when no browser is found).
Cowork CoworkConfig `toml:"cowork"`
// LLM holds the global request budget (rate limiting) applied to all
// providers. RPM=0 (the default) disables limiting for backward compat.
LLM LLMConfig `toml:"llm"`
// Profiles holds optional [[profiles]] entries that override the built-in
// dev/cowork profiles by name. A name collision with a builtin replaces it,
// so users can customise a profile's model/prompt/skills without code. Empty
// means only the builtins are available (dev + cowork).
Profiles []Profile `toml:"profiles"`
}
Config is fairpeer's runtime configuration.
func Default ¶
func Default() *Config
Default returns the built-in default configuration. No provider is preset — FairPeer is provider-agnostic, so the user configures their own via the CLI setup wizard (fairpeer chat/run) or the desktop onboarding/settings panel. An empty DefaultModel + empty Providers means the first run enters the setup wizard (CLI) or the onboarding overlay (desktop).
func Load ¶
Load builds the configuration: defaults, then user config, then project config, then MCP servers from Claude Code's .mcp.json, then (lowest priority) the v0.x ~/.fairpeer/config.json's mcpServers. A .env in the working directory is loaded first so api_key_env can resolve.
func LoadForEdit ¶
LoadForEdit returns a config to seed the `fairpeer setup` wizard when reconfiguring: the built-in defaults with the file at path (if present) decoded on top, so a reconfigure preserves the user's existing providers and agent settings instead of resetting to defaults. .env is loaded so api_key_env resolution works while the wizard decides which keys are still missing.
func LoadForRoot ¶
LoadForRoot builds the configuration with project files resolved from root instead of the current working directory. When root is "" or ".", it behaves like Load(). This is the workspace-aware entry point: desktop tabs use it so each project's fairpeer.toml + .env + .mcp.json are resolved independently without changing the process cwd.
func (*Config) AddPermissionRule ¶
AddPermissionRule appends a rule ("ToolName" or "ToolName(glob)") to the allow / ask / deny list. The rule is validated with the same parser the gate uses, and a duplicate is a no-op so a UI can call it idempotently.
func (*Config) AddSkillPath ¶
AddSkillPath appends a custom skill root, deduping by its expanded absolute path while preserving the caller's original spelling in the config file.
func (*Config) AutoStartPlugins ¶
func (c *Config) AutoStartPlugins() []PluginEntry
func (*Config) BashMode ¶
BashMode normalises the bash-sandbox mode: only an explicit "off" disables it; empty or any other value resolves to "enforce", so the sandbox is on by default and fails safe.
func (*Config) BashTimeoutSeconds ¶
BashTimeoutSeconds returns the foreground bash timeout in seconds. An omitted config keeps the historical 120s safety cap, explicit 0 disables the tool-local cap, and positive values set a custom cap. Negative values fall back to the default so a typo cannot silently remove the safety net.
func (*Config) ClearPluginAuthentication ¶
func (c *Config) ClearPluginAuthentication(name string) (PluginEntry, bool, error)
ClearPluginAuthentication removes locally stored auth-like material for one MCP server while keeping the server entry itself. It intentionally leaves non-auth config (command, URL host/path, ordinary env/header keys, tier) alone.
func (*Config) DesktopCheckUpdates ¶
DesktopCheckUpdates reports whether the desktop should check for updates on startup. Missing configs default to true so existing users keep update notices.
func (*Config) DesktopCloseBehavior ¶
DesktopCloseBehavior normalizes the desktop close-window preference. It falls back to the legacy ui.close_behavior value for configs written before [desktop] existed.
func (*Config) DesktopDisplayMode ¶
DesktopDisplayMode normalizes the transcript display mode. Default is "minimal" (collapsed model-generated intermediate items).
func (*Config) DesktopLanguage ¶
DesktopLanguage normalizes the desktop UI language. Empty means auto-detect from the browser/OS locale; it deliberately does not read top-level language, which is used by the CLI/model-facing runtime.
func (*Config) DesktopMetrics ¶
DesktopMetrics reports whether the desktop sends opt-in aggregate agent metrics — anonymous (signal, bucket) counters, never content. Default off.
func (*Config) DesktopTelemetry ¶
DesktopTelemetry reports whether the desktop sends the anonymous launch ping. It carries no conversation, key, or file data — see desktop/README.md.
func (*Config) DesktopTheme ¶
DesktopTheme normalizes desktop.theme. New desktop users default to the light graphite product look; an explicit auto/light/dark is preserved.
func (*Config) DesktopThemeStyle ¶
DesktopThemeStyle normalizes desktop.theme_style. Empty means the frontend chooses the default style for the resolved desktop theme.
func (*Config) DisabledSkillNames ¶
DisabledSkillNames returns valid disabled skill identifiers, preserving the first spelling and dropping duplicates/empty entries.
func (*Config) ExcludeSkillPath ¶
ExcludeSkillPath hides any skill discovery root matching path. This is used by UI "remove source" actions for convention roots that are not stored in paths.
func (*Config) IsProfileKnown ¶
IsProfileKnown reports whether name resolves to a profile (builtin or configured). Empty returns true (resolves to dev).
func (*Config) IsSkillDisabled ¶
IsSkillDisabled reports whether name is configured as disabled.
func (*Config) NetworkProxyMode ¶
NetworkProxyMode normalizes network.proxy_mode to a known value.
func (*Config) NetworkProxySpec ¶
NetworkProxySpec returns the expanded proxy settings used by netclient.
func (*Config) Provider ¶
func (c *Config) Provider(name string) (*ProviderEntry, bool)
Provider returns the named provider entry.
func (*Config) ReadRoots ¶
ReadRoots returns the directories read_file/grep are confined to, with ${VAR} expanded. Empty (the default) means reads are unconfined — the safe default that preserves the agent's ability to read system files. boot passes this to builtin.ConfineReaders only when non-empty. Audit A7.
func (*Config) RemovePermissionRule ¶
RemovePermissionRule drops the first exact match of rule from the named list, reporting whether anything was removed.
func (*Config) RemovePlugin ¶
RemovePlugin deletes the named MCP server, reporting whether it was present.
func (*Config) RemoveProvider ¶
RemoveProvider deletes the named provider. References to the removed provider are migrated to the first remaining configured provider when possible. The default model is required, so removal is refused when no fallback exists; optional planner/subagent refs are cleared instead of being left dangling.
func (*Config) RemoveSkillPath ¶
RemoveSkillPath removes the first custom skill root matching path after expansion and path cleaning. It reports whether anything changed.
func (*Config) ResolveModel ¶
func (c *Config) ResolveModel(ref string) (*ProviderEntry, bool)
ResolveModel resolves a model reference to a provider entry whose Model is the selected model string (a copy, so the config's lists stay intact). It accepts:
- "provider/model" — that exact model under that provider;
- a provider name — the provider's default model;
- a bare model name — the (first) provider that lists it.
The returned entry is ready to build a provider from (NewProvider reads .Model), so a single "vendor with many models" entry yields one instance per model without duplicating base_url/api_key_env. Single-`model` entries still resolve by provider name, keeping older configs working unchanged.
func (*Config) ResolveModelWithFallback ¶
ResolveModelWithFallback resolves a model reference to the canonical "provider/model" form used by the desktop runtime. If ref is stale or empty, it tries the user's configured default_model before falling back to the first configured provider — so preference isn't overwritten by iteration order.
func (*Config) ResolveProfile ¶
ResolveProfile returns the effective profile for name, or an error if the name is unknown. The builtin floor is merged with the config's [[profiles]] entries (config wins on name collision). Empty name resolves to ProfileDev so callers that never set a profile get unprofiled behaviour. The returned *Profile is a copy of the merged entry; mutating it does not affect the Config.
func (*Config) ResolveSystemPrompt ¶
ResolveSystemPrompt returns the system prompt, reading system_prompt_file if set.
func (*Config) RestoreSkillPath ¶
RestoreSkillPath removes a pseudo-deleted skill source from excluded_paths.
func (*Config) Save ¶
Save writes the configuration back to the file it was loaded from (SourcePath), or to ./fairpeer.toml when none exists yet — the conventional project-local target a fresh GUI session would create.
func (*Config) SaveForRoot ¶
SaveForRoot saves the config to root's fairpeer.toml, falling back to the user's global config when root has no existing fairpeer.toml.
func (*Config) SaveTo ¶
SaveTo writes the configuration to path as annotated TOML, atomically: it writes a sibling temp file then renames, so a crash mid-write can't leave a half-written fairpeer.toml that fails to parse on next load. Parent directories are created as needed.
func (*Config) SaveToScope ¶
func (c *Config) SaveToScope(path string, scope RenderScope) error
func (*Config) SetAutoPlan ¶
SetAutoPlan sets the interactive auto-plan gate. "off" keeps plan mode manual; "on" opts into automatic read-only planning for complex-looking turns. "ask" is accepted as a legacy synonym for "on" but is never written back.
func (*Config) SetDefaultModel ¶
SetDefaultModel points default_model at an existing model. It accepts both forms used by the runtime resolver:
- "provider" — the provider's own default model;
- "provider/model" — that specific model under that provider.
Either is rejected when the target does not exist, so a UI can't strand the config on a model that doesn't exist.
func (*Config) SetDesktopAppearance ¶
SetDesktopAppearance sets desktop-only theme preferences. It must not affect CLI theme settings or provider-visible request data.
func (*Config) SetDesktopCheckUpdates ¶
SetDesktopCheckUpdates sets whether the desktop app checks for updates on startup. Manual checks remain available in Settings regardless of this value.
func (*Config) SetDesktopCloseBehavior ¶
SetDesktopCloseBehavior sets the desktop close-window preference. It is intentionally UI-only and must not affect model prompts or provider-visible request data.
func (*Config) SetDesktopDisplayMode ¶
SetDesktopDisplayMode sets the transcript display mode. UI-only.
func (*Config) SetDesktopLanguage ¶
SetDesktopLanguage pins the desktop UI language. It intentionally does not modify Config.Language, which is used by the CLI/model-facing runtime.
func (*Config) SetDesktopMetrics ¶
SetDesktopMetrics sets whether the desktop sends opt-in aggregate agent metrics.
func (*Config) SetDesktopTelemetry ¶
SetDesktopTelemetry sets whether the desktop sends the anonymous launch ping.
func (*Config) SetDreamEnabled ¶
SetDreamEnabled toggles the background self-evolution master switch. When disabled, neither Dream nor Distill spawns automatically or via manual trigger.
func (*Config) SetDreamIntervals ¶
SetDreamIntervals configures the Dream and Distill automatic-run cadence in days. A non-positive value is rejected (use the package defaults by leaving the field at 0 in TOML, not by passing <=0 here). Both intervals must be >= 1.
func (*Config) SetExpandThinking ¶
SetExpandThinking sets whether the desktop reasoning/thinking section is expanded by default. It is desktop-only and must not affect CLI output or provider-visible request data.
func (*Config) SetLanguage ¶
SetLanguage pins the CLI UI/model language; empty/auto clears the override so runtime detection falls back to FAIRPEER_LANG / locale.
func (*Config) SetNetwork ¶
func (c *Config) SetNetwork(n NetworkConfig) error
SetNetwork updates ordinary outbound network proxy settings. Invalid custom proxy settings are rejected here so the desktop panel cannot save a config that would break provider startup.
func (*Config) SetPermissionMode ¶
SetPermissionMode sets the writer-fallback mode. Accepts "ask", "allow", or "deny" (case-insensitive); anything else errors rather than silently defaulting, so a UI surfaces a typo instead of installing a surprising mode.
func (*Config) SetPlannerModel ¶
SetPlannerModel sets (or, with "", clears) agent.planner_model for two-model collaboration. A non-empty name must be a configured provider.
func (*Config) SetProviderEffort ¶
SetProviderEffort updates a provider's provider-specific thinking effort knob.
func (*Config) SetProviderThinking ¶
SetProviderThinking updates a provider's provider-specific thinking mode knob.
func (*Config) SetShowReasoning ¶
SetShowReasoning sets the CLI's default verbose-reasoning preference. When true, thinking text is shown in the chat TUI on startup; when false (the default), it stays collapsed until the user toggles it with Ctrl+O or /verbose.
func (*Config) SetSkillEnabled ¶
SetSkillEnabled persists a per-skill enable/disable preference. Skills are enabled by default; disabling records the name, enabling removes it.
func (*Config) SetUICloseBehavior ¶
SetUICloseBehavior is kept for callers compiled against the old edit API.
func (*Config) SetUIShortcutLayout ¶
historical behavior; "desktop" enables the two-axis desktop-style shortcuts.
func (*Config) SkillCustomPaths ¶
SkillCustomPaths returns the configured custom skill roots with ${VAR} expanded; empty entries are dropped.
func (*Config) SkillExcludedPaths ¶
SkillExcludedPaths returns configured skill roots that should be hidden from discovery, with ${VAR} expanded and empty entries dropped.
func (*Config) SkillMaxDepth ¶
SkillMaxDepth bounds nested skill discovery. Depth 3 favors bundled skill packs while Store keeps nested markdown safe by requiring descriptions.
func (*Config) UICloseBehavior ¶
UICloseBehavior is the legacy name for DesktopCloseBehavior.
func (*Config) UIShortcutLayout ¶
UIShortcutLayout normalizes the legacy CLI shortcut layout setting. It is kept for compatibility; Shift+Tab toggles Plan and Ctrl+Y toggles YOLO in both layouts.
func (*Config) UITheme ¶
UITheme normalizes ui.theme (dark/light/auto). Empty or unrecognized falls back to "auto", which follows the OS shell preference.
func (*Config) UIThemeStyle ¶
UIThemeStyle normalizes ui.theme_style. Empty means "pick the default style for the resolved light/dark shell".
func (*Config) UpsertPlugin ¶
func (c *Config) UpsertPlugin(e PluginEntry) error
UpsertPlugin adds e, or replaces an MCP server with the same name (preserving position). The transport-specific required fields are validated: stdio needs a command, http/sse need a url.
func (*Config) UpsertProvider ¶
func (c *Config) UpsertProvider(e ProviderEntry) error
UpsertProvider adds e, or replaces an existing provider with the same name (preserving its position). Required fields (name, kind, base_url, model/models) are validated; whether the kind is actually registered and the key resolves is checked later by provider.New / Validate, which give actionable errors.
func (*Config) WriteRoots ¶
WriteRoots returns the directories file-writer tools may modify: the workspace root (defaulting to the current working directory when unset) plus any AllowWrite extras, with ${VAR} expanded. The roots are returned as given (relative or absolute); the confiner resolves them to absolute, symlink-free paths. The result is always non-empty, so confinement is on by default.
func (*Config) WriteRootsForRoot ¶
WriteRootsForRoot is like WriteRoots but falls back to fallbackRoot when the config doesn't explicitly set a workspace_root. Desktop tabs pass their project root here so tool confinement is correct without changing cwd.
type CoworkConfig ¶
type CoworkConfig struct {
BrowserPath string `toml:"browser_path"` // absolute path to a Chromium-based browser exe; empty = auto-detect
// BrowserHeadless controls whether the driven browser runs headless. Default
// false (headed/visible) — a visible browser behaves closer to a human user,
// keeps login state in a persistent profile, and avoids the rendering
// quirks headless has on JS-heavy/anti-bot sites (e.g. GitHub's challenge
// page). Set true for servers/CI where there is no display.
BrowserHeadless bool `toml:"browser_headless"`
// BrowserUserDataDir gives the driven browser a persistent user-data
// directory. Empty = a fresh temp profile per launch (state is lost on
// restart). Set to a fixed path to keep cookies/login across sessions —
// essential for sites that require sign-in, and it reduces the "verify you
// are human" friction on revisit.
BrowserUserDataDir string `toml:"browser_user_data_dir"`
// PPTActiveTemplate is the id of the active PPT template (from the templates
// dir <user-config>/fairpeer/ppt-templates/<id>.json). When set, the ppt-wizard
// skill generates decks from that template: it opens the template's master_file
// in WPS (if any) and places content at the template's pre-defined layout
// coordinates, so most slides don't need per-step VLM perception. Empty = no
// template, the CUA builds from a blank deck.
PPTActiveTemplate string `toml:"ppt_active_template"`
// PPTMode selects how the ppt-wizard skill builds decks. "fast" (default
// when empty) generates in one pass with no rework; "validate" generates,
// then checks and reworks on issues. The desktop cowork settings panel
// exposes this as a dropdown; PPTActiveTemplate still selects the template
// used in either mode.
PPTMode string `toml:"ppt_mode"`
// SMTP configures outbound email (email_send). All fields required to enable
// sending; empty SMTPHost disables email_send (it returns a config error).
SMTP SMTPConfig `toml:"smtp"`
// RAGEnabled is the master switch for the knowledge base (RAG). nil/unset =
// enabled (the historical default, backward compatible). Set to false to
// fully disable the knowledge base: no auto-injection into messages, the
// rag_search/rag_import/... tools are not registered, and expert teams skip
// knowledge-base context. This is distinct from EmbeddingModel (which only
// toggles semantic reranking on top of FTS5) — RAGEnabled governs whether RAG
// runs at all.
RAGEnabled *bool `toml:"rag_enabled"`
// EmbeddingModel enables semantic RAG reranking. When set to a provider model
// ref that supports embeddings, rag_search computes a query embedding and
// reranks FTS5 hits by cosine similarity. Empty = FTS5-only (the default,
// works offline). Set to a real embedding model (e.g. a provider with kind
// "embedding") to upgrade RAG to hybrid.
EmbeddingModel string `toml:"embedding_model"`
// VLMBackend is legacy: VLM now always uses the provider multimodal chat path
// (base64 image_url content parts). Kept for TOML backward-compat but ignored.
// Use VLMModel / ScreenshotVLMModel to pick the vision model.
VLMBackend string `toml:"vlm_backend"`
// VLMModel is the provider model ref for image recognition (screen_perceive).
// E.g. "<provider>/<model>". Must be vision-capable (provider vision=true).
VLMModel string `toml:"vlm_model"`
// IMAP configures inbound email (email_read/search). Empty Host = read tools
// return "not configured". Reading uses go-imap + go-message (protocol-level
// correct: full SEARCH, RFC 2047 header decoding, multipart MIME).
IMAP IMAPConfig `toml:"imap"`
// EmailAccounts holds the mailboxes FairPeer can talk to. At load time
// normalizeEmailAccounts folds the legacy single [cowork.smtp]/[cowork.imap]
// pair above into EmailAccounts[0] when this slice is empty, so existing
// single-account configs keep working unchanged; new configs may use either
// form. Tools select an account by Name; Default (or [0]) is the fallback.
EmailAccounts []EmailAccount `toml:"email_accounts"`
// ExtractModel is the LLM used by the RAG deep-extraction pipeline (turns
// imported documents into a structured entity/relation graph). Empty = fall
// back to the active profile's main model. Pair with ExtractInterval /
// ExtractConcurrency to tune request cadence; the pipeline is conservative
// by default (1 concurrent chunk, 3s between chunks) to avoid rate limits.
ExtractModel string `toml:"extract_model"`
// ExtractInterval is the pause between chunk extractions (default "3s").
// Raise it on rate-limited endpoints; lower on generous local models.
ExtractInterval string `toml:"extract_interval"`
// ExtractConcurrency is how many chunks extract in parallel (default 1).
// Keep low to avoid tripping rate limits — extraction is a background task
// where throughput matters less than "no errors".
ExtractConcurrency int `toml:"extract_concurrency"`
// ScreenshotEnabled turns on the global-hotkey screenshot-to-VLM feature.
// When true, pressing ScreenshotHotkey anywhere (even when FairPeer is in
// the background) captures the screen, sends it to ScreenshotVLMModel for
// recognition, and replies via IM bot + in-app toast. Default false — the
// user opts in via the cowork settings tab.
ScreenshotEnabled bool `toml:"screenshot_enabled"`
// ScreenshotHotkey is the global hotkey combination (e.g. "Ctrl+Shift+Alt+W").
// Detected via GetAsyncKeyState polling so it fires even when FairPeer isn't
// focused. Default "Ctrl+Shift+Alt+W".
ScreenshotHotkey string `toml:"screenshot_hotkey"`
// ScreenshotVLMModel is the model used for screenshot recognition.
// This is the SINGLE place all image-recognition config lives —
// set it once in the cowork settings page.
ScreenshotVLMModel string `toml:"screenshot_vlm_model"`
// ScreenshotPrompt is the user prompt sent with the screenshot image to the
// VLM model. Users can customize this to change the solving behavior (e.g.
// focus on specific subjects, require verification, etc.). Empty means use
// the built-in default.
ScreenshotPrompt string `toml:"screenshot_prompt"`
// EStopHotkey is the global EMERGENCY-STOP hotkey for coWork desktop
// automation. Pressing it anywhere (even with FairPeer minimized) cancels
// the in-flight turn on the active tab — the kill switch for screen_* tools,
// whose clicks/typing are irreversible. Registered via Win32 RegisterHotKey
// like the screenshot hotkey. Default "Ctrl+Shift+Pause". Set to "off" to
// disable the feature entirely.
EStopHotkey string `toml:"estop_hotkey"`
// HEPort is the port for the Hyper-Extract Python server. Default 0 means
// use the built-in default (18900).
HEPort int `toml:"he_port"`
// BrowserUseEnabled controls whether the browser-use autonomous-browsing
// sidecar is wired up. When false (the zero-value DEFAULT), browser_auto
// returns a clear "disabled" error and no Python sidecar is started. This
// is intentionally opt-in: the sidecar needs the browser-use Python package
// installed (and a provider client), so users who haven't set that up are
// not bothered by startup failures. Set browser_use_enabled = true once
// the environment is ready.
BrowserUseEnabled bool `toml:"browser_use_enabled"`
// BrowserUsePython overrides the Python interpreter used to run the
// browser-use sidecar. Empty = "python" (Windows) / "python3" (other). In
// the packaged build this resolves to the bundled runtime's python.exe.
BrowserUsePython string `toml:"browser_use_python"`
// BrowserUsePort is the port for the browser-use sidecar. Default 0 means
// use the built-in default (18901, distinct from HE's 18900).
BrowserUsePort int `toml:"browser_use_port"`
// BrowserUseModel is the provider model ref the sidecar uses for the
// agentic loop (e.g. "<provider>/<model>"). Empty = fall back to VLMModel,
// then the main agent model. A strong vision-capable model is strongly
// recommended — the loop reads screenshots/accessibility trees.
BrowserUseModel string `toml:"browser_use_model"`
// BrowserUseMaxSteps caps the agentic loop. Default 0 means let the sidecar
// pick a sensible bound. Set lower for cheaper/faster runs, higher for
// complex multi-page tasks.
BrowserUseMaxSteps int `toml:"browser_use_max_steps"`
// FastLLMBaseDomain overrides the base URL for direct /chat/completions calls
// made by the scheduler time-parser and RAG ask (legacy path; Phase 3 will
// route these through the resolved fast-task provider instead). Empty = the
// built-in default.
FastLLMBaseDomain string `toml:"fast_llm_base_domain"`
}
CoworkConfig holds coWork (office) profile settings: browser path, PPT generation, email (SMTP/IMAP), RAG/knowledge base, screenshot recognition, hotkeys, and the Hyper-Extract service. BrowserPath overrides the auto-detected Chromium-based browser; when empty, browser_* tools probe the standard Chrome/Edge/Brave install locations and fall back to CHROME_PATH. Users set this when the browser isn't in a standard location — the agent surfaces a clear error guiding them to fill [cowork] browser_path.
func (CoworkConfig) DefaultEmailAccount ¶
func (c CoworkConfig) DefaultEmailAccount() (EmailAccount, bool)
DefaultEmailAccount returns the account to use when no name is given: the one flagged Default, else the first, else a zero account with ok=false.
func (CoworkConfig) EmailAccountByName ¶
func (c CoworkConfig) EmailAccountByName(name string) (EmailAccount, bool)
EmailAccountByName returns the account whose Name matches (case-insensitive), or the default account when name is empty. ok=false when the name is non-empty and unknown, or when there are no accounts at all.
func (CoworkConfig) RAGEnabledOrDefault ¶
func (c CoworkConfig) RAGEnabledOrDefault() bool
RAGEnabledOrDefault reports whether the knowledge base (RAG) is enabled. A nil RAGEnabled means the user never set it → enabled (backward compatible). Only an explicit false disables RAG. Callers that gate RAG behaviour should use this rather than dereferencing the pointer directly.
type DesktopConfig ¶
type DesktopConfig struct {
Language string `toml:"language"` // auto|en|zh; empty/auto = browser/OS auto-detect
Theme string `toml:"theme"` // auto|dark|light; empty resolves to dark
ThemeStyle string `toml:"theme_style"` // graphite|aurora|slate|carbon|nocturne|amber and legacy aliases
CloseBehavior string `toml:"close_behavior"` // quit|background; desktop window close behavior
DisplayMode string `toml:"display_mode"` // standard|compact|minimal; transcript display mode
CheckUpdates *bool `toml:"check_updates"` // startup update checks; nil keeps the default enabled
Telemetry *bool `toml:"telemetry"` // anonymous launch ping (install id + version + OS); nil keeps the default enabled
Metrics *bool `toml:"metrics"` // opt-in aggregate agent metrics (anonymous signal/bucket counts; no content); nil = disabled
ProviderAccess []string `toml:"provider_access"` // desktop-only list of provider entries shown in Settings > Model > Access
ExpandThinking bool `toml:"expand_thinking"` // true = show reasoning text expanded by default; false = collapsed
}
DesktopConfig controls desktop-only UI preferences. It is intentionally separate from top-level language and [ui] so desktop choices do not affect CLI language, terminal colours, or provider-visible prompt/request data.
type DreamConfig ¶
type DreamConfig struct {
Enabled bool `toml:"enabled"` // master switch; false disables both background agents
DreamInterval int `toml:"dream_interval"` // days between automatic Dream runs; 0 = default 7
DistillInterval int `toml:"distill_interval"` // days between automatic Distill runs; 0 = default 30
SkillColdDays int `toml:"skill_cold_days"` // days a skill is unused before cold-retirement; 0 = default 90
IdleMinutes int `toml:"idle_minutes"` // minutes of user inactivity before a Dream run may fire; 0 = default 10
}
DreamConfig controls the background self-evolution agents: Dream consolidates session knowledge into project memory, Distill extracts repeated workflows into reusable skills. Intervals are in days; a value <= 0 falls back to the default so a partially-specified [dream] section still behaves sanely.
func (DreamConfig) DistillIntervalDays ¶
func (d DreamConfig) DistillIntervalDays() int
DistillIntervalDays returns the effective Distill cadence in days.
func (DreamConfig) DreamIntervalDays ¶
func (d DreamConfig) DreamIntervalDays() int
DreamIntervalDays returns the effective Dream cadence in days, applying the default when the configured value is non-positive.
func (DreamConfig) IdleMinutesEffective ¶
func (d DreamConfig) IdleMinutesEffective() int
IdleMinutesEffective returns the effective user-inactivity threshold in minutes before an idle Dream run may fire, applying the default when the configured value is non-positive. A negative value disables idle triggering entirely (Dream then only runs via manual trigger).
func (DreamConfig) SkillColdDaysEffective ¶
func (d DreamConfig) SkillColdDaysEffective() int
SkillColdDaysEffective returns the effective skill-cold threshold in days, applying the default when the configured value is non-positive.
type EffortCapability ¶
EffortCapability describes the abstract effort levels a provider/model can set through the /effort command.
func EffortCapabilityForEntry ¶
func EffortCapabilityForEntry(e *ProviderEntry) EffortCapability
EffortCapabilityForEntry returns the user-facing /effort levels for a resolved provider entry. Provider implementations still decide how a stored effort is serialized into requests.
func UnifiedEffortCapability ¶
func UnifiedEffortCapability() EffortCapability
UnifiedEffortCapability returns the standard effort capability for any provider that supports reasoning. This is the Claude Code-style approach: a simple low/medium/high vocabulary that works across all providers.
type EmailAccount ¶
type EmailAccount struct {
Name string `toml:"name"` // stable handle tools/scheduler address (e.g. "personal-139")
Default bool `toml:"default"` // used when a tool omits an account name
SMTP SMTPConfig `toml:"smtp"`
IMAP IMAPConfig `toml:"imap"`
}
EmailAccount bundles one mailbox's inbound (IMAP) and outbound (SMTP) settings under a user-chosen name, so FairPeer can talk to multiple mailboxes at once (e.g. a personal 139 box and a work CMCC box). Tools/scheduler select an account by Name; the one flagged Default (or else the first) is used when the caller omits a name.
type FeishuBotConfig ¶
type FeishuBotConfig struct {
Enabled bool `toml:"enabled"`
Domain string `toml:"domain"` // feishu(默认)| lark
AppID string `toml:"app_id"`
AppSecretEnv string `toml:"app_secret_env"` // 如 FEISHU_BOT_APP_SECRET
VerificationToken string `toml:"verification_token"` // 事件订阅验证 token
Mode string `toml:"mode"` // webhook(默认)| websocket
WebhookPort int `toml:"webhook_port"` // webhook 模式端口
RequireMention bool `toml:"require_mention"`
}
FeishuBotConfig 飞书自建应用 Bot 配置。
type IMAPConfig ¶
type IMAPConfig struct {
Host string `toml:"host"` // IMAP server host, e.g. imap.example.com
Port int `toml:"port"` // IMAP port (993 for implicit TLS, 143 for STARTTLS/plain)
Username string `toml:"username"` // mailbox login
PasswordEnv string `toml:"password_env"` // env var holding the password
SkipTLSVerify bool `toml:"skip_tls_verify"` // opt in to skip TLS cert verification (self-signed/corporate CAs). Verification stays ON unless this is true.
}
IMAPConfig holds inbound mail server settings for email_read/search.
type LLMConfig ¶
type LLMConfig struct {
RPM int `toml:"rpm"` // max requests/minute per API key (0 = unlimited)
TPM int `toml:"tpm"` // max tokens/minute (0 = unlimited; reserved, not enforced yet)
ReserveMain int `toml:"reserve_main"` // requests reserved for main-agent priority (default 2)
}
LLMConfig holds the global LLM request budget (rate limiting). It applies across ALL providers via a decorator, so main-agent + subagent + RAG extraction + IM bot responses all share the same per-API-key RPM quota.
RPM reflects the user's real API-key rate limit (default 60/min; higher tiers or other providers may allow more). Leave at 0 to disable rate limiting entirely (unlimited, backward-compatible).
ReserveMain keeps main-agent requests always answerable even when background tasks (expert teams, extraction) have consumed most of the per-minute quota: background requests wait for the next window when remaining <= reserve.
type LSPConfig ¶
type LSPConfig struct {
Enabled bool `toml:"enabled"`
Servers map[string]LSPServer `toml:"servers"`
}
LSPConfig governs the optional Language Server Protocol tools (lsp_definition, lsp_references, lsp_hover, lsp_diagnostics). Enabled defaults to true; the servers themselves are never bundled — each resolves on PATH and the tool returns an install hint when it is missing, so the capability is dormant until the user installs a server. Servers overrides or extends the built-in language → server map, keyed by language id (e.g. "go", "rust", "python").
type LSPServer ¶
type LSPServer struct {
Command string `toml:"command"`
Args []string `toml:"args"`
Env map[string]string `toml:"env"`
LanguageID string `toml:"language_id"`
Extensions []string `toml:"extensions"`
InstallHint string `toml:"install_hint"`
}
LSPServer overrides a built-in language's server or, when keyed by a new language, adds one. An empty field falls back to the built-in default for that language; Extensions is required when adding a language the built-ins don't cover (e.g. ".ex" for Elixir) so files route to it.
type MCPImportCandidate ¶
type MCPImportCandidate struct {
Entry PluginEntry
Recommended bool
Reasons []string
}
func LoadCCSwitchMCPCandidates ¶
func LoadCCSwitchMCPCandidates() ([]MCPImportCandidate, error)
type MigrationResult ¶
MigrationResult summarizes a one-time legacy import for the boot-time notice.
func MigrateLegacyIfNeeded ¶
func MigrateLegacyIfNeeded() (*MigrationResult, error)
MigrateLegacyIfNeeded performs a one-time, non-destructive import of older installs into the current user config when the latter does not exist yet. It checks v1-era TOML first, then v0.5/v0.x ~/.fairpeer/config.json, and never modifies or deletes the legacy files. Returns nil when there is nothing to migrate, or when the current user config already exists.
func (*MigrationResult) Notice ¶
func (r *MigrationResult) Notice() string
type NetworkConfig ¶
type NetworkConfig struct {
// ProxyMode is "auto" (default; environment proxy for now), "env", "custom",
// or "off". auto leaves room for OS proxy detection later without changing the
// config shape.
ProxyMode string `toml:"proxy_mode"`
// ProxyURL is an advanced custom override such as "socks5://127.0.0.1:7890".
// When set and proxy_mode = "custom", it wins over the structured proxy table.
ProxyURL string `toml:"proxy_url"`
// NoProxy is honored for custom proxies. Env/auto modes use NO_PROXY from the
// process environment instead.
NoProxy string `toml:"no_proxy"`
Proxy NetworkProxyConfig `toml:"proxy"`
}
NetworkConfig controls ordinary outbound HTTP traffic such as model providers, updater checks, CodeGraph downloads, and web_fetch. web_fetch reuses these proxy settings while keeping its own SSRF-guarded dialer.
type NetworkProxyConfig ¶
type NetworkProxyConfig struct {
Type string `toml:"type"` // http|https|socks5|socks5h
Server string `toml:"server"`
Port int `toml:"port"`
Username string `toml:"username"`
Password string `toml:"password"`
}
NetworkProxyConfig is the structured custom-proxy editor shape. Password is optional and supports ${VAR} expansion, so users can avoid storing it literally.
type NotificationsConfig ¶
type NotificationsConfig struct {
Enabled bool `toml:"enabled"`
TurnDone bool `toml:"turn_done"`
ApprovalRequest bool `toml:"approval_request"`
AskRequest bool `toml:"ask_request"`
}
NotificationsConfig controls optional system notifications for CLI chat/run.
type PermissionsConfig ¶
type PermissionsConfig struct {
Mode string `toml:"mode"`
Allow []string `toml:"allow"`
Ask []string `toml:"ask"`
Deny []string `toml:"deny"`
}
PermissionsConfig declares the per-call permission policy (see internal/permission). Mode is the fallback decision for writer tools when no rule matches ("ask" | "allow" | "deny"; default "ask"); read-only tools always fall back to allow. Allow/Ask/Deny are rule lists of the form "ToolName" or "ToolName(glob)". Precedence: deny > ask > allow > fallback.
type PluginEntry ¶
type PluginEntry struct {
Name string `toml:"name"`
Type string `toml:"type"` // "stdio" (default) | "http" | "sse"
Command string `toml:"command"`
Args []string `toml:"args"`
Env map[string]string `toml:"env"`
URL string `toml:"url"`
Headers map[string]string `toml:"headers"`
// AutoStart controls whether the server connects during session startup.
// Nil preserves historical behavior: configured servers start automatically.
AutoStart *bool `toml:"auto_start"`
// Tier selects how aggressively the server is connected at boot:
// "eager" — blocks startup until the handshake completes; required for
// servers whose tools the system prompt depends on.
// "lazy" — registers placeholder tools immediately (from on-disk
// schema cache when available) and only spawns the real
// subprocess on first model use. Kept for legacy configs.
// "background" — placeholder + spawn fired at boot but not waited on;
// swap happens once the spawn finishes.
// Empty defaults to "background" so enabled MCPs connect automatically
// without blocking chat. Unknown non-empty values fall back to "lazy".
Tier string `toml:"tier"`
// CallTimeout overrides the per-call default timeout (60s) for this MCP
// server's JSON-RPC calls. Prevents a slow server from blocking the agent.
// Format: Go duration string (e.g. "30s", "2m", "0" to disable).
CallTimeout string `toml:"call_timeout"`
// Risk marks this MCP server's tools' risk class for the permission gate
// (SPEC v2 §3.2A). MCP tools default to "external" (safe: outward
// operations need approval). Set to "read" for a trusted read-only server
// so its tools don't prompt; "write_local"/"exec" for the in-between cases.
// Empty/unknown = "external" (fail-safe). See internal/permission/risk.go.
Risk string `toml:"risk"`
}
PluginEntry declares an external MCP server. Type selects the transport: "stdio" (default) launches Command/Args/Env as a subprocess; "http" (a.k.a. streamable-http) and "sse" connect to a remote URL with optional static Headers. String fields support ${VAR} / ${VAR:-default} expansion so secrets (bearer tokens, keys) come from the environment, not the file. The fields mirror Claude Code's mcpServers spec, so entries can come from either fairpeer.toml's [[plugins]] or a project-root .mcp.json (see loadMCPJSON).
func ClearPluginAuthenticationInSource ¶
func ClearPluginAuthenticationInSource(name string) (PluginEntry, bool, string, error)
ClearPluginAuthenticationInSource clears auth material in the file that actually owns the MCP server. Load() merges user/project TOML and project .mcp.json into one Config, so callers must not mutate that merged view and Save() it back: a .mcp.json-only server would otherwise be serialized into fairpeer.toml or the user config. Source priority mirrors Load(): project TOML, user TOML, then the project .mcp.json entry if TOML did not define that server.
func LoadCCSwitchMCP ¶
func LoadCCSwitchMCP() ([]PluginEntry, error)
LoadCCSwitchMCP reads MCP servers enabled for Codex from cc-switch and maps them to fairpeer plugin entries. Newer cc-switch stores servers in SQLite; older installs kept them in config.json(.migrated/.bak), so we support both.
func NormalizePluginCommandLine ¶
func NormalizePluginCommandLine(e PluginEntry) (PluginEntry, bool)
NormalizePluginCommandLine repairs the common MCP copy/paste mistake where a tutorial's full command line is placed in command while args is left empty. Valid commands that are just paths with spaces are left untouched unless they are quoted or start with a known MCP runner such as npx/uvx/node.
func (PluginEntry) ExpandedPlugin ¶
func (e PluginEntry) ExpandedPlugin() PluginEntry
ExpandedPlugin returns a copy of e with ${VAR} references expanded across the command, args, env values, url, and header values — the fields Claude Code also expands. The entry itself is left untouched.
func (PluginEntry) ResolvedTier ¶
func (e PluginEntry) ResolvedTier() string
ResolvedTier returns the normalized tier ("eager"|"lazy"|"background") with the project default applied. Unknown values fall back to "lazy" so a typo never forces a slow boot.
func (PluginEntry) ShouldAutoStart ¶
func (e PluginEntry) ShouldAutoStart() bool
type Profile ¶
type Profile struct {
Name string `toml:"name"`
DisplayName string `toml:"display_name"`
Model string `toml:"model"` // overrides DefaultModel; "" = config default
SubagentModel string `toml:"subagent_model"` // overrides agent.subagent_model; "" = unchanged
Effort string `toml:"effort"` // overrides effort; "" = provider default
SystemPromptAddon string `toml:"system_prompt_addon"` // appended to resolved prompt; "" = unchanged
SystemPromptFile string `toml:"system_prompt_file"` // when set, replaces the resolved prompt entirely
EnabledSkills []string `toml:"enabled_skills"` // whitelist; empty = all skills
DisabledSkills []string `toml:"disabled_skills"` // extra-disabled on top of config
Plugins []string `toml:"plugins"` // plugin name whitelist; empty = all plugins
HiddenTools []string `toml:"hidden_tools"` // tools to Hide from main loop schemas; empty = all visible. Subagents still see them via FilterRegistry.
WorkspaceType string `toml:"workspace_type"` // "code" | "document"; frontend hint only
}
Profile is a named bundle of boot.Options overrides that switches the whole Controller between product modes — today "dev" (coding) and "cowork" (office). A profile does NOT replace configuration; it layers on top of it:
- Model / SubagentModel / Effort override the resolved provider knobs, so a coWork profile can pin a cheaper/faster model without touching fairpeer.toml.
- SystemPromptAddon is appended to the resolved system prompt (after the instruction/output-style/memory/skill folding in boot.Build), so a profile can bias behaviour (e.g. "you are an office agent") without owning the whole prompt. Empty means no change.
- DisabledSkills / EnabledSkills flip skill availability. EnabledSkills is a whitelist: when non-empty, only those (plus anything the profile does not name) — see ResolveSkillDisabled for the exact merge. For Phase 0 both stay empty so the skill set is unchanged.
- Plugins whitelists which [[plugins]] entries are visible. Empty = all plugins (unchanged behaviour), so dev stays dev until coWork opts in.
- WorkspaceType is a frontend hint ("code" | "document") that selects the layout; the backend ignores it. It rides the profile so the switch is atomic across Go rebuild + React layout.
Design rationale: profile switching reuses the proven SetModelForTab rebuild flow (acquire shared host → snapshot history → Close → boot.Build → Resume). A profile is therefore just "a richer set of boot.Options inputs" — not a new runtime concept. Everything here is resolved once in config and consumed in boot.Build / desktop.app.
func DefaultProfiles ¶
func DefaultProfiles() []Profile
DefaultProfiles returns the profiles effective when fairpeer.toml declares no [[profiles]]. The caller (Config.Profiles resolution) merges user entries on top of these by name.
func (*Profile) ResolveSkillDisabled ¶
ResolveSkillDisabled merges the config-wide disabled-skill set with a profile's skill overrides and returns the effective disabled set (skill name key → true).
Merge rules:
- Start from cfg.DisabledSkillNames() (the [skills].disabled config).
- Profile.DisabledSkills is additive (a profile can disable more).
- Profile.EnabledSkills, when non-empty, is a whitelist: any skill NOT in it is disabled. This lets a future cowork profile expose only office skills. Empty EnabledSkills (Phase 0) means "no whitelist, keep all".
The returned map uses SkillNameKey normalization so it composes with the existing config-disabled set regardless of platform case rules.
type ProviderEntry ¶
type ProviderEntry struct {
Name string `toml:"name"`
Kind string `toml:"kind"`
BaseURL string `toml:"base_url"`
Model string `toml:"model"` // a single model (back-compat)
Models []string `toml:"models"` // a vendor's model list (one base_url/key, many models)
ModelsURL string `toml:"models_url"` // auto-fetch models from this URL on startup
Default string `toml:"default"` // default model when Models is set (else Models[0])
// FastModel is the lightweight model used for background/fast tasks
// (dream/distill/rag-extract, scheduler time-parse). Empty = fall back to
// Default at runtime. This is the per-provider "fast" role; the global
// agent.fast_task_model can override it.
FastModel string `toml:"fast_model"`
APIKeyEnv string `toml:"api_key_env"`
ContextWindow int `toml:"context_window"`
Price *provider.Pricing `toml:"price"`
// Thinking / Effort are provider-kind-specific knobs forwarded to the provider
// via Config.Extra. The anthropic provider reads Thinking="adaptive" to enable
// extended thinking and Effort ("low".."max") to tune depth. The
// openai-compatible provider forwards Effort as reasoning_effort for
// thinking-capable models.
// Empty = provider default.
Thinking string `toml:"thinking"`
Effort string `toml:"effort"`
// ReasoningProtocol selects the request shape for OpenAI-compatible reasoning
// models. Empty/auto uses the model capability registry plus endpoint
// heuristics; none disables automatic reasoning controls for this provider.
ReasoningProtocol string `toml:"reasoning_protocol"`
// SupportedEfforts lists the /effort levels this provider/model exposes.
// When non-empty, it overrides the built-in defaults. All providers
// support the unified low/medium/high vocabulary by default. "auto" is
// the implicit prefix — always accepted.
SupportedEfforts []string `toml:"supported_efforts"`
// DefaultEffort is the /effort level used when the user picks "auto" or
// has not set Effort. Ignored when SupportedEfforts is empty.
DefaultEffort string `toml:"default_effort"`
// Vision enables image support for this provider. When true, user-attached
// images are sent as image_url content parts. When false (default), images
// are stripped before sending to avoid 400 errors from text-only models.
Vision bool `toml:"vision"`
// VisionDetail controls the image detail level sent to the API ("auto",
// "low", "high"). Only effective when Vision is true.
VisionDetail string `toml:"vision_detail"`
// NoProxy reaches this provider's base_url directly, never through the proxy.
// For China-only endpoints a foreign-exit proxy resets the TLS handshake (#2803).
NoProxy bool `toml:"no_proxy"`
// CodingOnly marks this provider as consuming a Coding Plan subscription
// quota (vs the regular token quota). UI surfaces a "consumes subscription
// quota" hint; optionally restricts to coding-tool use per vendor terms.
CodingOnly bool `toml:"coding_only"`
// Aggregator marks this provider as a model-aggregation platform that can
// call multiple vendors' models under one endpoint+key (e.g. a Coding Plan).
// UI groups these under an "aggregators" section; not a routing branch.
Aggregator bool `toml:"aggregator"`
}
ProviderEntry declares a model provider instance. ContextWindow is the model's token budget; the harness compacts older history as a turn's prompt approaches it (see agent compaction). 0 disables compaction for the instance.
func (*ProviderEntry) APIKey ¶
func (e *ProviderEntry) APIKey() string
APIKey resolves the entry's API key from its api_key_env.
func (*ProviderEntry) ChatModelList ¶
func (e *ProviderEntry) ChatModelList() []string
ChatModelList returns ModelList filtered to likely chat/completion models. Non-chat models (TTS, STT, ASR, embedding, etc.) are excluded so they do not appear in the chat model picker. Use ModelList() only when the full raw provider model list is needed, such as config serialization, provider diagnostics, or model-fetch editing.
func (*ProviderEntry) Configured ¶
func (e *ProviderEntry) Configured() bool
Configured reports whether the provider's api_key_env is set — the same check Validate enforces, so pickers can filter on it.
func (*ProviderEntry) DefaultModel ¶
func (e *ProviderEntry) DefaultModel() string
DefaultModel returns the provider's default model: the explicit `default`, else the first of ModelList.
func (*ProviderEntry) FetchModels ¶
func (e *ProviderEntry) FetchModels(ctx context.Context) ([]string, error)
FetchModels queries the provider's OpenAI-compatible GET /models endpoint and returns the available model IDs, sorted alphabetically.
func (*ProviderEntry) HasModel ¶
func (e *ProviderEntry) HasModel(m string) bool
HasModel reports whether m is one of the provider's models.
func (*ProviderEntry) ModelList ¶
func (e *ProviderEntry) ModelList() []string
ModelList returns the models this provider exposes: the explicit `models` list, or the single `model` as a one-element list (back-compat). Empty if neither set.
type QQBotConfig ¶
type QQBotConfig struct {
Enabled bool `toml:"enabled"`
AppID string `toml:"app_id"`
AppSecretEnv string `toml:"app_secret_env"` // 环境变量名,如 QQ_BOT_APP_SECRET
}
QQBotConfig QQ 官方 Bot API v2 配置。
type RenderScope ¶
type RenderScope string
const ( RenderScopeFull RenderScope = "full" RenderScopeUser RenderScope = "user" RenderScopeProject RenderScope = "project" )
type SMTPConfig ¶
type SMTPConfig struct {
Host string `toml:"host"` // SMTP server host, e.g. smtp.example.com
Port int `toml:"port"` // SMTP port (587 for STARTTLS, 465 for implicit TLS, 25 plain)
From string `toml:"from"` // sender address, e.g. agent@example.com
Username string `toml:"username"` // SMTP auth username (often = From); empty = no auth
PasswordEnv string `toml:"password_env"` // env var holding the SMTP password (never stored)
UseTLS bool `toml:"use_tls"` // implicit TLS (port 465); false = STARTTLS/plain. DEPRECATED: use encryption_mode.
EncryptionMode string `toml:"encryption_mode"` // "tls" (implicit, 465) | "starttls" (587) | "none" (25). Empty → migrate from use_tls.
}
SMTPConfig holds outbound mail server settings. Secrets come from the env via PasswordEnv (never stored in the TOML).
type SandboxConfig ¶
type SandboxConfig struct {
WorkspaceRoot string `toml:"workspace_root"`
AllowWrite []string `toml:"allow_write"`
// Bash is the OS-sandbox mode for the bash tool: "enforce" (default) jails
// each command, "off" runs it unconfined. Phase 1; macOS only for now, with
// a graceful fallback elsewhere (see internal/sandbox).
Bash string `toml:"bash"`
// Network allows network egress from inside the bash sandbox. Defaults true
// so module/package downloads keep working; the boundary is then writes.
Network bool `toml:"network"`
// RequireAvailable, when true, makes bash mode "enforce" fail-closed (refuse
// all commands) if no OS sandbox is available on this platform, rather than
// silently degrading to unconfined. False (default) keeps the graceful
// fallback so fairpeer stays usable on unsupported OSes.
RequireAvailable bool `toml:"require_available"`
// StrictWrites narrows the macOS Seatbelt toolchain-cache grants to true
// cache subdirs only (~/.cargo/registry/cache, ~/Library/Caches, …) so a
// prompt-injected command can't drop an executable in ~/.cargo/bin or
// ~/.npm. Default false: the broad grants are needed for `go install`/
// `cargo build`/`npm install` (they write to bin/pkg dirs). Turn on for
// high-security deployments that don't run build tools. Audit A8. macOS only.
StrictWrites bool `toml:"strict_writes"`
// ReadRoots confines read_file/grep to these directories (opt-in). Empty =
// unconfined reads (the default), because an agent legitimately reads /etc,
// system headers, ~/.gitconfig, package caches, etc. A high-security
// deployment that wants read/data isolation sets this; boot then wires
// ConfineReaders to override the unconfined defaults. Note: bash is NOT
// read-confined even when this is set, so this is defense-in-depth. Audit A7.
ReadRoots []string `toml:"read_roots"`
}
SandboxConfig bounds the blast radius of tool calls (Phase 0: file-writer confinement). WorkspaceRoot is the directory the built-in file writers (write_file / edit_file / multi_edit) may modify; empty means the current working directory, so writes stay inside the project by default. AllowWrite lists extra directories writers may also touch (e.g. a sibling repo or a temp dir). Both support ${VAR} / ${VAR:-default} expansion. Reads are unrestricted; confining `bash` is Phase 1 (OS-level sandbox).
type SearchConfig ¶
SearchConfig tunes the grep tool's engine. Engine is "auto" (default — use ripgrep when it's on PATH, else the native Go scanner), "native" (always Go), or "rg" (require ripgrep; warn at startup and fall back to native if absent). RgPath optionally points at a specific ripgrep binary instead of a PATH lookup.
type SkillsConfig ¶
type SkillsConfig struct {
Paths []string `toml:"paths"`
ExcludedPaths []string `toml:"excluded_paths"`
DisabledSkills []string `toml:"disabled_skills"`
MaxDepth int `toml:"max_depth"`
}
SkillsConfig configures skill discovery. Paths adds extra "custom"-scope skill roots — each a directory of SKILL.md / <name>.md playbooks — scanned between the project roots (.fairpeer/.agents/.agent/.claude under the workspace) and the global roots. ExcludedPaths hides matching discovery roots without deleting folders. ~, relative paths, and ${VAR} expansion are supported. DisabledSkills hides named skills from the agent prompt, slash invocation, and skill tools while keeping them manageable.
type StatuslineConfig ¶
type StatuslineConfig struct {
Command string `toml:"command"`
}
StatuslineConfig configures a custom status line. Command, when set, is run at startup and after each turn; its first line of stdout replaces the built-in status data row. A JSON payload (model, context tokens, cwd) is fed on stdin.
type ToolsConfig ¶
type ToolsConfig struct {
Enabled []string `toml:"enabled"`
BashTimeoutSeconds *int `toml:"bash_timeout_seconds"`
Search SearchConfig `toml:"search"`
}
ToolsConfig selects which built-in tools are enabled. Empty means all of them.
type UIConfig ¶
type UIConfig struct {
Theme string `toml:"theme"` // auto|dark|light; empty resolves to auto
ThemeStyle string `toml:"theme_style"` // graphite|aurora|slate|carbon|nocturne|amber and legacy aliases
ShortcutLayout string `toml:"shortcut_layout"` // classic|desktop; accepted for compatibility
CloseBehavior string `toml:"close_behavior"` // legacy desktop close behavior; prefer desktop.close_behavior
ShowReasoning bool `toml:"show_reasoning"` // Ctrl+O / /verbose: show thinking text in CLI; false = collapsed
}
UIConfig controls CLI presentation-only settings. Desktop appearance is kept in DesktopConfig so desktop preferences cannot alter terminal output or prompts.