config

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Jul 9, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Index

Constants

View Source
const ActiveProviderEnv = "ZERO_PROVIDER"

ActiveProviderEnv selects the active provider profile by name (read in applyEnv).

View Source
const AnthropicBaseURL = "https://api.anthropic.com"
View Source
const GoogleBaseURL = "https://generativelanguage.googleapis.com"
View Source
const MaxRecentModels = 5

MaxRecentModels caps the persisted/displayed recent-selection history.

View Source
const MaxTurnsCeiling = 500

MaxTurnsCeiling caps the per-run tool-turn budget so a stray env value or typo can't set an absurd ceiling. Shared between applyEnv (read site) and the /turns command (write site) so the bound holds even if the env is set by a raw shell.

View Source
const MaxTurnsEnv = "ZERO_MAX_TURNS"

MaxTurnsEnv overrides the per-run tool-turn budget by name (read in applyEnv).

View Source
const OpenAIBaseURL = "https://api.openai.com/v1"

Variables

View Source
var ErrNoActiveProvider = errors.New("no active provider configured")

ErrNoActiveProvider marks a resolve failure caused solely by a missing or unresolvable active provider. The interactive TUI treats this as "needs onboarding" (drop into the setup wizard) rather than a fatal config error.

View Source
var ErrProviderRequiresModel = errors.New("provider requires model")

