config

package
v1.11.0 Latest Latest
Warning

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

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

Documentation

Overview

Package config carries the runtime configuration the evva agent and its bundled tools read at startup and during a session.

The package is brand-neutral: a Config is constructed via Load(appName, appHome, workdir) so downstream apps can choose their own home directory (e.g. ~/.myapp/) and binary name. LoadDefault preserves evva's historical behavior (~/.evva/) for the bundled CLI.

There is no package-level singleton. Callers construct one Config per process (or per agent, if running multiple agents with different configurations) and pass it through agent.New via WithConfig.

Index

Examples

Constants

View Source
const (
	DefaultAppName    = "evva"
	DefaultAppVersion = version.Version
)

Default values that appear unchanged across all Config instances.

Variables

View Source
var BuildDate string

BuildDate is the UTC build timestamp injected at build time. Empty in dev builds.

View Source
var CommitSHA string

CommitSHA is the git commit hash injected at build time via ldflags. Empty in dev builds.

View Source
var Version string

Version is the canonical version string injected at build time via ldflags:

go build -ldflags "-X github.com/johnny1110/evva/pkg/config.Version=v1.2.3"

When empty (dev builds, go run), DefaultAppVersion is used as the fallback.

Functions

func DisplayVersion

func DisplayVersion() string

DisplayVersion returns the best available version string: the ldflags-injected Version, or DefaultAppVersion if not set, followed by the commit and build date when available. The result is meant for --version output.

func ResolveDefaultModel

func ResolveDefaultModel(provider, model string) (constant.LLMProvider, constant.Model, error)

ResolveDefaultModel parses the (provider name, model name) pair from the YAML and returns the typed constants. Validates that the model is actually one the provider lists — a typo or a model/provider mismatch fails fast at startup with a clear message rather than a confusing runtime "unknown model" from the LLM API.

func SaveFileConfig

func SaveFileConfig(path string, cfg FileConfig) error

SaveFileConfig writes cfg to path atomically (temp file + rename) so a crash mid-write never leaves a truncated YAML behind.

Types

type APIConfig

type APIConfig struct {
	ApiURL    string
	ApiSecret string
	Models    []constant.Model
}

APIConfig carries the per-provider credentials an LLM client needs to talk to its backend. The host (cmd/evva or a downstream consumer) constructs one APIConfig per provider from whatever config source it uses (YAML, env vars, secret manager) and passes it to the registry-resolved ClientFactory in pkg/llm.

Defined in pkg/config rather than pkg/llm to avoid a cycle: pkg/llm imports pkg/tools, pkg/tools imports pkg/config (for the State.Config() return type). pkg/config sits at the bottom and is imported by both pkg/llm (via alias) and pkg/tools.

type Config

