# `Pixir.Config`
[🔗](https://github.com/Ranvier-Technologies/pixir/blob/main/lib/pixir/config.ex#L1)

Loader for `~/.pixir/config.json` (ADR 0005 ergonomics).

Parses user-global knobs, ignores invalid values with warnings (never hard-fails on
a bad field), and resolves effective values with this precedence for each key:

  1. `config :pixir, :key` (programmatic override)
  2. `~/.pixir/config.json`
  3. built-in default

Model id keeps the Provider chain: `config :pixir, :model` → `PIXIR_MODEL` →
`"model"` in config.json → built-in default.

Legacy keys (`model`, `models`, `context_windows`) remain supported alongside the
expanded surface (`permission_default`, `reasoning.effort`, `text.verbosity`,
`bash_timeout_ms`, `bash_timeout_max_ms`, `host_commands`, `max_retries`,
`stream_idle_timeout_ms`, `compaction.tail_events`).

`bash_timeout_max_ms` is an override cap, not a way to shorten the configured
default. The effective cap is never lower than `bash_timeout_ms`; when config asks
for a lower cap, `load/1` reports a warning and raises the effective cap so the
default bash command remains executable.

# `load_result`

```elixir
@type load_result() :: %{required(String.t()) =&gt; term()}
```

# `warning`

```elixir
@type warning() :: %{required(String.t()) =&gt; String.t()}
```

# `bash_timeout_max_ms`

```elixir
@spec bash_timeout_max_ms(keyword()) :: pos_integer()
```

# `bash_timeout_ms`

```elixir
@spec bash_timeout_ms(keyword()) :: pos_integer()
```

# `compaction_model_assisted`

```elixir
@spec compaction_model_assisted(keyword()) :: boolean()
```

Whether model-assisted compaction is enabled (default `false`).

# `compaction_tail_events`

```elixir
@spec compaction_tail_events(keyword()) :: pos_integer()
```

# `file_anthropic_models`

```elixir
@spec file_anthropic_models(keyword()) :: [String.t()] | nil
```

Anthropic models list from config.json only, or `nil` when absent/invalid.

# `file_context_windows`

```elixir
@spec file_context_windows(keyword()) :: %{required(String.t()) =&gt; pos_integer()}
```

Context-window overrides from config.json (model => positive integer).

# `file_model`

```elixir
@spec file_model(keyword()) :: String.t() | nil
```

Effective model when config.json is present; application and env precedence still applies.

# `file_models`

```elixir
@spec file_models(keyword()) :: [String.t()] | nil
```

Models list from config.json only, or `nil` when absent/invalid.

# `host_commands`

```elixir
@spec host_commands(keyword()) ::
  {:ok, %{required(String.t()) =&gt; non_neg_integer() | pos_integer()}}
```

Resolved host-command boundary limits.

# `load`

```elixir
@spec load(keyword()) :: load_result()
```

Load and resolve `config.json`.

Returns a JSON-serializable map with `"path"`, `"present"`, `"effective"`, and
`"warnings"`. Missing files yield defaults and an empty warning list.

# `max_retries`

```elixir
@spec max_retries(keyword()) :: non_neg_integer()
```

# `merge_provider_opts`

```elixir
@spec merge_provider_opts(keyword(), keyword()) :: keyword()
```

Merge config defaults into Provider opts without clobbering explicit values.

# `models_refreshed_at`

```elixir
@spec models_refreshed_at(keyword()) :: String.t() | nil
```

UTC timestamp written by the last explicit model-catalog refresh, if present.

# `permission_default`

```elixir
@spec permission_default(keyword()) :: Permissions.mode()
```

Resolved permission default (`:auto`, `:ask`, or `:read_only`).

# `reasoning_effort`

```elixir
@spec reasoning_effort(keyword()) :: String.t() | nil
```

Resolved reasoning effort, or `nil` to omit and let the model default.

# `request_snapshot`

```elixir
@spec request_snapshot(keyword()) :: {:ok, map()} | {:error, map()}
```

Resolve one immutable model/backend snapshot from exactly one source-document read.

# `responses_backend`

```elixir
@spec responses_backend(keyword()) ::
  {:ok, :absent | Pixir.Providers.ResponsesBackend.t()} | {:error, map()}
```

Resolve only the explicit Responses backend descriptor from one request snapshot.

# `stream_idle_timeout_ms`

```elixir
@spec stream_idle_timeout_ms(keyword()) :: non_neg_integer()
```

# `text_verbosity`

```elixir
@spec text_verbosity(keyword()) :: String.t() | nil
```

Resolved text verbosity, or `nil` to omit and let the model default.

# `valid_reasoning_efforts`

```elixir
@spec valid_reasoning_efforts() :: [String.t()]
```

Accepted reasoning effort ids.

# `web_search`

```elixir
@spec web_search(keyword()) :: map() | nil
```

Resolved hosted web search config, or `nil` when disabled/absent.

---

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