ErrProviderRequiresModel marks a resolve failure caused solely by the active provider missing a model with no catalog default to fall back on (custom openai-/anthropic-compatible endpoints — Zero cannot guess a gateway's model). Like ErrNoActiveProvider, the interactive TUI treats it as "needs onboarding" and drops into the setup wizard so the user can fix it; headless commands (zero config, zero exec) still fail with the actionable message.

Functions

func ClearProviderKeyStored

func ClearProviderKeyStored(path, provider string) (bool, error)

ClearProviderKeyStored unsets the APIKeyStored marker for a provider in the config at path, so credential checks no longer claim a stored key after one is removed. No-op when path/provider is empty, the config is absent, or the marker is already unset.

func DefaultMCPServers

func DefaultMCPServers() map[string]MCPServerConfig

DefaultMCPServers returns the MCP servers Zero ships ENABLED by default so web search and scraping work out of the box with no setup and no API key. They are seeded before user/project config is merged (see ResolveMCP), so a user can override any field — for example point firecrawl at a self-hosted instance, or add an API-key header to lift the free-tier limit — or disable it entirely with `zero mcp disable <name>` (which writes `"disabled": true`).

Keyless Firecrawl routes requests through firecrawl.dev (1,000 free credits per month, no account). Self-host Firecrawl (AGPL-3.0) for unlimited and private use. Zero only calls it over the network, so Firecrawl's license never reaches into Zero's own code.

func DefaultUserConfigPath

func DefaultUserConfigPath() (string, error)

func ForgetProviderKey

func ForgetProviderKey(provider string) (bool, error)

ForgetProviderKey removes a provider's stored API key from the credential store, reporting whether one existed. Used by the lifecycle "remove key" / auth logout.

func HasProviderProfile

func HasProviderProfile(profile ProviderProfile) bool

func IsDefaultMCPServer

func IsDefaultMCPServer(name string) bool

IsDefaultMCPServer reports whether name is one of Zero's built-in default MCP servers. The config commands use it so a default can be disabled/enabled even though it is not written to the user's config file until overridden.

func IsUnconfiguredDefault added in v0.3.0

func IsUnconfiguredDefault(name string, server MCPServerConfig) bool

IsUnconfiguredDefault reports whether server is one of Zero's built-in defaults that the user never wrote an entry for in their config — i.e. it is running with whatever Zero ships (e.g. keyless Firecrawl, no credentials).

Both conditions below must hold:

  • !server.configured: the user's JSON never declared an object for this server key at all (set by MCPServerConfig.UnmarshalJSON only when it actually ran for this key). Any explicit action — including a disable/enable toggle like `zero mcp enable firecrawl` that leaves the resolved value unchanged — sets configured, so it always counts as user-configured, even though the value comparison below could not tell the difference on its own.
  • reflect.DeepEqual(def, server): the value still matches the default. This is the fallback for callers that construct MCPServerConfig directly rather than through the JSON/merge pipeline (server.configured is then always false) — without it, any hand-built config with different field values would be misreported as unconfigured.

Callers use this to tell "server we turned on for the user" apart from "server the user configured themselves," e.g. to avoid warning loudly when an out-of-the-box default that was never given credentials fails to connect.

func MarkProviderAPIKeyStored added in v0.3.0

func MarkProviderAPIKeyStored(path string, provider string) error

MarkProviderAPIKeyStored records that provider's API key now lives in the credential store. It also clears inline/env key fields so the stored key is the runtime credential; an old apiKeyEnv value must not keep overriding a freshly captured key from `zero auth openrouter` or provider setup.

func MigratePlaintextProviderKeys

func MigratePlaintextProviderKeys(path string, store APIKeySetter) (int, error)

MigratePlaintextProviderKeys moves any inline plaintext API key in the config at path into the credential store, marking the profile APIKeyStored and stripping the inline secret — but ONLY after the store write succeeds, so a failed Set leaves the plaintext key in place and never strands a credential. Returns how many keys were migrated; a no-op (0, nil) when path is empty/absent or nothing needs migrating. Safe to run on every startup (idempotent).

func ProviderKeyStore

func ProviderKeyStore() (*credstore.Store, error)

ProviderKeyStore opens the credential store beside the default user config.

func ProviderKeyStoreAt

func ProviderKeyStoreAt(dir string) (*credstore.Store, error)

ProviderKeyStoreAt opens the encrypted credential store whose file backend lives in dir. The backend resolves keyring-first, then encrypted-file, with a plaintext opt-out via ZERO_CRED_STORAGE. The dir parameter exists so tests can point the file backend at a temp directory; production always uses the user config directory (ProviderKeyStore) because provider API keys are user-scoped by design — they are only ever captured under the user config, never project config (a cloned repo must not carry keys), so runtime lookups deliberately use the user store regardless of where a provider profile was resolved from.

func SetActiveProviderEnv

func SetActiveProviderEnv(name string)

SetActiveProviderEnv exports the active provider name to the process environment so a spawned child process (which inherits the environment) resolves the SAME provider profile — and therefore the same credentials (env key / stored key / OAuth) — as its parent. Without this a sub-agent re-resolves config.json's default provider and can land on one whose credentials don't match the parent's live selection, failing auth the instant it spawns. A blank name CLEARS the variable: switching back to an unnamed/default profile must not keep exporting a stale provider to children.

func SetMaxTurnsEnv

func SetMaxTurnsEnv(n int)

SetMaxTurnsEnv exports the per-run tool-turn budget to the process environment so a spawned child (sub-agent / swarm member, which inherits the environment) runs with the SAME budget the user set via /turns. Without it a child re-resolves config.json's default and a large delegated task can exhaust its turns mid-run (exit 4 / max-turns). No-op for n <= 0.

func UserConfigDir added in v0.2.0

func UserConfigDir() (string, error)

UserConfigDir returns the base directory Zero stores per-user config under. It mirrors os.UserConfigDir everywhere except macOS: there Go defaults to ~/Library/Application Support, but Zero deliberately uses ~/.config (XDG-style, matching Linux and Claude Code) so a single config path works cross-platform. $XDG_CONFIG_HOME still wins on macOS when set.

func ValidateBytes

func ValidateBytes(data []byte) (FileConfig, []Issue)

ValidateBytes parses data as a Zero FileConfig and runs the same semantic provider/model rules as ValidateFile. It returns the parsed config (zero value on parse failure) plus any structured issues. A parse failure yields a single issue whose Message wraps the underlying JSON error (path-less form: "invalid config JSON: <err>") so callers can extract *json.SyntaxError / *json.UnmarshalTypeError offsets via errors.As.

func ValidateFile

func ValidateFile(path string) (FileConfig, []Issue)

ValidateFile reads and parses path as a Zero FileConfig and runs the same semantic provider/model rules used during resolution. It returns the parsed config (zero value on parse failure) plus any structured issues. A parse failure yields a single issue whose Message wraps the underlying JSON error so callers can extract *json.SyntaxError / *json.UnmarshalTypeError offsets via errors.As.

Types

type APIKeyGetter

type APIKeyGetter interface {
	Get(provider string) (string, bool, error)
}

APIKeyGetter is the read surface of the credential store; *credstore.Store satisfies it. Kept here so callers can inject a fake in tests.

type APIKeySetter

type APIKeySetter interface {
	Set(provider, key string) error
}

APIKeySetter is the write surface of the credential store.

type EnsuredProvider added in v0.3.0

type EnsuredProvider struct {
	Name    string
	Created bool
	Active  string
}

EnsuredProvider reports the outcome of EnsureCatalogProvider: the profile name that serves the catalog entry, whether it was newly created, and which provider is active after the call (unchanged unless it was blank).

func EnsureCatalogProvider added in v0.3.0

func EnsureCatalogProvider(path string, catalogID string) (EnsuredProvider, error)

EnsureCatalogProvider guarantees a provider profile exists in the config at path for the given catalog entry. OAuth login flows call this right after storing a token: a login is only reachable from the provider list and `zero providers use` when a profile exists, but a login must never replace or deactivate the user's current active provider — so an existing profile whose Name or CatalogID already matches is left completely untouched (its name, credentials, and model are the user's), and a created profile is NOT marked active unless no provider was active at all.