type Config struct {
	// OS / runtime
	OS string

	// Logging
	LogLevel  string  // default: "info"
	LogFormat string  // default: "text"
	LogDir    *string // default: nil → stdout only

	// Application
	AppEnv     string // default: "development"
	AppName    string // default: "evva" — the binary / brand name; drives AppHome layout.
	AppVersion string

	// Per-user home dir layout
	AppHome            string
	AppHomeSkillsDir   string
	AppHomeUserProfile string
	AppHomeConfigFile  string // absolute path to <app>-config.yml under AppHome/config/

	AutoCompactThreshold float64

	// Workdir layout
	WorkDir          string
	WorkDirSkillsDir string

	// llm providers(from <app>-config.yml) key: provider name, value: provider APIConfig
	LLMProviderConfig map[string]APIConfig

	// DefaultProvider / DefaultModel are the (provider, model) the agent
	// boots with. Sourced from <app>-config.yml; the /model switch updates
	// them in-memory and persists via SaveFile().
	DefaultProvider constant.LLMProvider
	DefaultModel    constant.Model

	// DefaultEffort is the user-facing effort level name: low|medium|high|ultra.
	// Defaults to "medium". Sourced from <app>-config.yml; /effort updates it.
	DefaultEffort string

	// DefaultProfile is the persona the root agent boots into ("evva", "nono",
	// etc). Sourced from <app>-config.yml; /profile updates it. Empty falls
	// back to "evva" at bootstrap so old configs keep working.
	DefaultProfile string

	// PermissionMode is the startup permission stance: one of
	// default|accept_edits|plan|bypass|auto. The -permission-mode CLI flag
	// overrides this at boot; the TUI's Shift+Tab cycle mutates the
	// in-memory value via SetPermissionMode (not yet persisted).
	PermissionMode string

	// Loaded metadata
	LoadedAt time.Time
	// DefaultMaxIterations is the loop's safety cap. Hitting it emits a
	// KindIterLimit event and pauses the agent; the caller may invoke
	// Continue(ctx) to keep going.
	DefaultMaxIterations int
	// DefaultMaxTokens is the per-completion output-token cap passed to
	// the LLM. 0 → let the provider apply its own default.
	DefaultMaxTokens int

	// UI
	DisplayThinking bool

	// Auto-memory subsystem. When true (default), the system prompt carries the
	// typed-memory guidance + the MEMORY.md index, the permission write carve-out
	// is active, and per-turn recall runs. The model writes memory files itself
	// with write/edit (no dedicated tool). /config (or hand-edit) flips this;
	// EVVA_AUTO_MEMORY=0 forces off at boot regardless of the YAML.
	EnableAutoMemory bool

	// EnableMemoryRecall gates the per-turn relevance side-query (only meaningful
	// when EnableAutoMemory is true). Default true; the cost-sensitive escape
	// hatch keeps the index but drops the extra completion per turn.
	EnableMemoryRecall bool

	// MemoryRecallModel optionally pins the recall side-query model id. Empty →
	// a cheap model within the active provider (anthropic: sonnet, deepseek:
	// flash, openai: gpt-5.4-mini, glm: glm-4.6 at medium effort; ollama/other:
	// the active model + the main agent's effort). See internal/agent recall wiring.
	MemoryRecallModel string

	// Auto-dream: background memory consolidation. When EnableAutoDream is true
	// (and EnableAutoMemory is true), the main agent — on going idle — may fork a
	// fenced background agent that consolidates the global memory store (merge
	// duplicates, prune stale entries, rebuild the MEMORY.md index). Gated so it
	// fires at most ~once per AutoDreamMinHours after AutoDreamMinSessions new
	// sessions. Off by default — a dream is a real (if rare) token cost. See the
	// internal/agent dream wiring + docs/roadmap/PRD/auto-dream.md.
	EnableAutoDream bool
	// AutoDreamMinHours is the minimum hours since the last consolidation before
	// dream may fire again (default 24).
	AutoDreamMinHours int
	// AutoDreamMinSessions is the minimum number of sessions touched since the
	// last consolidation before dream may fire (default 5).
	AutoDreamMinSessions int
	// AutoDreamModel optionally pins the dream agent's model id. Empty → the same
	// cheap per-provider default the recall side-query uses (see MemoryRecallModel).
	AutoDreamModel string

	// Checkpoint & rewind. When EnableCheckpoints is true (opt-in; off by default), the main
	// agent records a checkpoint at each user-turn boundary and captures the
	// before-image of every file its fs tools touch, so /rewind can restore the
	// code, the conversation, or both. CheckpointMaxPerSession caps how many
	// checkpoints one session retains (oldest pruned first; 0 = unlimited).
	// Storage lives under <workdir>/.evva/checkpoints — a .gitignore candidate.
	// See docs/roadmap/PRD/checkpoint-rewind.md.
	EnableCheckpoints       bool
	CheckpointMaxPerSession int

	// Repo map. When EnableRepoMap is true (opt-in; off by default), the main
	// agent's session-open prompt carries a compact, ranked, token-bounded
	// overview of the codebase's symbols, built from the LSP layer (or a glob
	// fallback when no language server is configured). RepoMapTokenBudget bounds
	// the map's size; the map is dropped lower-ranked-first to fit. Main agent
	// only. See docs/roadmap/PRD/lsp-repo-map.md.
	EnableRepoMap      bool
	RepoMapTokenBudget int

	// LSPDiagnosticsOnEdit gates the synchronous self-healing-edit tier. When
	// true (opt-in; off by default), the edit/write tools wait a short bounded
	// window (~750ms) after a mutation for LSP diagnostics on that file and
	// fold them into the tool's own result, so the model sees its own
	// compile/type error on the same turn it introduced it. The core tier
	// (didChange sync so the between-turns drain has real content to deliver)
	// runs regardless of this flag whenever an LSP manager is configured.
	// See docs/roadmap/PRD/edit-diagnostics-sync.md.
	LSPDiagnosticsOnEdit bool

	// Solo dynamic workflow. When EnableDynamicWorkflow is true (opt-in; off
	// by default), the main agent mounts the wf_task_* graph-board tools
	// (replacing todo_write) and an in-process engine auto-dispatches
	// ephemeral subagent workers as task dependencies complete.
	// WorkflowMaxWorkers bounds concurrent engine-dispatched workers
	// (default 4; floor 1). Solo main agent only — swarm-resident personas
	// never mount the board. See docs/roadmap/PRD/solo-dynamic-workflow.md.
	EnableDynamicWorkflow bool
	WorkflowMaxWorkers    int

	// OutputStyle names the active output style — a prompt overlay that
	// changes how the main persona talks (built-in "Explanatory"/"Learning"
	// or a custom output-styles/*.md). Empty means the default style (no
	// overlay). Resolution + fallback live in internal/outputstyle; this is
	// just the persisted name. /output-style updates it and rebuilds the
	// profile; /config edits take effect at the next profile build.
	OutputStyle string

	// Web tools
	TavilyAPIKey  string // empty → web_search reports "not configured"
	FetchMaxBytes int    // cap on extracted text returned by web_fetch

	// CustomConfig holds downstream-app-defined settings. Reads/writes go
	// through GetCustom / SetCustom / DeleteCustom under c.mu. Values
	// round-trip through YAML as a `custom:` section; complex types are
	// encoded as whatever gopkg.in/yaml.v3 produces for any (typically a
	// map[string]any tree). Consumers cast at use-site — pkg/config does
	// not know the value shapes.
	//
	// Use this slot for downstream-private secrets/settings that don't fit
	// the typed fields above (e.g. friday's broker URL, a billing token,
	// feature flags). evva itself never reads from CustomConfig.
	CustomConfig map[string]any

	// LLMParamsTemperature / LLMParamsTopP / LLMParamsTopK are session-only
	// sampling knobs the /config form mutates at runtime. nil → provider
	// default (not sent in API request). Reset to nil on every evva start;
	// never persisted to YAML.
	LLMParamsTemperature *float64
	LLMParamsTopP        *float64
	LLMParamsTopK        *int
	// contains filtered or unexported fields
}

Config holds all parsed runtime configuration. Most fields are populated once during Load and treated as read-only; the small subset that the /config and /model setters mutate at runtime is guarded by c.mu.

