<!-- SPDX-License-Identifier: Apache-2.0 -->

# Engine configuration

`config/engine.toml` configures inference providers. It is required and must define exactly one global default provider.

The file maps provider names to provider tables.

```toml
[default]
apiBase = "https://api.openai.com/v1"
apiKey = "not-needed"
defaultModel = "gpt-5.6"
isGlobalDefault = true
kind = "openai"
```

## Provider fields

| Field | Required | Default | Description |
| --- | --- | --- | --- |
| `apiBase` | Yes | — | Absolute provider API base URL. |
| `apiKey` | No | `["not-needed"]` | One API key, or an array of keys for rotation. |
| `defaultModel` | Yes | — | Default model name. |
| `isGlobalDefault` | No | `false` | Marks exactly one provider as the global default. |
| `kind` | No | `"openai"` | Provider kind: `openai`, `anthropic`, or `openai-codex`. |
| `authId` | No | `"default"` | OAuth credential id used by `openai-codex`. |
| `availableModels` | No | `"analyze"` | Explicit model list, or `"analyze"` to resolve the provider's models on demand; a list replaces analysis for that provider. |
| `customHeaders` | No | `{}` | Extra request headers sent with provider requests. |
| `maxGenerationRetries` | No | `2` | Generation retry count. |
| `maxTurns` | No | `30` | Maximum engine turns per request. |
| `useFilesApi` | No | `false` | Files API mode; `false` or `"kimi"`. |
| `useJpegForImages` | No | `false` | Send images as JPEG. |
| `useToolChoiceAuto` | No | `false` | Prefer automatic tool choice. |

When `availableModels = "analyze"`, the `/model override` subcommand analyzes the provider's model list on demand, so models published after the runtime started are selectable without a restart.
Autocomplete reuses a successful analysis for 30 seconds and renews that window on every keystroke, so one lookup covers an operator typing a model name.

When a header value contains `$session`, the runtime replaces it on generation requests with the lower-case hexadecimal SHA-256 hash of the current session ID.
The same hash is sent as the provider prompt cache key, so configured headers and provider-side cache partitioning share one opaque session identifier instead of the internal ID.
The placeholder is also resolved for OpenAI-compatible file uploads and Codex requests.

## Per-model overrides

A `[<provider>.models.<model>]` table overrides context and reasoning settings for one model.

```toml
[default.models.gpt-5.6]
contextWindow = 128000
contextBudget = 0.8
contextHardBudget = 0.95
reasoning = "medium"
reasoningBudget = 8192
supportsVision = true
supportsVideo = false
maxImagesPerRequest = 10
toolFailThreshold = 3
```

| Field | Default | Description |
| --- | --- | --- |
| `contextWindow` | — | Context window size in tokens; resolved from provider metadata when absent. |
| `contextBudget` | — | Soft context budget as a fraction of the window. |
| `contextHardBudget` | — | Hard context budget as a fraction of the window. |
| `reasoning` | `true` | Reasoning toggle or mode: `true`, `false`, or `xhigh`, `high`, `medium`, `low`, `minimal`, `none`. |
| `reasoningBudget` | `16384` | Reasoning token budget. |
| `supportsVision` | `true` | Whether the model accepts images. |
| `supportsVideo` | `false` | Whether the model accepts video. |
| `maxImagesPerRequest` | — | Maximum images per request. |
| `toolFailThreshold` | `3` | Tool failures allowed before recovery. |

## Provider kinds

- `openai` — OpenAI-compatible chat completions API.
- `anthropic` — Anthropic messages API.
- `openai-codex` — OpenAI Codex via ChatGPT OAuth credentials managed by the `codex` command.

## Codex models

An `openai-codex` provider talks to the ChatGPT Codex backend, so `apiBase` must be `https://chatgpt.com/backend-api`; the runtime appends `codex/responses` for generation and `codex/models` for the model list.

```toml
[codex]
kind = "openai-codex"
apiBase = "https://chatgpt.com/backend-api"
authId = "default"
defaultModel = "gpt-5.6-terra"
isGlobalDefault = true
```

The runtime asks that backend for the account's model list, so models it publishes after the runtime started are selectable without a restart.
The query reuses its result for five minutes, sends the provider's `customHeaders`, and falls back to a catalog snapshot bundled with the build when it fails, logging a warning instead of failing the command.
Only models the backend publishes as visible appear in the list and in autocomplete; a hidden model stays selectable by naming it in `availableModels` or in a `[codex.models.<model>]` table.

A configured model name reaches the backend unchanged apart from a stripped `vendor/` prefix and normalized case and separators, so the runtime never substitutes a different model for the one you chose.
Reasoning follows the levels the backend advertises for that model: an explicit `reasoning` mode is used when the model advertises it, `false` and `minimal` use the model's least advertised level, and an unset mode uses the model's advertised default.
The `reasoning` picklist stops at `xhigh`, so `max`, which the backend accepts for some models, is not selectable in configuration.
Context windows resolve from the same catalog, so `contextWindow` is optional when the backend reports one.

Codex image input is automatically downscaled when its 32-pixel patch count would exceed the backend's 30,000-patch per-image limit.
The resized image is sent as WebP, or JPEG when `useJpegForImages = true`.

## OpenAI-compatible request headers

The `openai` provider sends these attribution headers with chat-completions requests:

| Header | Value |
| --- | --- |
| `HTTP-Referer` | `https://github.com/CutieZone/CireilClaw` |
| `X-OpenRouter-Categories` | `personal-agent,cli-agent` |
| `X-OpenRouter-Title` | `CireilClaw` |

These headers support OpenRouter application attribution and are sent for every `openai` provider because the route supports OpenAI-compatible APIs.

Use `customHeaders` for additional provider-specific request headers.

## Reload

The runtime watches `engine.toml` and applies changes live to subsequent turns.