type FileConfig

type FileConfig struct {
	ActiveProvider string             `json:"activeProvider,omitempty"`
	Providers      []ProviderProfile  `json:"providers,omitempty"`
	MaxTurns       int                `json:"maxTurns,omitempty"`
	MCP            MCPConfig          `json:"mcp,omitempty"`
	Sandbox        SandboxConfig      `json:"sandbox,omitempty"`
	Notify         NotifyConfig       `json:"notify,omitempty"`
	Tools          ToolsConfig        `json:"tools,omitempty"`
	Swarm          SwarmConfig        `json:"swarm,omitempty"`
	Preferences    PreferencesConfig  `json:"preferences,omitempty"`
	KeyBindings    KeyBindingsConfig  `json:"keybindings,omitempty"`
	LocalControl   LocalControlConfig `json:"localControl,omitempty"`
	STT            STTConfig          `json:"stt,omitempty"`
}

func EditProvider added in v0.3.0

func EditProvider(path string, edit ProviderEdit) (FileConfig, error)

EditProvider applies a provider edit in ONE read-modify-write: rename (activeProvider follows; a stored key migrates, with a best-effort rollback if the config write fails), field updates, the stored-key marker, and the verbatim description. A single write keeps the operation atomic — the previous rename+upsert+describe sequence could fail halfway and leave config.json renamed while every in-memory consumer still held the old name — and, unlike UpsertProvider's exact-name merge, the case-insensitive match here makes a case-only rename (groq -> Groq) an in-place update instead of an appended duplicate profile.

func LoadProviderCommand

func LoadProviderCommand(command string) (FileConfig, error)

func RemoveProvider added in v0.3.0

func RemoveProvider(path string, name string) (FileConfig, error)

RemoveProvider deletes the named provider profile from the config at path. When the removed profile was active, activeProvider hands off to the first remaining provider (or clears when none remain) so the config never points at a profile that no longer exists. The caller owns cleaning up the credential store entry — config stays pure of secret I/O on the read path, and remove keeps that symmetry by only touching config.json.

func RenameProvider added in v0.3.0

func RenameProvider(path string, oldName string, newName string) (FileConfig, error)

RenameProvider renames a provider profile, keeping everything keyed by the profile name consistent: the activeProvider pointer follows the rename, and a key in the encrypted credential store (APIKeyStored) is migrated to the new name BEFORE the config is rewritten — the store write must succeed first so a failed migration never strands the config pointing at a key that no longer resolves. OAuth tokens are deliberately not migrated: the runtime's login candidates fall back to the profile's CatalogID, which every OAuth-capable catalog profile carries, so a rename keeps the login reachable.

func SetActiveProvider

func SetActiveProvider(path string, name string) (FileConfig, error)

func SetFavoriteModels

func SetFavoriteModels(path string, models []string) (FileConfig, error)

func SetProviderDescription added in v0.3.0

func SetProviderDescription(path string, name string, description string) (FileConfig, error)

SetProviderDescription sets a provider's description VERBATIM — including to empty. The generic UpsertProvider merge treats empty fields as "leave unchanged", so clearing a description needs this dedicated setter.

func SetProviderModel

func SetProviderModel(path string, name string, model string) (FileConfig, error)

func SetRecapsEnabled

func SetRecapsEnabled(path string, enabled bool) (FileConfig, error)

SetRecapsEnabled persists the post-turn recap preference, mirroring SetFavoriteModels (read-modify-atomic-write).

func SetRecentModels added in v0.3.0

func SetRecentModels(path string, entries []RecentModelEntry) (FileConfig, error)

SetRecentModels persists the automatic recent-model-switch history, mirroring SetFavoriteModels (read-modify-atomic-write). Unlike favorites, order is preserved (newest first) rather than sorted, since it reflects switch recency, not an alphabetical preference list.

func SetSTTLocalEngine added in v0.3.0

func SetSTTLocalEngine(path, binary, serverBinary, modelPath string, streaming bool) (FileConfig, error)

SetSTTLocalEngine persists the paths of an auto-downloaded local engine + model and switches dictation to the local provider, mirroring SetTheme (read-modify-atomic-write). streaming selects the pipeline matching the downloaded model (a streaming transducer vs a batch model). Called after a download completes.

func SetSTTModel added in v0.3.0

func SetSTTModel(path string, provider STTProviderKind, model string) (FileConfig, error)

SetSTTModel persists the dictation model and its provider, mirroring SetTheme (read-modify-atomic-write). provider must be one of the known STT provider kinds; a local provider stores the model as stt.localModelPath, otherwise as stt.model. A blank model clears the stored value for that slot.

func SetSTTProvider added in v0.3.0

func SetSTTProvider(path string, provider STTProviderKind) (FileConfig, error)