AppHome-prefixed paths point inside the per-user home dir (~/.<app>/) where skills, USER_PROFILE.md, evva-config.yml, and logs live. WorkDir-prefixed paths point inside the process's current working directory where workdir-local resources (skills, EVVA.md, plans) live.

func Get

func Get() *Config

Get returns a process-wide Config initialized lazily via LoadDefault. Subsequent calls return the same pointer.

Get is a host-side convenience for the bundled cmd/evva binary and for the reference TUI (which reads runtime settings without an injected pointer). Library code inside the agent loop and tools must NOT call Get — they receive *Config through dependency injection (agent.WithConfig, toolset.ToolState.SetConfig, function parameters).

Downstream hosts that want a non-default AppHome should call Load with explicit LoadOptions and pass the result into agent.WithConfig.

func Load

func Load(opts LoadOptions) (*Config, error)

Load parses env vars + the per-user YAML and returns a populated Config. Each LoadOptions field has a sensible default (see LoadOptions doc).

Unlike LoadDefault, Load returns an error instead of calling os.Exit so downstream hosts can surface it through their own error path.

Example

ExampleLoad demonstrates the canonical downstream-app load: pick an AppName + AppHome, let Load auto-create the YAML on first run, and trust the returned *Config from then on.

AppName drives the AppHome layout (~/.{AppName}/) AND the first-run YAML's `default_profile` value — running this with AppName="friday" stamps `default_profile: friday`, not "evva" (Phase 19b).

package main

import (
	"fmt"
	"path/filepath"
	"strings"

	"github.com/johnny1110/evva/pkg/config"
)

func main() {
	tmp, _ := filepath.Abs("/tmp/evva-example-load")

	cfg, err := config.Load(config.LoadOptions{
		AppName: "friday",
		AppHome: tmp,
		WorkDir: tmp + "/work",
	})
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	fmt.Println("app:", cfg.AppName)
	fmt.Println("default_profile:", cfg.DefaultProfile)
	fmt.Println("config_file_endswith:", strings.HasSuffix(cfg.AppHomeConfigFile, "friday-config.yml"))
}
Output:
app: friday
default_profile: friday
config_file_endswith: true

func LoadDefault

func LoadDefault() *Config

LoadDefault returns a Config populated with evva's historical defaults: AppName="evva", AppHome=~/.evva/, WorkDir=os.Getwd(). Intended for the bundled cmd/evva binary and for backward-compatible callers.

Startup failures (missing/invalid YAML, unknown provider/model) bail with os.Exit so the user gets a clear single-line error rather than a panic stack from deep inside the agent boot path.

func (*Config) Clone added in v1.4.3

func (c *Config) Clone() *Config

Clone returns a shallow copy of c with fresh mutexes. Used by callers that need to override a small subset of fields (notably WorkDir) for a scoped agent — the AgentTool isolation path does this so a subagent can run with cfg.WorkDir = <worktree path> while the parent keeps its own. The copy reads through c.mu so concurrent mutations don't tear across fields.

"Shallow" — the LLMProviderConfig map is reused by reference. That's safe today because providers are loaded once at boot and never mutated after; if that invariant ever changes, this method should deep-copy the map.

func (*Config) DeleteCustom added in v1.4.3

func (c *Config) DeleteCustom(key string) error

DeleteCustom removes key from CustomConfig and persists. A missing key is a no-op (no error). Persists via SaveFile so the YAML reflects the removal immediately.

func (*Config) Effort

func (c *Config) Effort() string

Effort returns the current effort level name under the read lock.

func (*Config) GetAutoCompactThreshold

func (c *Config) GetAutoCompactThreshold() float64

GetAutoCompactThreshold returns the current threshold under the read lock. compact.go reads this every turn.

func (*Config) GetAutoDreamMinHours added in v1.8.0

func (c *Config) GetAutoDreamMinHours() int

GetAutoDreamMinHours returns the consolidation time-gate (hours) under the read lock. Always ≥1 (load normalizes a missing/≤0 value to the default 24).

func (*Config) GetAutoDreamMinSessions added in v1.8.0

func (c *Config) GetAutoDreamMinSessions() int

GetAutoDreamMinSessions returns the consolidation activity-gate (session count) under the read lock. Always ≥1 (load normalizes ≤0 to the default 5).

func (*Config) GetAutoDreamModel added in v1.8.0

func (c *Config) GetAutoDreamModel() string

GetAutoDreamModel returns the pinned dream-model id ("" → default resolution) under the read lock.

func (*Config) GetCheckpointMaxPerSession added in v1.8.2

func (c *Config) GetCheckpointMaxPerSession() int

GetCheckpointMaxPerSession returns the per-session checkpoint retention cap under the read lock (0 = unlimited).

func (*Config) GetCustom added in v1.4.3

func (c *Config) GetCustom(key string) (any, bool)

GetCustom returns the value stored under key in CustomConfig. ok=false when the key is absent. Reads under c.mu.RLock.

Values are stored as `any` — cast at use-site. After a YAML reload the concrete type is whatever yaml.v3 decoded into (typically string, int, float64, bool, []any, or map[string]any). Round-tripping through SaveFile is lossy for typed Go structs unless the caller (or yaml tags on a value-type) preserves the shape.

func (*Config) GetDefaultProfile added in v1.4.3

func (c *Config) GetDefaultProfile() string

GetDefaultProfile returns the boot persona name under the read lock. Empty falls back to "evva" at bootstrap. Paired with SetDefaultProfile.

func (*Config) GetDisplayThinking

