# `MingaAgent.Config`
[🔗](https://github.com/jsmestad/minga/blob/main/lib/minga_agent/config.ex#L1)

Single source of truth for all agent tunables.

Reads user-facing settings from `Minga.Config.Options` and defines
sensible defaults for internal tunables. No other agent module should
define `@default_*` constants or call `Config.get(:agent_*)` directly.

Call `resolve/0` once at session or provider init, then thread the
resulting `%Config{}` through state and function arguments.

## Adding a new setting

1. Add the field to the struct and `@type t`
2. Add the default value in `defstruct`
3. If user-configurable, register it in `Minga.Config.Options` and
   read it in `resolve/0`
4. If internal-only, the struct default is sufficient

# `t`

```elixir
@type t() :: %MingaAgent.Config{
  agent_hooks: [MingaAgent.Hooks.Hook.t()],
  api_base_url: String.t(),
  api_base_url_override: String.t() | nil,
  api_endpoints: map() | nil,
  append_system_prompt: String.t(),
  approval_timeout_ms: pos_integer(),
  auto_context: boolean(),
  compaction_keep_recent: pos_integer(),
  compaction_threshold: float() | nil,
  destructive_tools: [String.t()],
  diff_size_threshold: pos_integer(),
  max_cost: float() | nil,
  max_file_size: pos_integer(),
  max_image_size: pos_integer(),
  max_mention_candidates: pos_integer(),
  max_retries: non_neg_integer(),
  max_tokens: pos_integer(),
  max_turns: pos_integer(),
  mcp_servers: [MingaAgent.MCP.ServerConfig.t() | map()],
  memory_max_tokens: pos_integer(),
  model: String.t(),
  models: [String.t()],
  notifications: boolean(),
  notify_debounce_ms: pos_integer(),
  notify_on: [atom()],
  panel_split: pos_integer(),
  prompt_cache: boolean(),
  provider: :auto | :native | String.t(),
  save_debounce_ms: pos_integer(),
  session_retention_days: pos_integer(),
  shell_debounce_ms: pos_integer(),
  subagent_timeout_ms: pos_integer(),
  system_prompt: String.t(),
  tool_approval: :destructive | :all | :none,
  tool_permissions: map() | nil
}
```

# `default_model`

```elixir
@spec default_model() :: String.t()
```

Returns the default model string (with provider prefix).

# `extract_provider_prefix`

```elixir
@spec extract_provider_prefix(String.t()) :: String.t()
```

Extracts the provider prefix from a model spec string.

Returns the provider name, or `""` if no prefix is present.

## Examples

    iex> MingaAgent.Config.extract_provider_prefix("openai_codex:gpt-5.3-codex-spark")
    "openai_codex"

    iex> MingaAgent.Config.extract_provider_prefix("claude-sonnet-4")
    ""

# `normalize_hooks`

```elixir
@spec normalize_hooks(term()) :: [MingaAgent.Hooks.Hook.t()]
```

Normalizes raw `:agent_hooks` config declarations into hook structs.

# `resolve`

```elixir
@spec resolve() :: t()
```

Builds a `%Config{}` from `Options` with struct defaults as fallbacks.

Safe to call before the Options ETS table exists (e.g., in tests or
standalone usage). When Options is unavailable, all fields use their
struct defaults.

# `resolve_model`

```elixir
@spec resolve_model() :: String.t()
```

Returns the configured model, falling back to the default.

Safe to call before Config is running (catches exits).

# `resolve_provider`

```elixir
@spec resolve_provider() :: :auto | :native | String.t()
```

Returns the configured provider, falling back to `:auto`.

Safe to call before Config is running (catches exits).

# `strip_provider_prefix`

```elixir
@spec strip_provider_prefix(String.t()) :: String.t()
```

Strips the "provider:" prefix from a model spec string.

Returns the bare model name. If there's no prefix, returns the string unchanged.

## Examples

    iex> MingaAgent.Config.strip_provider_prefix("anthropic:claude-sonnet-4")
    "claude-sonnet-4"

    iex> MingaAgent.Config.strip_provider_prefix("claude-sonnet-4")
    "claude-sonnet-4"

# `without_hooks`

```elixir
@spec without_hooks(t()) :: t()
```

Returns a config with agent hooks disabled.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