SetSTTProvider persists just the dictation batch provider, mirroring SetTheme.

func SetTheme

func SetTheme(path string, theme string) (FileConfig, error)

SetTheme persists the TUI theme preference, mirroring SetFavoriteModels (read-modify-atomic-write). A blank theme clears the stored preference.

func UpsertProvider

func UpsertProvider(path string, profile ProviderProfile, setActive bool) (FileConfig, error)

func (FileConfig) MarshalJSON

func (cfg FileConfig) MarshalJSON() ([]byte, error)

func (*FileConfig) UnmarshalJSON

func (cfg *FileConfig) UnmarshalJSON(data []byte) error

type Issue

type Issue struct {
	FieldPath string `json:"fieldPath,omitempty"`
	Message   string `json:"message"`
}

Issue is a single structured problem found while validating a config file. Message is already routed through the package secret redaction.

type KeyBindingDef added in v0.2.0

type KeyBindingDef string

KeyBindingDef defines one key binding string (e.g. "ctrl+o") that the TUI can remap. An empty string means "use the built-in default" for that action.

type KeyBindingsConfig added in v0.2.0

type KeyBindingsConfig struct {
	// ToggleDetailed toggles the detailed transcript view (default: ctrl+o).
	ToggleDetailed KeyBindingDef `json:"toggleDetailed,omitempty"`
	// ToggleMouse toggles mouse capture release (default: ctrl+e).
	ToggleMouse KeyBindingDef `json:"toggleMouse,omitempty"`
	// CycleReasoning cycles through reasoning effort levels (default: ctrl+t).
	CycleReasoning KeyBindingDef `json:"cycleReasoning,omitempty"`
	// TogglePlan toggles the plan panel expansion (default: ctrl+p).
	TogglePlan KeyBindingDef `json:"togglePlan,omitempty"`
	// ToggleSidebar toggles the right context sidebar (default: ctrl+b).
	ToggleSidebar KeyBindingDef `json:"toggleSidebar,omitempty"`
}

KeyBindingsConfig holds the subset of TUI keybindings that users may remap via config.json. Each field defaults to a sensible built-in when empty.

type LocalControlConfig

type LocalControlConfig struct {
	Enabled      bool                     `json:"enabled,omitempty"`
	Browser      LocalControlDriverConfig `json:"browser,omitempty"`
	Desktop      LocalControlDriverConfig `json:"desktop,omitempty"`
	Terminal     LocalControlDriverConfig `json:"terminal,omitempty"`
	ArtifactsDir string                   `json:"artifactsDir,omitempty"`
	// contains filtered or unexported fields
}

LocalControlConfig controls local browser/desktop/terminal automation helpers. Helpers are discovered lazily by the tool that needs them; no setup command or background probe is required during startup.

func (LocalControlConfig) BrowserEnabled

func (cfg LocalControlConfig) BrowserEnabled() bool

func (LocalControlConfig) DesktopEnabled

func (cfg LocalControlConfig) DesktopEnabled() bool

func (LocalControlConfig) Disabled

func (cfg LocalControlConfig) Disabled() bool

func (LocalControlConfig) Empty

func (cfg LocalControlConfig) Empty() bool

func (LocalControlConfig) MarshalJSON

func (cfg LocalControlConfig) MarshalJSON() ([]byte, error)

func (LocalControlConfig) TerminalEnabled

func (cfg LocalControlConfig) TerminalEnabled() bool

func (*LocalControlConfig) UnmarshalJSON

func (cfg *LocalControlConfig) UnmarshalJSON(data []byte) error

type LocalControlDriverConfig

type LocalControlDriverConfig struct {
	Enabled    bool   `json:"enabled,omitempty"`
	HelperPath string `json:"helperPath,omitempty"`
	Driver     string `json:"driver,omitempty"`
	// contains filtered or unexported fields
}

func (LocalControlDriverConfig) Empty

func (cfg LocalControlDriverConfig) Empty() bool

func (LocalControlDriverConfig) MarshalJSON

func (cfg LocalControlDriverConfig) MarshalJSON() ([]byte, error)

func (*LocalControlDriverConfig) UnmarshalJSON

func (cfg *LocalControlDriverConfig) UnmarshalJSON(data []byte) error

type MCPConfig

type MCPConfig struct {
	Servers map[string]MCPServerConfig `json:"servers,omitempty"`
}

func ResolveMCP

func ResolveMCP(options ResolveOptions) (MCPConfig, error)

type MCPOAuthConfig

type MCPOAuthConfig struct {
	ClientID              string   `json:"clientID,omitempty"`
	ClientSecret          string   `json:"clientSecret,omitempty"`
	Scopes                []string `json:"scopes,omitempty"`
	AuthorizationEndpoint string   `json:"authorizationEndpoint,omitempty"`
	TokenEndpoint         string   `json:"tokenEndpoint,omitempty"`
	RegistrationEndpoint  string   `json:"registrationEndpoint,omitempty"`
	IssuerURL             string   `json:"issuerURL,omitempty"`
}