func (c *Config) GetDisplayThinking() bool

GetDisplayThinking returns the current DisplayThinking flag under the read lock. Agent code reads this every turn (state_machine.go, stream.go); the UI may write it via /config.

func (*Config) GetEnableAutoDream added in v1.8.0

func (c *Config) GetEnableAutoDream() bool

GetEnableAutoDream returns the background-consolidation master switch under the read lock. Read at main-agent idle to decide whether to run the gate.

func (*Config) GetEnableAutoMemory added in v1.4.3

func (c *Config) GetEnableAutoMemory() bool

GetEnableAutoMemory returns the auto-memory flag under the read lock. Read by agent.Main (to decide whether to attach the memory tools) and by the sysprompt builder (to decide whether to inject the auto-memory guidance section).

func (*Config) GetEnableCheckpoints added in v1.8.2

func (c *Config) GetEnableCheckpoints() bool

GetEnableCheckpoints returns the checkpoint/rewind flag under the read lock. Read by agent.New to decide whether to construct the checkpoint manager and wire the fs-tool capture sink.

func (*Config) GetEnableDynamicWorkflow added in v1.11.0

func (c *Config) GetEnableDynamicWorkflow() bool

GetEnableDynamicWorkflow returns the solo dynamic-workflow flag under the read lock. Read at profile build to decide whether the main agent mounts the wf_task_* board (and drops todo_write).

func (*Config) GetEnableMemoryRecall added in v1.4.3

func (c *Config) GetEnableMemoryRecall() bool

GetEnableMemoryRecall returns the per-turn recall flag under the read lock. Read by the agent loop each user turn to decide whether to run the relevance side-query.

func (*Config) GetEnableRepoMap added in v1.8.2

func (c *Config) GetEnableRepoMap() bool

GetEnableRepoMap returns the repo-map flag under the read lock. Read by agent.New to decide whether to build and inject the session-open repo map.

func (*Config) GetFetchMaxBytes added in v1.4.3

func (c *Config) GetFetchMaxBytes() int

GetFetchMaxBytes returns the web_fetch byte cap under the read lock. Paired with SetFetchMaxBytes.

func (*Config) GetLSPDiagnosticsOnEdit added in v1.11.0

func (c *Config) GetLSPDiagnosticsOnEdit() bool

GetLSPDiagnosticsOnEdit returns the synchronous self-healing-edit flag under the read lock. Read by the LSP-sync adapter on every edit/write, not cached, so toggling it via /config takes effect on the very next mutation.

func (*Config) GetMaxIterations added in v1.4.3

func (c *Config) GetMaxIterations() int

GetMaxIterations returns the agent-loop iteration cap under the read lock. Paired with SetMaxIterations.

func (*Config) GetMaxTokens added in v1.4.3

func (c *Config) GetMaxTokens() int

GetMaxTokens returns the per-completion output-token cap under the read lock. 0 means "provider default". Paired with SetMaxTokens.

func (*Config) GetMemoryRecallModel added in v1.4.3

func (c *Config) GetMemoryRecallModel() string

GetMemoryRecallModel returns the pinned recall-model id ("" → default resolution) under the read lock.

func (*Config) GetOutputStyle added in v1.11.0

func (c *Config) GetOutputStyle() string

GetOutputStyle returns the active output-style name under the read lock, normalizing the empty value to "default" so callers compare against one spelling. Read at profile build (internal/agent.applyOutputStyle).

func (*Config) GetProviderAPIKey added in v1.4.3

func (c *Config) GetProviderAPIKey(name string) string

GetProviderAPIKey returns the stored api key for the named provider under the read lock, or "" when the provider has no entry.

func (*Config) GetProviderAPIURL added in v1.4.3

func (c *Config) GetProviderAPIURL(name string) string

GetProviderAPIURL returns the stored api base URL for the named provider under the read lock, or "" when the provider has no entry.

func (*Config) GetRepoMapTokenBudget added in v1.8.2

func (c *Config) GetRepoMapTokenBudget() int

GetRepoMapTokenBudget returns the repo-map token budget under the read lock.

func (*Config) GetTavilyAPIKey added in v1.4.3

func (c *Config) GetTavilyAPIKey() string

GetTavilyAPIKey returns the Tavily key under the read lock. Empty means web_search is disabled. Paired with SetTavilyAPIKey.

func (*Config) GetWorkflowMaxWorkers added in v1.11.0

func (c *Config) GetWorkflowMaxWorkers() int

GetWorkflowMaxWorkers returns the engine's concurrent-worker cap under the read lock (≥1; load normalizes ≤0 to the default 4).

func (*Config) IsDevelopment

func (c *Config) IsDevelopment() bool

IsDevelopment / IsProduction — semantic helpers so call sites don't hardcode string literals scattered across the codebase.

func (*Config) IsProduction

func (c *Config) IsProduction() bool

func (*Config) LLMTemperature added in v1.4.3

func (c *Config) LLMTemperature() *float64

LLMTemperature returns the current temperature or nil (provider default).

func (*Config) LLMTopK added in v1.4.3

func (c *Config) LLMTopK() *int

LLMTopK returns the current top_k or nil (provider default).

func (*Config) LLMTopP added in v1.4.3

func (c *Config) LLMTopP() *float64

LLMTopP returns the current top_p or nil (provider default).

func (*Config) SaveFile

func (c *Config) SaveFile() error

SaveFile re-serializes the user-tunable subset to AppHomeConfigFile. The /config setters and the runtime /model switch both call this.

