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

# Engine configuration

Each agent's `agents/<slug>/config/engine.toml` configures its inference providers. It is required and must define exactly one default provider for that agent using `isGlobalDefault = true`. Agents do not inherit providers from a runtime-root `config/engine.toml`.

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`              | Separate retry limits for provider failures and undelivered-output recovery, each counted across a turn.                    |
| `useFilesApi`          | No       | `false`          | Files API mode; `false` or `"kimi"`.                                                                                        |
| `useJpegForImages`     | No       | `false`          | Send images as JPEG.                                                                                                        |
| `useToolChoiceAuto`    | No       | `false`          | Prefer automatic tool choice.                                                                                               |

History pruning uses the model's token budget, not a sliding turn cap. The runtime uses an explicit per-model `contextWindow` first, then provider metadata, then a fixed 128000-token fallback when neither supplies a window. Budget pruning drops history near the token limit rather than shifting the visible prefix on every turn, preserving provider prompt caching between pruning events. Set `contextWindow` explicitly for models with smaller windows when their provider does not publish usable metadata. The removed `maxTurns` setting is migrated automatically; see [upgrade instructions](../operations/upgrades.md#v200-context-budget-migration).

Provider context-window metadata set to `0` is treated as unspecified, as with ArliAI's `Fastest` routing alias, without discarding metadata for other models. OpenAI-compatible discovery checks `context_length`, then `top_provider.context_length`, then `max_input_tokens`, using the first positive value. If all are absent, null, or zero, configure `contextWindow` explicitly or the runtime uses its fixed fallback.

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
systemOnlyAtStart = true
maxImagesPerRequest = 10
repeatedToolCallThreshold = 3
toolFailThreshold = 3
```

| Field                       | Default | Description                                                                                                         |
| --------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------- |
| `contextWindow`             | —       | Context window size in tokens; uses provider metadata when absent, then a 128000-token fallback.                    |
| `contextBudget`             | `0.6`   | Soft context budget as a fraction of the window.                                                                    |
| `contextHardBudget`         | `0.85`  | 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.                                                                                    |
| `systemOnlyAtStart`         | `true`  | Send system messages after the initial system prompt as user messages.                                              |
| `maxImagesPerRequest`       | —       | Maximum images per request.                                                                                         |
| `repeatedToolCallThreshold` | `3`     | Consecutive identical successful calls, with unchanged input and result, allowed before the turn fails.             |
| `toolFailThreshold`         | `3`     | Consecutive failures per tool before disabling it for the rest of the turn; `respond` and `no-response` are exempt. |

`systemOnlyAtStart` leaves the initial system prompt unchanged and converts later system injections, including OpenAI-compatible tool-use reminders, to the `user` role without changing their content or stored session history. Set it to `false` to preserve the provider's previous behavior: OpenAI-compatible and Codex requests keep later system roles, while Anthropic omits system messages from conversation history because its API only accepts a separate initial system prompt.

A repeated-call limit applies to every turn, including heartbeats. A tripped limit never produces `HEARTBEAT_OK`; the scheduler records the turn as failed and continues with the next scheduled heartbeat.

The runtime captures the date, context usage, and any near-limit warning once at the start of each turn. This metadata is labeled as a turn-start snapshot, not a live reading. Tool continuations and generation retries retain the original snapshot before that turn's tool activity instead of appending a fresh user-context message after each tool result. The snapshot is request-local and is not saved in session history; the next turn captures a new one. Context pruning still runs before each inference request.

## 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.