MCPOAuthConfig describes how to authenticate to a remote MCP server using an OAuth 2.0 + PKCE authorization-code flow. Endpoints may be discovered from the authorization server's metadata document; explicit values here override or fill in anything discovery cannot provide. Client credentials are optional when the server supports dynamic client registration.

type MCPServerConfig

type MCPServerConfig struct {
	Type     string            `json:"type,omitempty"`
	Command  string            `json:"command,omitempty"`
	Args     []string          `json:"args,omitempty"`
	Env      map[string]string `json:"env,omitempty"`
	URL      string            `json:"url,omitempty"`
	Headers  map[string]string `json:"headers,omitempty"`
	Auth     string            `json:"auth,omitempty"`
	OAuth    *MCPOAuthConfig   `json:"oauth,omitempty"`
	Disabled bool              `json:"disabled,omitempty"`
	// contains filtered or unexported fields
}

func (*MCPServerConfig) UnmarshalJSON

func (server *MCPServerConfig) UnmarshalJSON(data []byte) error

type NotifyConfig

type NotifyConfig struct {
	Mode      string `json:"mode,omitempty"`
	FocusMode string `json:"focusMode,omitempty"`
}

type Overrides

type Overrides struct {
	ActiveProvider string
	Providers      []ProviderProfile
	Provider       ProviderProfile
	MaxTurns       int
	MCP            MCPConfig
	Sandbox        SandboxConfig
	Notify         NotifyConfig
	Tools          ToolsConfig
	KeyBindings    KeyBindingsConfig
	LocalControl   LocalControlConfig
	STT            STTConfig
}

type PreferencesConfig

type PreferencesConfig struct {
	FavoriteModels []string `json:"favoriteModels,omitempty"`
	// RecentModels is the short automatic history of provider+model pairs the
	// user has switched to via /model, newest first. Unlike FavoriteModels
	// (manual pins), this list is maintained automatically on every switch and
	// capped to MaxRecentModels entries. See RecentModelEntry.
	RecentModels []RecentModelEntry `json:"recentModels,omitempty"`
	// Theme is the persisted TUI palette preference — "auto" or a registered theme
	// name (e.g. "dracula"). Applied at startup below the --theme flag and
	// ZERO_THEME, so a /theme choice survives restart. Empty = unset (defaults auto).
	Theme string `json:"theme,omitempty"`
	// Recaps is a tri-state: nil (unset) defaults to ON; an explicit false means
	// the user turned post-turn recaps off. A *bool is its own tri-state, so no
	// custom unmarshal is needed (unlike ToolsConfig.DeferThreshold's int).
	Recaps *bool `json:"recaps,omitempty"`
}

func (PreferencesConfig) RecapsEnabled

func (p PreferencesConfig) RecapsEnabled() bool

RecapsEnabled reports whether post-turn recaps are on. Unset defaults to ON.

type ProviderEdit added in v0.3.0

type ProviderEdit struct {
	Name         string
	NewName      string
	BaseURL      string
	Model        string
	APIKey       string
	APIKeyStored bool
	Description  string
}

ProviderEdit is a field-level edit of one saved provider, applied by EditProvider in a single atomic write. Name is the CURRENT profile name (matched case-insensitively); NewName renames (case-only renames included). Empty BaseURL/Model/APIKey mean "leave unchanged"; Description is applied VERBATIM (the editor always knows the full desired text, so clearing works).

type ProviderKind

type ProviderKind string
const (
	ProviderKindOpenAI           ProviderKind = "openai"
	ProviderKindAnthropic        ProviderKind = "anthropic"
	ProviderKindAnthropicCompat  ProviderKind = "anthropic-compatible"
	ProviderKindGoogle           ProviderKind = "google"
	ProviderKindOpenAICompatible ProviderKind = "openai-compatible"
)

type ProviderProfile

type ProviderProfile struct {
	Name         string       `json:"name"`
	Provider     string       `json:"provider,omitempty"`
	ProviderKind ProviderKind `json:"provider_kind,omitempty"`
	CatalogID    string       `json:"catalogID,omitempty"`
	BaseURL      string       `json:"baseURL,omitempty"`
	APIKey       string       `json:"apiKey,omitempty"`
	APIKeyEnv    string       `json:"apiKeyEnv,omitempty"`
	// APIKeyStored marks that this provider's API key lives in the encrypted
	// credential store (internal/credstore), not inline in APIKey. The effective
	// key is loaded from the store at provider-build time; config.json holds only
	// this marker, never the secret.
	APIKeyStored    bool              `json:"apiKeyStored,omitempty"`
	APIFormat       string            `json:"apiFormat,omitempty"`
	AuthHeader      string            `json:"authHeader,omitempty"`
	AuthScheme      string            `json:"authScheme,omitempty"`
	AuthHeaderValue string            `json:"authHeaderValue,omitempty"`
	CustomHeaders   map[string]string `json:"customHeaders,omitempty"`
	Model           string            `json:"model,omitempty"`
	ParseThinkTags  *bool             `json:"parseThinkTags,omitempty"`
	Description     string            `json:"description,omitempty"`
}