Snapshots all fields under c.mu.RLock, releases that lock before blocking on disk I/O, then takes c.saveMu so concurrent saves don't interleave on the file.

func (*Config) SetAutoCompactThreshold

func (c *Config) SetAutoCompactThreshold(v float64) error

SetAutoCompactThreshold validates 0 < v <= 1 and persists.

func (*Config) SetAutoDreamModel added in v1.8.0

func (c *Config) SetAutoDreamModel(v string) error

SetAutoDreamModel pins (or clears, with "") the dream agent's model and persists.

func (*Config) SetCustom added in v1.4.3

func (c *Config) SetCustom(key string, value any) error

SetCustom stores value under key in CustomConfig and persists the change to AppHomeConfigFile via SaveFile. An empty key is rejected.

Nil values are stored as-is — call DeleteCustom to remove the entry. Concurrent SetCustom calls are serialized by c.mu.

Example

ExampleConfig_SetCustom shows the CustomConfig extension slot. Use it for downstream-private settings that don't fit the typed Config fields (broker URLs, feature flags, tenant secrets). Values round-trip through YAML as a `custom:` section under the AppHome config file; consumers cast at use-site.

package main

import (
	"fmt"
	"path/filepath"

	"github.com/johnny1110/evva/pkg/config"
)

func main() {
	tmp, _ := filepath.Abs("/tmp/evva-example-custom")
	cfg, _ := config.Load(config.LoadOptions{
		AppName: "friday", AppHome: tmp, WorkDir: tmp,
	})

	_ = cfg.SetCustom("broker.url", "https://broker.internal")
	_ = cfg.SetCustom("flags", map[string]any{"beta_ui": true})

	if v, ok := cfg.GetCustom("broker.url"); ok {
		fmt.Println("broker:", v.(string))
	}
	if v, ok := cfg.GetCustom("flags"); ok {
		fmt.Println("beta_ui:", v.(map[string]any)["beta_ui"])
	}
}
Output:
broker: https://broker.internal
beta_ui: true

func (*Config) SetDefaultEffort

func (c *Config) SetDefaultEffort(level string) error

SetDefaultEffort validates the effort level name and persists it.

func (*Config) SetDefaultModel

func (c *Config) SetDefaultModel(provider constant.LLMProvider, model constant.Model) error

SetDefaultModel updates the (provider, model) pair the agent boots with and persists it. Phase-3's runtime /model swap calls this after rebuilding the Agent's llm.Client so next launch starts with the user's last choice. Validates that the model is actually offered by the provider.

func (*Config) SetDefaultProfile

func (c *Config) SetDefaultProfile(name string) error