func ApplyStoredAPIKey

func ApplyStoredAPIKey(profile ProviderProfile, store APIKeyGetter) ProviderProfile

ApplyStoredAPIKey fills profile.APIKey from the credential store when it is not already resolved — an inline config key or a resolved APIKeyEnv always wins, so this only supplies a key for providers whose secret lives in the store. A nil store, a miss, or a store error leaves the profile unchanged.

func SecureProviderProfile

func SecureProviderProfile(profile ProviderProfile, configPath string) ProviderProfile

SecureProviderProfile moves an inline APIKey on the profile into the credential store co-located with configPath, returning a profile with APIKeyStored set and APIKey cleared so the secret is never written to config.json. On any store error (e.g. no keyring) it returns the profile unchanged — the inline key persists and the startup migration retries later — so capturing a provider never fails on a flaky credential backend.

func (ProviderProfile) HasConfiguredCredential

func (profile ProviderProfile) HasConfiguredCredential() bool

HasConfiguredCredential reports whether the profile is set up to authenticate with its own API key (inline, stored, or a raw auth header). It is the single definition of "this profile is key-authed", shared by OAuthLoginCandidates and the cli/tui setup surfaces so they never disagree about whether a profile is keyed. APIKeyEnv is deliberately NOT included: an env var may be unset at runtime while the profile actually relies on an OAuth login, so an env-configured-but-empty profile must still be allowed to resolve a token.

func (ProviderProfile) MissingCredentialEnv added in v0.3.0

func (profile ProviderProfile) MissingCredentialEnv() (string, bool)

MissingCredentialEnv reports the environment variable this profile is expected to read an API key from, and whether that expectation is currently unmet. It is the single source of truth for "does this saved provider need a credential it doesn't have" — the CLI (`providers check`, `usableSavedProviders`) and TUI (`/providers` status, provider wizard summary) surfaces all delegate here so they never disagree about a profile's status.