SetDefaultProfile persists the chosen persona name. Validation against the agent registry happens at the call site (AgentRegistry lives in internal/agent, which can't be imported from config without a cycle). Empty string is accepted — bootstrap interprets "" as "fall back to evva".

func (*Config) SetDisplayThinking

func (c *Config) SetDisplayThinking(v bool) error

SetDisplayThinking mutates the in-memory flag and persists to disk.

func (*Config) SetEnableAutoDream added in v1.8.0

func (c *Config) SetEnableAutoDream(v bool) error

SetEnableAutoDream toggles background memory consolidation and persists.

func (*Config) SetEnableAutoMemory added in v1.4.3

func (c *Config) SetEnableAutoMemory(v bool) error

SetEnableAutoMemory toggles the auto-memory subsystem and persists. Takes effect for the prompt and tool registration on next agent boot.

func (*Config) SetEnableCheckpoints added in v1.8.2

func (c *Config) SetEnableCheckpoints(v bool) error

SetEnableCheckpoints toggles checkpoint/rewind and persists. Takes effect on the next agent boot (the manager + capture sink are wired at construction).

func (*Config) SetEnableDynamicWorkflow added in v1.11.0

func (c *Config) SetEnableDynamicWorkflow(v bool) error

SetEnableDynamicWorkflow toggles the solo dynamic workflow and persists. Takes effect on the next agent boot / profile switch (the board tools and the engine wire at agent construction).

func (*Config) SetEnableMemoryRecall added in v1.4.3

func (c *Config) SetEnableMemoryRecall(v bool) error

SetEnableMemoryRecall toggles the per-turn recall side-query and persists.

func (*Config) SetEnableRepoMap added in v1.8.2

func (c *Config) SetEnableRepoMap(v bool) error

SetEnableRepoMap toggles the repo map and persists. Takes effect on the next agent boot / profile switch (the map is composed at prompt-render time).

func (*Config) SetFetchMaxBytes

func (c *Config) SetFetchMaxBytes(n int) error

SetFetchMaxBytes validates > 0 and persists.

func (*Config) SetLLMTemperature added in v1.4.3

func (c *Config) SetLLMTemperature(v *float64) error

SetLLMTemperature updates the session-only temperature. nil clears it (provider default). Validates 0 ≤ v ≤ 2. Never persisted to disk.

func (*Config) SetLLMTopK added in v1.4.3

func (c *Config) SetLLMTopK(v *int) error

SetLLMTopK updates the session-only top_k. nil clears it (provider default). Validates v > 0. Never persisted to disk.

func (*Config) SetLLMTopP added in v1.4.3

func (c *Config) SetLLMTopP(v *float64) error

SetLLMTopP updates the session-only top_p. nil clears it (provider default). Validates 0 ≤ v ≤ 1. Never persisted to disk.

func (*Config) SetLSPDiagnosticsOnEdit added in v1.11.0

func (c *Config) SetLSPDiagnosticsOnEdit(v bool) error

SetLSPDiagnosticsOnEdit toggles the synchronous tier and persists.

func (*Config) SetMaxIterations

func (c *Config) SetMaxIterations(n int) error

SetMaxIterations validates >0 and persists. NOTE: this only updates the YAML default; the live cap on a running agent is on Agent itself — call Controller.SetMaxIterations to mutate it.

func (*Config) SetMaxTokens

func (c *Config) SetMaxTokens(n int) error

SetMaxTokens validates >=0 and persists. 0 means "provider default". Effective on next launch — the agent's profile snapshots this at construction.

func (*Config) SetMemoryRecallModel added in v1.4.3

func (c *Config) SetMemoryRecallModel(v string) error

SetMemoryRecallModel pins (or clears, with "") the recall side-query model and persists.

func (*Config) SetOutputStyle added in v1.11.0

func (c *Config) SetOutputStyle(name string) error

SetOutputStyle records the output-style name and persists. "default" (any case) stores as "" so an untouched config file stays free of the key. Existence validation deliberately lives with the callers that hold the resolved catalog (the /output-style picker offers only real styles; the config tool validates before calling) — the profile build falls back to default on an unknown name regardless, so a deleted style file can never wedge boot.

func (*Config) SetProviderAPIKey

func (c *Config) SetProviderAPIKey(name, key string) error

SetProviderAPIKey installs an api key for the named provider and persists. Empty key removes the provider from LLMProviderConfig (cloud providers require a key to be listed). The constant.LLMProvider must already be known.

func (*Config) SetProviderAPIURL

func (c *Config) SetProviderAPIURL(name, url string) error

SetProviderAPIURL overrides the api_url for the named provider. Empty resets to the provider's built-in default.

func (*Config) SetProviderCredentials added in v1.4.3

func (c *Config) SetProviderCredentials(name, apiURL, apiKey string) error

SetProviderCredentials writes the (apiURL, apiKey) pair for the named LLM provider into Config.LLMProviderConfig under the mutex.

This is the documented path for downstream apps to install credentials at runtime — direct map assignment (`cfg.LLMProviderConfig["deepseek"] = ...`) still works but races with concurrent reads. SetProviderCredentials takes c.mu so two goroutines wiring different providers at startup don't tear.

An empty name is rejected. Unknown provider names are NOT rejected: downstream apps register custom providers into pkg/llm's registry without touching constant, and the agent's LLM-build step will surface the typo if no factory matches. apiURL may be empty — providers with a sane default (DeepSeek, Anthropic) fall back to it. apiKey may be empty for local providers (Ollama, ...).

Models on the existing APIConfig (if any) are preserved. Pass through the public map slot when a custom Models list is also needed.

Example

ExampleConfig_SetProviderCredentials shows the Phase 19b thread-safe setter for LLM credentials. Prefer this over direct map assignment when wiring providers at runtime — direct writes race concurrent reads on the same *Config.

package main

import (
	"fmt"
	"path/filepath"

	"github.com/johnny1110/evva/pkg/config"
)

func main() {
	tmp, _ := filepath.Abs("/tmp/evva-example-creds")
	cfg, _ := config.Load(config.LoadOptions{
		AppName: "alpha", AppHome: tmp, WorkDir: tmp,
	})

	if err := cfg.SetProviderCredentials(
		"deepseek",
		"https://api.deepseek.com",
		"sk-example-key",
	); err != nil {
		fmt.Println("error:", err)
		return
	}

	got := cfg.LLMProviderConfig["deepseek"]
	fmt.Println("api_url:", got.ApiURL)
	fmt.Println("api_secret_present:", got.ApiSecret != "")
}
Output:
api_url: https://api.deepseek.com
api_secret_present: true

func (*Config) SetRepoMapTokenBudget added in v1.8.2

func (c *Config) SetRepoMapTokenBudget(v int) error

SetRepoMapTokenBudget sets the repo-map token budget and persists. Values ≤0 are normalized to the default at load.

func (*Config) SetTavilyAPIKey

func (c *Config) SetTavilyAPIKey(s string) error

SetTavilyAPIKey persists the key. Empty string disables web_search.

func (*Config) SetWorkflowMaxWorkers added in v1.11.0

func (c *Config) SetWorkflowMaxWorkers(v int) error

SetWorkflowMaxWorkers sets the engine's concurrent-worker cap and persists. Values ≤0 are normalized to the default at load.

type EnvOverride added in v1.4.3

type EnvOverride struct {
	Name string
	Fn   func(*Config) error
}

EnvOverride is one entry in LoadOptions.EnvOverrides. The Name is used only for diagnostics — when Fn returns an error, Load wraps it as `config: EnvOverrides[<Name>]: <err>` so the failing override is identifiable in logs without re-running Load.

type FileConfig

type FileConfig struct {
	MaxIterations        int     `yaml:"max_iterations"`
	MaxTokens            int     `yaml:"max_tokens"`
	AutoCompactThreshold float64 `yaml:"auto_compact_threshold"`
	DisplayThinking      bool    `yaml:"display_thinking"`

	// DefaultProvider / DefaultModel are the (provider, model) pair the
	// agent boots with. Phase 3's /model switch will mutate these and call
	// Save to persist across launches.
	DefaultProvider string `yaml:"default_provider"`
	DefaultModel    string `yaml:"default_model"`

	DefaultEffort string `yaml:"default_effort"`

	// DefaultProfile is the persona the root agent boots into. Phase 6's
	// /profile switch mutates this and calls Save to persist across launches.
	// Empty falls back to "evva" at bootstrap.
	DefaultProfile string `yaml:"default_profile"`

	// PermissionMode is the agent's startup stance. One of:
	// "default" | "accept_edits" | "plan" | "bypass" | "auto". Defaults to
	// "default" when omitted. The -permission-mode CLI flag overrides this.
	PermissionMode string `yaml:"permission_mode"`

	FetchMaxBytes int    `yaml:"fetch_max_bytes"`
	TavilyAPIKey  string `yaml:"tavily_api_key"`

	// EnableAutoMemory gates the typed-memory subsystem (the per-session prompt
	// guidance + MEMORY.md index, the write carve-out, and per-turn recall).
	// Default true; users opt out via /config or by setting this to false.
	// Pointer so a missing key in YAML preserves the default rather than zeroing.
	EnableAutoMemory *bool `yaml:"enable_auto_memory,omitempty"`

	// EnableMemoryRecall gates the per-turn relevance side-query that surfaces
	// stored memories matching the current prompt. Default true (when auto-memory
	// is on); the cost-sensitive escape hatch — set false to keep the MEMORY.md
	// index but drop the extra completion per turn. Pointer to preserve the
	// default on a missing key.
	EnableMemoryRecall *bool `yaml:"enable_memory_recall,omitempty"`

	// MemoryRecallModel optionally pins the model used for the recall side-query.
	// Empty → a cheap model within the active provider (anthropic: sonnet,
	// deepseek: flash, openai: gpt-5.4-mini at medium effort; ollama/other: the
	// active model + the main agent's effort). Set a specific model here to
	// override, e.g. a different cost lever or any model whose provider you have a
	// key for. Unknown values are ignored (fall back to the default resolution).
	MemoryRecallModel string `yaml:"memory_recall_model,omitempty"`

	// EnableAutoDream gates background memory consolidation (the "dream" pass).
	// Default false — opt-in, since each dream is a real (if rare) token cost.
	// Pointer so a missing key preserves the default rather than zeroing.
	EnableAutoDream *bool `yaml:"enable_auto_dream,omitempty"`
	// AutoDreamMinHours / AutoDreamMinSessions are the dream gate thresholds
	// (defaults 24 / 5); load normalizes ≤0 to the default. AutoDreamModel
	// optionally pins the dream agent's model (empty → the recall-style
	// per-provider default).
	AutoDreamMinHours    int    `yaml:"auto_dream_min_hours,omitempty"`
	AutoDreamMinSessions int    `yaml:"auto_dream_min_sessions,omitempty"`
	AutoDreamModel       string `yaml:"auto_dream_model,omitempty"`

	// EnableCheckpoints gates checkpoint/rewind. Default false — opt-in, since an
	// enabled session writes per-turn before-images under .evva/checkpoints. Set
	// true to record them so /rewind can restore code/conversation. Pointer so a
	// missing key preserves the default. CheckpointMaxPerSession caps retained
	// checkpoints per session (default 50; ≤0 normalizes to the default at load).
	EnableCheckpoints       *bool `yaml:"enable_checkpoints,omitempty"`
	CheckpointMaxPerSession int   `yaml:"checkpoint_max_per_session,omitempty"`

	// EnableRepoMap gates the LSP-backed repo map injected into the main agent's
	// session-open prompt. Default false — opt-in, since an enabled session runs
	// a workspace symbol sweep at startup. Pointer so a missing key preserves the
	// default. RepoMapTokenBudget bounds the map's size (default 2000; ≤0
	// normalizes to the default at load).
	EnableRepoMap      *bool `yaml:"enable_repo_map,omitempty"`
	RepoMapTokenBudget int   `yaml:"repo_map_token_budget,omitempty"`

	// LSPDiagnosticsOnEdit gates the synchronous self-healing-edit tier: when
	// true, edit/write wait a short bounded window for LSP diagnostics on the
	// touched file and fold them into the tool's own result. Default false —
	// opt-in, since it adds latency to every edit/write when an LSP manager
	// is configured. Pointer so a missing key preserves the default.
	LSPDiagnosticsOnEdit *bool `yaml:"lsp_diagnostics_on_edit,omitempty"`

	// EnableDynamicWorkflow gates the solo dynamic-workflow board: the main
	// agent swaps todo_write for the wf_task_* graph tools and an in-process
	// engine auto-dispatches ephemeral subagent workers as dependencies
	// complete. Default false — opt-in, since an enabled session can spawn
	// real (token-costing) subagents without a per-dispatch prompt. Pointer so
	// a missing key preserves the default. WorkflowMaxWorkers caps concurrent
	// engine-dispatched workers (default 4; ≤0 normalizes to the default at
	// load).
	EnableDynamicWorkflow *bool `yaml:"enable_dynamic_workflow,omitempty"`
	WorkflowMaxWorkers    int   `yaml:"workflow_max_workers,omitempty"`

	// OutputStyle names the active output style overlaid on the main
	// persona's system prompt: "Explanatory", "Learning", or a custom
	// output-styles/*.md name. Empty (or "default") means no overlay.
	OutputStyle string `yaml:"output_style,omitempty"`

	Providers map[string]FileProviderConfig `yaml:"providers"`

	// Custom is the downstream-app extension slot. Values round-trip through
	// YAML as the `custom:` section. Empty / nil produces no `custom:` key in
	// the output. Decoded as map[string]any — consumers cast at use-site.
	Custom map[string]any `yaml:"custom,omitempty"`
}

FileConfig is the on-disk schema for $EvvaHome/config/evva-config.yml. It owns the user-tunable subset of configuration; deployment knobs (LOG_LEVEL, APP_ENV, ...) stay in .env.

func LoadFileConfig

func LoadFileConfig(path, appName string) (FileConfig, bool, error)

LoadFileConfig reads the YAML at path. On first launch (file absent) it writes a default YAML whose default_profile is the caller's appName — so a friday-flavoured Load writes "default_profile: friday" instead of bleeding evva's persona into a sibling app's config.

Returns (cfg, created, err):

  • created=true means the file didn't exist and was just written with defaults; callers can use this to surface a one-time first-launch notice.
  • Missing keys in an existing file fall back to defaultFileConfig values via pre-population, so partial YAML never crashes startup.

type FileProviderConfig

type FileProviderConfig struct {
	APIKey string `yaml:"api_key"`
	APIURL string `yaml:"api_url"`
}

FileProviderConfig carries per-provider credentials. Empty ApiURL falls back to the constant's built-in default.

type LoadOptions

type LoadOptions struct {
	AppName    string // brand identifier; drives the AppHome layout. Defaults to "evva".
	AppHome    string // absolute path; defaults to ~/.<AppName>/.
	WorkDir    string // process cwd; defaults to os.Getwd().
	AppVersion string // version string for diagnostics; defaults to DefaultAppVersion.

	// EnvAliases maps the caller's preferred env-var names onto evva's
	// canonical ones BEFORE godotenv.Load runs. Useful when a downstream
	// app advertises friendlier spellings — e.g. `{"LOGDIR": "LOG_DIR",
	// "LOGLEVEL": "LOG_LEVEL"}` lets a friday user write either form in
	// `~/.friday/.env` and have evva's loader pick it up.
	//
	// The promotion is non-overriding: an alias only seeds the canonical
	// name when that canonical name is unset. Existing canonical exports
	// win, so a deliberate `LOG_DIR=...` is never clobbered by a stray
	// alias.
	EnvAliases map[string]string

	// EnvOverrides runs AFTER the YAML + canonical env-vars have built
	// the Config. Each entry gets the populated *Config and can fold in
	// env vars that don't have a native hook inside Load (e.g.
	// MAX_ITERS → cfg.SetMaxIterations). The first error short-circuits
	// the rest and is returned from Load wrapped with the failing
	// override's Name — `config: EnvOverrides[<name>]: <wrapped>` — so
	// a downstream app with several overrides can identify the culprit
	// without grepping stack traces.
	//
	// Use this to translate downstream-flavoured env conventions
	// (APIKEY → cfg.SetProviderCredentials, MAX_ITERS → cfg.SetMaxIterations)
	// in one place instead of post-Load shim code at every call site.
	EnvOverrides []EnvOverride

	// ProviderCredentials wires LLM provider credentials from env vars
	// declaratively — the alternative to writing a separate EnvOverride
	// that reads os.Getenv(...) and calls cfg.SetProviderCredentials.
	//
	// Keyed by provider name (matches pkg/llm.DefaultRegistry); the
	// ProviderCredsFromEnv value names the env vars to read. EnvAliases
	// promotion runs first, so aliased names (e.g. `APIKEY` → `DEEPSEEK_API_KEY`)
	// reach this layer through their canonical form. Empty env-var values
	// are passed through to SetProviderCredentials — the agent's LLM-build
	// step will surface "API_KEY not set" loudly on first Run.
	//
	// Example:
	//
	//	LoadOptions{
	//	    ProviderCredentials: map[string]config.ProviderCredsFromEnv{
	//	        "deepseek": {APIKeyEnv: "DEEPSEEK_API_KEY",
	//	                    APIURLDefault: constant.DEEPSEEK.ApiUrl},
	//	    },
	//	}
	//
	// Applied AFTER the YAML loader populates LLMProviderConfig but
	// BEFORE EnvOverrides run, so an EnvOverride can still mutate the
	// installed creds if it needs to.
	ProviderCredentials map[string]ProviderCredsFromEnv

	// SeedEnvTemplate is written to <AppHome>/.env on first launch when
	// the file is missing. Useful for closing the "evva creates the YAML
	// but the user doesn't know to create .env" first-run gap.
	//
	// Empty means "don't write a .env template" (the historical behaviour).
	// An existing .env is never overwritten, even with SeedEnvTemplate set.
	SeedEnvTemplate string
}

LoadOptions tunes Load. Zero-value fields fall back to LoadDefault behavior — AppName="evva", AppHome=~/.evva/, WorkDir=os.Getwd(), AppVersion=DefaultAppVersion. Downstream apps that want a different home dir or app name fill in the relevant fields.

type ProviderCredsFromEnv added in v1.4.3

type ProviderCredsFromEnv struct {
	// APIKeyEnv is the env-var name carrying the provider's API key
	// (e.g. "DEEPSEEK_API_KEY"). Empty means "no key from env".
	APIKeyEnv string
	// APIURLEnv is the env-var name carrying the provider's API URL.
	// Most consumers leave this empty and lean on APIURLDefault.
	APIURLEnv string
	// APIURLDefault is the URL to use when APIURLEnv is empty or unset.
	// Typically a constant from pkg/constant (e.g. constant.DEEPSEEK.ApiUrl).
	APIURLDefault string
}

ProviderCredsFromEnv declares which env vars carry a provider's credentials. Empty fields are skipped (the YAML default wins for that slot). See LoadOptions.ProviderCredentials.

Jump to

Keyboard shortcuts

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