For the catalog's two "bring your own endpoint" descriptors (custom-openai-compatible, custom-anthropic-compatible), RequiresAuth is just a wizard template default, not a fact about the user's actual endpoint: a custom target may need no auth at all. So for those two only, a credential is expected exclusively when this profile carries its own explicit APIKeyEnv that differs from the catalog's guessed default — a value matching that default is indistinguishable from the wizard's old auto-stamp bug (issue #555) and is treated the same as unset, which also self-heals profiles saved before that bug was fixed.

func (ProviderProfile) OAuthLoginCandidates

func (profile ProviderProfile) OAuthLoginCandidates() []string

OAuthLoginCandidates returns the login names to try, in order, when resolving this profile's OAuth token — used by the runtime bearer resolver, the Codex account-header resolver, and the onboarding presence check so all three agree.

It returns NO candidates for a profile that carries its own configured credential: attaching an OAuth resolver to such a profile makes withBearer erase the key the instant a token resolves, silently routing/billing the call through the OAuth account instead of the intended key (the cross-profile override the review caught — and, via the profile name, a same-name login overriding an explicit key). A stored-key profile that reaches this keyless (e.g. a transiently unreadable keyring) is likewise treated as key-auth, so it fails with a clear missing-credential error rather than silently borrowing an unrelated OAuth login. APIKeyEnv is intentionally not part of that gate (an env var may be unset while the profile relies on OAuth).

For a keyless profile the profile name comes first (a login under your own profile name is unambiguously yours), then the catalog ID as a FALLBACK (so a profile renamed away from its catalog ID — e.g. {name:"codex", catalogID:"chatgpt"} — still finds a `zero auth login chatgpt` token). Candidates are trimmed, blank-skipped, and de-duplicated CASE-SENSITIVELY — the OAuth token store is a case-sensitive map, so "ChatGPT" and "chatgpt" are distinct keys and both must survive.

func (*ProviderProfile) UnmarshalJSON

func (profile *ProviderProfile) UnmarshalJSON(data []byte) error

type RecentModelEntry added in v0.3.0

type RecentModelEntry struct {
	Provider string `json:"provider"`
	Model    string `json:"model"`
}

RecentModelEntry is one provider-qualified model selection recorded in Preferences.RecentModels. Provider is the saved provider profile's Name (not a display label), so a recent entry can be resolved back to a concrete profile the same way the /model picker's cross-provider rows are.

func NormalizeRecentModels added in v0.3.0

func NormalizeRecentModels(entries []RecentModelEntry) []RecentModelEntry

NormalizeRecentModels trims, drops entries with no model id, de-duplicates by provider+model pair (keeping the first/newest occurrence), and caps the result to MaxRecentModels. Order is preserved — the caller is responsible for passing entries newest-first. Exported so callers outside this package (e.g. the TUI, which keeps its own in-memory copy of recent history) apply the exact same normalization rules as the persisted config, instead of maintaining a second, independently-drifting copy of this logic.

type ResolveOptions

type ResolveOptions struct {
	UserConfigPath    string
	ProjectConfigPath string
	ProviderCommand   string
	Env               map[string]string
	Overrides         Overrides
	// ExcludeProject drops the project config layer (ProjectConfigPath) from MCP
	// resolution when the workspace is untrusted, so a cloned repo's ./.zero/config.json
	// cannot spawn stdio MCP servers. It is fail-closed: only a trusted workspace sets
	// it false. Mirrors the ExcludeProject option hooks and plugins already honor.
	ExcludeProject bool
}

func DefaultResolveOptions

func DefaultResolveOptions(workspaceRoot string) (ResolveOptions, error)

DefaultResolveOptions builds config resolution inputs from the local process environment and workspace.

type ResolvedConfig

type ResolvedConfig struct {
	ActiveProvider string
	Providers      []ProviderProfile
	Provider       ProviderProfile
	MaxTurns       int
	MCP            MCPConfig
	Sandbox        SandboxConfig
	Notify         NotifyConfig
	Tools          ToolsConfig
	Swarm          SwarmConfig
	Preferences    PreferencesConfig
	KeyBindings    KeyBindingsConfig
	LocalControl   LocalControlConfig
	STT            STTConfig
}

func Resolve

func Resolve(options ResolveOptions) (ResolvedConfig, error)

type STTConfig added in v0.3.0

type STTConfig struct {
	// Provider is the batch transcription backend: "local" (sherpa-onnx-offline,
	// the default), "groq", or "openai". Termux and the fallback everywhere use
	// this path.
	Provider STTProviderKind `json:"provider,omitempty"`
	// StreamProvider is the streaming backend on desktop: "local" (sherpa-onnx
	// websocket server, default), "deepgram", or "openai" (Realtime).
	StreamProvider STTProviderKind `json:"streamProvider,omitempty"`
	// Streaming enables the live-transcript pipeline on platforms that support
	// it (desktop). Defaults to on; set false to always use the batch pipeline.
	Streaming *bool `json:"streaming,omitempty"`
	// Model overrides the cloud batch model (e.g. "whisper-large-v3-turbo" for
	// Groq, "whisper-1" for OpenAI). Empty uses the provider default.
	Model string `json:"model,omitempty"`
	// StreamModel overrides the cloud streaming model (Deepgram/OpenAI Realtime).
	StreamModel string `json:"streamModel,omitempty"`
	// LocalModelPath is the sherpa-onnx model directory for local transcription
	// (batch and streaming). Required to use a local provider.
	LocalModelPath string `json:"localModelPath,omitempty"`
	// LocalBinary overrides the offline binary name/path (default
	// "sherpa-onnx-offline"); LocalServerBinary overrides the streaming server
	// (default "sherpa-onnx-online-websocket-server"). Both looked up on PATH.
	LocalBinary       string `json:"localBinary,omitempty"`
	LocalServerBinary string `json:"localServerBinary,omitempty"`
	// LocalServerPort is the localhost port for the sherpa-onnx websocket server
	// (default 6006).
	LocalServerPort int `json:"localServerPort,omitempty"`
	// EngineVersion selects the sherpa-onnx release the auto-download fetches
	// ("" → a pinned known-good default; "latest" or any release tag also works,
	// so a newer engine needs no Zero update). Verified against the release's
	// published SHA256 digest either way.
	EngineVersion string `json:"engineVersion,omitempty"`
	// NumThreads sets the local engine's thread count (0 = engine default).
	NumThreads int `json:"numThreads,omitempty"`
	// Language optionally constrains recognition (ISO-639-1, e.g. "en"). Empty =
	// auto-detect.
	Language string `json:"language,omitempty"`
	// MaxDurationSeconds is the hard runaway-recording cap on every platform
	// (0 = default 300s).
	MaxDurationSeconds int `json:"maxDurationSeconds,omitempty"`
	// SilenceAutoStop ends a recording ~2s after the signal goes quiet, as a
	// backstop for a forgotten manual stop (not VAD-triggered start). Defaults on.
	SilenceAutoStop *bool `json:"silenceAutoStop,omitempty"`
	// AutoSubmit fires the transcript at the agent instead of inserting it for
	// review. Defaults OFF — insert-for-review is the safety net for a misheard
	// prompt (§3).
	AutoSubmit *bool `json:"autoSubmit,omitempty"`
	// WindowsAudioDevice names the dshow capture device (auto-detected when empty).
	WindowsAudioDevice string `json:"windowsAudioDevice,omitempty"`
}

STTConfig configures speech-to-text dictation. All fields are optional; empty values take the documented defaults. Booleans that need a real tri-state (distinguishing "unset" from "false") use *bool, matching PreferencesConfig.Recaps.

func (STTConfig) AutoSubmitEnabled added in v0.3.0

func (c STTConfig) AutoSubmitEnabled() bool

AutoSubmitEnabled reports whether transcripts are auto-fired. Unset defaults OFF.

func (STTConfig) Empty added in v0.3.0

func (c STTConfig) Empty() bool

Empty reports whether the STT config carries no user-set values (so it can be omitted from a marshaled config).

func (STTConfig) STTProvider added in v0.3.0

func (c STTConfig) STTProvider() STTProviderKind

STTProvider returns the configured batch provider, defaulting to local.

func (STTConfig) STTStreamProvider added in v0.3.0

func (c STTConfig) STTStreamProvider() STTProviderKind

STTStreamProvider returns the configured streaming provider, defaulting to local.

func (STTConfig) SilenceAutoStopEnabled added in v0.3.0

func (c STTConfig) SilenceAutoStopEnabled() bool

SilenceAutoStopEnabled reports whether trailing-silence auto-stop is on. Unset defaults ON.

func (STTConfig) StreamingEnabled added in v0.3.0

func (c STTConfig) StreamingEnabled() bool

StreamingEnabled reports whether the live pipeline is on. Unset defaults ON.

type STTProviderKind added in v0.3.0

type STTProviderKind string

STTProviderKind is the batch (or streaming) transcription backend selector. Validated at config load time against the known set, so a typo fails loudly with the valid options rather than silently falling back to a default.

const (
	STTProviderLocal    STTProviderKind = "local"
	STTProviderGroq     STTProviderKind = "groq"
	STTProviderOpenAI   STTProviderKind = "openai"
	STTProviderDeepgram STTProviderKind = "deepgram"
)

type SandboxConfig

type SandboxConfig struct {
	// Network controls whether shell commands classified as network-touching
	// (curl, git push, package installs, …) are allowed: "allow" or "deny".
	// Empty keeps the built-in default (deny). Without this knob the engine's
	// hard-coded NetworkDeny was unreachable from any config surface.
	Network string `json:"network,omitempty"`
	// AdditionalWriteRoots lists directories outside the workspace the sandbox
	// allows writes in. Each entry must be an existing directory; entries are
	// normalized (~-expanded, absolutized, symlink-resolved) at startup and an
	// invalid entry fails the run. Honored from the GLOBAL user config and CLI
	// flags only — deliberately not project config, so a cloned repo cannot
	// grant itself write access. Session-only grants use /add-dir instead.
	AdditionalWriteRoots []string `json:"additionalWriteRoots,omitempty"`
	// BlockUnixSockets, when true, asks the Linux sandbox helper to install a
	// best-effort seccomp filter that denies AF_UNIX socket creation inside the
	// sandboxed command. Off by default; ignored on non-Linux backends.
	BlockUnixSockets bool `json:"blockUnixSockets,omitempty"`
	// MonitorDenials, when true on macOS, tails the unified log for this run's
	// sandbox denials and appends them to a command's stderr so blocked operations
	// are visible to the agent. Off by default. No-op on platforms/OS versions that
	// do not deliver seatbelt denials to the queryable log.
	MonitorDenials bool `json:"monitorDenials,omitempty"`
}

type SwarmConfig

type SwarmConfig struct {
	MaxTeamSize int `json:"maxTeamSize,omitempty"`
}

SwarmConfig tunes the multi-agent swarm. MaxTeamSize caps how many members run concurrently per team; 0 uses the built-in default (8). Spawns past the cap queue and launch as slots free, so lowering it bounds parallelism (and provider load / rate-limit pressure) without dropping work.

type ToolsConfig

type ToolsConfig struct {
	DeferThreshold int `json:"deferThreshold,omitempty"`
	// contains filtered or unexported fields
}

func ToolsOverride

func ToolsOverride(deferThreshold int) ToolsConfig

ToolsOverride builds a ToolsConfig that explicitly overrides the deferred-tool threshold (including to 0, which disables deferral). Use this for programmatic Overrides — a bare ToolsConfig{DeferThreshold: 0} is indistinguishable from "unset" and will not override.

func (*ToolsConfig) UnmarshalJSON

func (cfg *ToolsConfig) UnmarshalJSON(data []byte) error

Jump to

Keyboard shortcuts

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