# Models & providers

> Reference for choosing a model/backend wire, effort controls, context, and pricing. To connect a provider, follow the canonical /models first-run path.

Reference for choosing a model/backend wire, effort controls, context, and
pricing. To connect a provider, follow the canonical
[`/models` first-run path](/docs/kin/getting-started/first-run/#fast-path-models).

<!-- SOURCE: src/kin/harness/presets.py, src/kin/harness/model_catalog.py, src/kin/harness/model_hub.py, src/kin/harness/model_routes.py, src/kin/harness/media_routing.py, src/kin/harness/backends/factory.py, src/kin/harness/backends/base.py, src/kin/harness/backends/openai.py, src/kin/harness/backends/responses.py, src/kin/harness/backends/anthropic.py, src/kin/harness/compaction.py, src/kin/harness/loop.py, src/kin/harness/pricing.py, src/kin/harness/session/, src/kin/harness/settings/, src/kin/harness/tools/inspect_media.py, src/kin/tui/commands/model.py, src/kin/tui/widgets/, src/kin/tui/modals/model_hub.py, scripts/_live_model.py, scripts/live_laguna.py, scripts/live_qwen_vision.py, scripts/live_vllm_responses.py, settings.toml.example -->

`kin` separates provider identity from wire shape. Pick OpenAI or Anthropic with
`provider`, pick Chat Completions or Responses with `wire_api`, pick the model
with `model`, and pick the endpoint with `base_url`. Each knob resolves in this
order: explicit/CLI override, environment, merged settings, preset, then the
built-in default. See [Configuration](/docs/kin/getting-started/configuration/) for
the file precedence rules and [Environment
variables](/docs/kin/reference/environment-variables/) for the full list.

## Current Kinra-hosted topology

Kin's repository live gates follow two independently qualified routes. The
main route is DS4 over OpenAI Responses at the stable private Tailnet relay,
with a 131,072-token context window and 32,768-token output limit. Vision and
utility side-calls use the one-slot Qwen route on Kloud, with a 32,768-token
context window and a 1,024-token output cap. Run both proofs with:

```bash
task live-models
```

Hosted clients use the authenticated public DS4 door instead of copying the
private addresses. Create a distinct bearer for each person/device in the
Kinra console, then run this in that client's terminal; the key prompt is
masked and the secret is written to `~/.kin/credentials.toml`, never argv or
shell history:

```bash
kin connect kinra-api \
  --base-url https://api.kinra.ai/v1 \
  --wire-api responses \
  --model deepseek-v4-flash \
  --label 'Kinra hosted inference' \
  --vision-route kinra-vision \
  --vision-model qwen3.6-35b-a3b \
  --vision-description 'Kinra hosted vision on Kloud' \
  --vision-max-tokens 1024 \
  --vision-reasoning-profile qwen-llamacpp \
  --vision-effort thinking-512 \
  --utility-route kinra-vision
```

DS4 advertises exact input counting, tools, tool choice, and reasoning effort.
It does not currently advertise Responses `text.format`, so Kin qualifies the
strict tool-schema paths but does not claim server-constrained free-text JSON
on this route. Historical vLLM, Laguna, and llama.cpp sections below are
explicit compatibility profiles, not fleet defaults.

## Chat, Responses, and Messages

There are three wire APIs across two provider families.

| Backend | `provider` | `wire_api` | Default model | Notes |
|---|---|---|---|---|
| OpenAI Chat | `openai` | `chat` | `gpt-4o-mini` | Compatibility default. Works against OpenAI, OpenRouter, vLLM, LiteLLM, Together, and other Chat-compatible servers. |
| OpenAI Responses | `openai` | `responses` | `gpt-4o-mini` | Explicit opt-in. Uses typed Responses items/events and local replay. Qualified implementations include the current DS4 relay, vLLM 0.25.1, and Paddock-managed llama.cpp. |
| Anthropic Messages | `anthropic` | `messages` (internal) | `claude-opus-4-8` | Native Anthropic, or a vLLM serve that exposes `/v1/messages`. |

Public `wire_api` values are only `chat` and `responses`; either one implies
the OpenAI provider. `messages` is Kin's internal discriminator for Anthropic
history and is not a valid CLI, environment, or settings value. A bare
`provider = "openai"` still selects Chat, and the built-in OpenAI preset stays
on Chat. Select Responses deliberately:

```bash
uv run kin --wire-api responses --base-url http://localhost:8000/v1
# or: KIN_WIRE_API=responses uv run kin
```

```toml
provider = "openai"       # optional: wire_api already implies OpenAI
wire_api = "responses"
```

Unknown providers/wires and `provider = "anthropic"` combined with an OpenAI
wire fail at configuration time. An explicit `wire_api` also bypasses the bare
base-URL Anthropic heuristic described below.

The OpenAI-compat backend streams text, reasoning, tool-call fragments, and
usage, normalizing the quirks of various compat stacks (it trusts assembled
tool calls over a stray `finish_reason: stop`, and reads reasoning from
whichever field the provider uses). The Anthropic-compatible path likewise
keeps a complete terminal response when a third-party endpoint omits the final
usage object; token usage remains unknown for that response instead of turning
already-streamed content into a backend error.

The Responses backend streams typed text, refusal, reasoning, reasoning-summary,
function-draft, and terminal events. The terminal response is authoritative for
exact output items, completed calls, model identity, usage, and
incomplete/failed status. It sends the system prompt as `instructions`, the
output cap as `max_output_tokens`, reasoning effort as `reasoning.effort`, and
JSON Schema output through `text.format`. vLLM-specific `top_k`, priority, and
chat-template values remain explicit compatibility extras.

The Anthropic Messages backend uses adaptive thinking with summarized display so reasoning streams, sets effort via `output_config`, preserves thinking-block signatures across turns so they replay on the same model, and supports prefix caching on the frozen system prompt. The `thinking.type` knob is knob-shaped (`enabled` / `disabled` / `adaptive`); real Anthropic defaults to `adaptive` so behavior is unchanged on a vanilla install. The Anthropic-compat presets (Z.ai, MiniMax) don't implement `output_config.effort` — their `/effort` vocabularies map to each provider's real knob instead (see [Effort picker](#effort-picker)).

<a id="the-kin_base_url-heuristic"></a>
## The KIN_BASE_URL heuristic

When you set `base_url` (via `--base-url`, `KIN_BASE_URL`, or a settings file)
but leave both `provider` and `wire_api` unset, the factory defaults to the
**Anthropic Messages wire**, not an OpenAI wire.

The reason is that a `base_url` almost always points at a self-hosted server (typically vLLM), and on such a server the Anthropic wire is the stronger choice:

- **Parallel-tool-safe blocks** — typed `tool_use` blocks instead of the streamed-delta reassembly the compat wire has to do.
- **A real `count_tokens`** — the per-component context breakdown in `/tokens` works, where the compat wire reports totals only.
- **Preserved thinking signatures** — typed thinking blocks survive across turns for correct replay.

The escape hatch is explicit. Set `provider` (any of `--provider openai`,
`KIN_PROVIDER=openai`, or `provider = "openai"` in a settings file) to force
Chat Completions, or select `wire_api = "responses"` for Responses—for example
when `base_url` points at real OpenAI, a qualified vLLM, OpenRouter, or another
GPT-style endpoint.

> **Note**
>
> The heuristic only fires when both provider and wire API are unset.
> Anthropic-direct users (no `base_url`) keep OpenAI Chat as the default and
> opt in with `KIN_PROVIDER=anthropic`. Setting either identity axis
> explicitly always wins over the heuristic.

## base_url handling

The two SDKs disagree about what a base URL means, so the factory adjusts for the Anthropic wire.

`KIN_BASE_URL` is conventionally written the OpenAI way, ending in `/v1` (the OpenAI SDK appends `/chat/completions` to it). The Anthropic SDK appends `/v1/messages` to whatever you give it, so passing `http://host:port/v1` would produce `http://host:port/v1/v1/messages` and 404.

To let one env var drive both wires, the factory trims a trailing `/v1` before handing the URL to the Anthropic backend:

```text
KIN_BASE_URL=http://model-host:8000/v1
  → OpenAI-compat backend:  http://model-host:8000/v1   (used as-is)
  → Anthropic backend:      http://model-host:8000      (trailing /v1 trimmed)
```

You write the URL once, the OpenAI way; the trim happens only on the Anthropic path. With no `base_url` set, each backend talks to its provider's public endpoint (`api.openai.com` / `api.anthropic.com`).

## Model routes

Use `/models` for the human configuration surface. It opens on plain-language
jobs—Main conversation, Images, Computer screenshots, Tasks, Workflows,
Utility side-calls, and discovered agent profiles—then lets you assign an
existing compatible route or create one inline. `vision = false` routes are unavailable for image/Rig jobs;
unknown support remains selectable with a warning. `/routes` deep-links to the
route editor, while `/providers [id]` deep-links to Connections.

Connections and routes are staged together. **Save & apply** validates the
entire graph, refuses a stale same-section edit from another Kin, writes once,
and installs one prepared live snapshot. Escape discards the whole draft —
after a confirmation when edits (including a typed key) are staged, since a
form's ctrl+s only stages onto the draft and nothing is written until Save &
apply. The model and AI-drafted settings editor cannot call this human-only
transaction.

Connections also lists **orphaned keys**: a `provider_keys` entry whose id
matches no connection (renaming or removing a connection leaves its key behind
under the old id, and swaps read keys strictly by the current id). Select the
orphan row and press `del` to remove the stranded secret, or add a connection
with that exact id to reclaim it. `kin doctor` reports the same strays.

Named model routes let one session use different models or endpoints for
different responsibilities without making provider details part of the task.
For example, the main collaborative agent (MCA) can stay on a strong text-only
model while image inspection goes to a smaller vision model, and repetitive
task/workflow children go to a cheaper endpoint.

A route is wire-agnostic and operator-owned. It points to a trusted provider
preset plus an exact model id; it cannot contain a URL or key. Provider presets
remain the endpoint trust boundary and `[provider_keys]` remains the credential
store. The route and assignment tables are global-only, so a cloned repository
cannot redirect your prompts or images to another endpoint.

```toml
# ~/.kin/credentials.toml — kin writes this file (0600) for you
[provider_keys]
openai = "sk-..."
anthropic = "sk-ant-..."
```

```toml
# ~/.kin/settings.toml
[model_routes.primary]
provider_preset = "anthropic"
model = "your-text-model-id"
description = "Main collaboration model"
vision = false
effort = "high"

[model_routes.visual]
provider_preset = "openai"
model = "your-vision-model-id"
description = "Isolated image inspection"
vision = true
selectable_for = ["task", "workflow"]

[model_assignments]
mca = "primary"
vision = "visual"
rig = "visual"
task = "primary"
workflow = "primary"
utility = "visual"

[model_assignments.profiles]
researcher = "primary"
```

`openai` and `anthropic` are built-in direct presets, so the example does not
need custom endpoint rows. An OpenAI-compatible server still uses an ordinary
custom `[[providers]]` row; use that row's id as `provider_preset` in as many
routes as needed. A direct preset's empty base URL means the vendor SDK endpoint
and does not inherit an ambient `KIN_BASE_URL` intended for the default/local
backend; an explicitly supplied caller base URL remains the override escape
hatch.

<a id="qwen-llamacpp-bounded-thinking"></a>
### Qwen / llama.cpp bounded thinking

```toml
[model_routes.kinra-vision]
provider_preset = "kinra-api" # existing custom Chat/Responses connection
model = "qwen3.6-35b-a3b"
vision = true
reasoning_profile = "qwen-llamacpp"
effort = "thinking-512"
max_tokens = 1024
```

`reasoning_profile = "qwen-llamacpp"` is an explicit route-local wire
contract. It is never inferred from a model id, endpoint hostname, or the fact
that a connection uses Responses. Its effort picker uses budget labels:
`default`, `off`, `thinking-128`, `thinking-512`, `thinking-1024`, and
`thinking-2048`. These do not pretend Qwen implements OpenAI's semantic
low/medium/high scale.

`default` emits no Qwen fields. `off` emits only
`chat_template_kwargs.enable_thinking=false`. A `thinking-N` choice emits
`chat_template_kwargs.enable_thinking=true` and the pinned Kinra gateway's one
validated per-request field, `reasoning_budget_tokens=N`, on both Chat and
Responses. Kin deliberately does not also send current upstream llama.cpp's
`thinking_budget_tokens` spelling, and never enables `preserve_thinking` for
isolated media or Utility calls.

`max_tokens` is the total reasoning-plus-final allowance and must exceed the
reasoning budget. A route assigned to Utility must additionally leave 512
tokens for title output: 128/512/1024/2048 budgets therefore need at least
640/1024/1536/2560 total output tokens. Kin still clips the visible title to
the existing short-title contract. The initial `thinking-512` example is a
validation candidate, not a claim that 512—or the one-case 128-token result—is
optimal.

### Resolution and model authority

Kin resolves each delegated call in one fixed order:

1. `[model_assignments.profiles].<profile>`
2. the consumer assignment (`task` or `workflow`)
3. a model-requested `route` that the operator listed in `selectable_for`
4. the legacy per-call `model` override
5. the current MCA backend

`route` and `model` are mutually exclusive. A profile or consumer assignment
is operator policy and therefore wins over a model request. An invalid model
clone fails visibly instead of silently falling back to the parent model.
`fork_agent` is different by design: a fork keeps its parent's exact backend
and represented identity.

The task/workflow model sees only selectable route ids, descriptions, and
vision state. It never sees endpoint URLs, keys, or even preset/model ids.

### The utility job (housekeeping side-calls)

`utility` binds kin-internal housekeeping to a lightweight route — today that
is session [auto-titles](/docs/kin/guide/sessions/#session-titles); other bounded side jobs
can opt in individually later. It exists for prompt-cache hygiene: a hosted
main endpoint that keeps long conversations resident should not have its cache
disturbed by a ~100-token title call, so assigning `utility` to a small model
(the vision route works — one route can hold both jobs) moves that traffic off
the main model entirely.

The job is deliberately different from `task`: `task` is delegated subagent
execution and follows the full resolution order above, while `utility` is
assignment-only — the model can never select it, and it accepts no
`selectable_for` opt-in. Utility calls are bounded (titles cap at 64 output
tokens regardless of the route's `max_tokens`), best-effort (a route busy with
interactive work makes the call skip its idle window instead of queueing), and
never fall back: if the utility call fails, kin keeps the first-message
fallback title rather than re-running the request on the main backend.
Humans can inspect and edit the richer but still secret-free map in `/models
routes`, which shows preset/model identity, credential readiness, assignments,
manual/observed vision state, and this run's call/token totals. Route calls and
agent rows carry the same identity in the UI and metadata-only span sink.

### Vision routing and manual overrides

`vision = true` or `vision = false` is authoritative. This is the direct
override for a server whose model metadata is missing or wrong. If `vision` is
omitted, the route begins as `unknown`; the first isolated semantic media call
is attempted, and a successful call or a capability-shaped rejection is cached
for the loaded catalog. `/reload` constructs a fresh catalog and therefore
starts discovery fresh.

An `@image` attachment is inspected through the `vision` assignment before the
MCA turn. That vision request contains exactly the raw user question and image
bytes—no root system prompt, tools, conversation history, workspace context,
or prior images. Its prose returns as provenance-labelled, untrusted evidence
inside the durable user turn. If no `vision` assignment exists, Kin uses the
current MCA only when it is not marked `vision = false`. A direct
`inspect_media` call then fails with a configuration hint instead of sending the
bytes anyway; automatic `@image` handling preserves the textual turn and adds a
framed failure notice when the auxiliary route is unavailable.

A multi-image inspection is **one vision request per image, sequentially**.
Some hosted gateways accept only a single image per request, so Kin never
sends more than one image upstream: each image gets its own isolated request
(carrying the same question plus a deterministic `Image 2 of 3 (name.png)`
position note), and the labelled observations are combined into one
observation for the main turn, in the original order. The single-image
request shape is unchanged. If one image's inspection fails, the error names
that image and how many earlier images had succeeded — an image is never
silently skipped.

Tool-produced images stay in canonical history. A text-only MCA receives a
wire-only projection that replaces image payloads with deterministic refs such
as `media:call_7:0`; canonical history is never mutated. The model can call
`inspect_media(question, paths, refs)` to ask the vision route about those refs.
When `rig` is assigned, Rig browser screenshots and marked desktop snapshots
also gain a routed, untrusted visual observation while retaining the canonical
image result.

Semantic vision and tool-image transport are deliberately separate. A model
may understand an image in an initial user request while its endpoint rejects
images nested in tool results (or vice versa); one successful transport probe
does not claim the other capability.

### Sessions, reloads, budgets, and concurrency

The active route id, non-secret fingerprint, and backend snapshot are saved in
the session journal. Resume reconstructs that saved backend rather than
retargeting an old conversation through today's settings. A child likewise
keeps the exact route/backend it started with across pause/restart. `/reload`
validates and prepares the entire new catalog off to the side, then installs it
atomically for the root and future dispatches; already-running children keep
their old catalog snapshot.

Routed agent-loop and isolated-vision calls count toward the root run's token
budget and the `/tokens` input/output totals, but an isolated vision call does
not inflate the MCA context meter. `/routes` breaks those streamed calls down
by route. Pre-existing unmetered title, compaction, and structured-output side
calls remain outside that counter. Each route keeps its provider-tier
concurrency ceiling, and all routes share a catalog-wide ceiling; permits cover
only the network stream, never tool execution, so a child can delegate without
holding a nested model lease.

### OpenAI endpoints and the Responses API

Direct OpenAI endpoints are supported through the built-in `openai` preset,
and OpenAI-compatible endpoints continue to work through custom provider
presets. The built-in stays on Chat Completions as the broad compatibility
default. Set `wire_api = "responses"` on a custom row or top-level settings
when the selected endpoint has qualified Responses support.

A custom row may also carry `reasoning_effort = "high"` (or any level the
serve accepts) as the endpoint's own default — it folds into the resolution
chain like the row's other fields, so selecting the row is enough; no global
knob or per-session `/effort` needed. `KIN_REASONING_EFFORT` remains the
higher-precedence one-run override, including for an Outpost job that selects
an explicit effort. On the Responses wire a set effort does
double duty: the request carries
`reasoning: {"effort": ..., "summary": "auto"}`, and the `summary` opt-in is
what makes a thinking-by-default serve (DS4, some vLLM Responses adapters)
stream reasoning deltas at all. Without it, such a serve emits **zero events
for the entire reasoning phase**, and Kin's stream-stall watchdog
(`KIN_STREAM_STALL_TIMEOUT`, default 120s) will kill a perfectly healthy
multi-minute think as a stalled connection — then retry it, doubling the load
on the endpoint. If you point a Responses row at a local reasoning serve, set
`reasoning_effort` on the row.

Kin follows OpenAI's [Responses migration
model](https://developers.openai.com/api/docs/guides/migrate-to-responses) but
keeps conversation ownership local:

- Every request sends `store = false`; Kin never uses
  `previous_response_id`, server Conversations, or background execution.
- Kin asks for encrypted reasoning and stores the exact typed terminal
  `response.output` items in the existing journal history field. Function
  results are `function_call_output` items keyed by the response `call_id`.
- Same-endpoint Responses replay is verbatim, including encrypted reasoning.
  A Responses endpoint change, Chat↔Responses move, or provider change rebuilds
  neutral text/calls/results/images and drops server item ids, encrypted
  reasoning, and endpoint-specific metadata.
- Refusals remain visible assistant output. Reasoning items remain journaled for
  exact replay but are omitted from reconstructed UI transcripts, matching
  Anthropic thinking blocks.

The supported core is streaming text/refusals/reasoning, function tools
(forced, parallel, and strict schemas), usage/model/incomplete/failure state,
JSON-schema structured output, image inputs, and multimodal function results.
This iteration deliberately excludes hosted tools, remote MCP tools through the
Responses service, background responses, server-side conversations, and other
server-managed state. Kin's own MCP and local tools remain available through
ordinary function calls.

The implementation uses only the public `AsyncOpenAI.responses` SDK surface
(`openai>=2.43.0`). Kin continues to own schema normalization, neutral history
conversion, media policy, journals, permissions, and tool lifecycle behavior;
it does not vendor OpenAI-generated types or vLLM protocol models. See the
[typed streaming guide](https://developers.openai.com/api/docs/guides/streaming-responses)
and [decision 0141](https://github.com/kinra-ai/kin/blob/dev/docs/decisions/0141-openai-responses-local-replay.md).

## vLLM wire behavior

This is an explicit compatibility profile, not the current Kinra fleet
default. The connection commands and custom-provider row live in [Connect a model and
first run](/docs/kin/getting-started/first-run/#alternative-connection-methods).
At runtime, a vLLM endpoint follows the heuristic above: with both identity
axes unset, a bare URL uses Anthropic Messages; explicit `provider = "openai"`
selects Chat; explicit `wire_api = "responses"` selects Responses and bypasses
the heuristic. `model = "default"` is the wire-side sentinel—vLLM resolves it
to the live `served-model-name`, and Kin surfaces that resolved id in the
welcome banner and top bar.

vLLM 0.25.1 is Kin's first qualified self-hosted Responses implementation.
The on-demand `task live-vllm-responses` gate requires explicit
`KIN_BASE_URL` and `KIN_MODEL`, then checks `/version` for that exact release as
qualification evidence, then exercises typed text/reasoning, strict single and
parallel calls, structured output, two-turn local replay, journal resume, and
model/usage reporting. Vision runs only when the operator declares the served
model capable with `KIN_VLLM_RESPONSES_VISION=1`. `/version` never participates
in runtime backend selection; Kin does not sniff hostnames, model ids, or server
versions. See vLLM's [OpenAI-compatible server
guide](https://docs.vllm.ai/en/v0.25.1/serving/openai_compatible_server/) and
[Codex/Responses integration](https://docs.vllm.ai/en/stable/serving/integrations/codex/).

The context window starts from a conservative default and is refined from the
server's `/v1/models` metadata on the first turn; set `KIN_CONTEXT_WINDOW` to pin
it. For a protected endpoint, that metadata request uses the same authentication
and custom headers as the active inference client; a separate unauthenticated
probe is not required. Discovery reads the selected model's entry and normalizes
the wire fields to Kin's semantics:

- shared total-context limit: `max_model_len` (vLLM), `context_length`
  (OpenRouter / DS4-style), and the common compat alias `context_window`;
- maximum input limit: Anthropic's official `max_input_tokens`;
- maximum output limit: Anthropic `max_tokens` / OpenRouter-style
  `max_completion_tokens` or `top_provider.max_completion_tokens`;
- exact Responses counting: `capabilities.responses_input_tokens = true`.

A shared context limit constrains input + requested output + Kin's safety
margin; `max_input_tokens` independently constrains the prompt, so the
admissible prompt budget is the strictest applicable limit. The server's
maximum output capability is metadata only — Kin keeps its configured/requested
output reservation. The entry whose id matches the selected model is used; a
listing with exactly one entry is also trusted when the id differs (a
single-model server — an aliased vLLM `served-model-name` or LM Studio —
answers every request with that model), but with multiple entries and no exact
match there is no fallback. Probe failures or malformed metadata silently
keep the conservative default. An explicit `context_window` /
`KIN_CONTEXT_WINDOW` (or an explicit model-window mapping) pins the numeric
budget; Kin still reads metadata for optional capabilities. An automatically
discovered value is live server state, not a saved pin: resuming the
conversation rechecks `/v1/models`, while an explicit value remains fixed
across resume.

The Responses backend calls `POST /v1/responses/input_tokens` before every
model round only when that capability is proven by the selected model entry
(or when using OpenAI's own endpoint). Counting sends the exact prompt-bearing
wire fields—input items, instructions, tools, tool choice, reasoning, and
endpoint extensions—through the SDK's typed `responses.input_tokens.count`
resource. The result drives the same `ensure_model_round` proactive-compaction
path as Anthropic Messages. Missing metadata, an unknown third-party endpoint,
or an unsupported count route returns `None`, preserving provider admission
and the generic one-shot context-rejection recovery instead of guessing with a
client tokenizer. A server can implement this cheaply by rendering and
tokenizing without reserving inference capacity or building KV; Kin counts
every round because tool results can change prompt size sharply between rounds.

Sampling remains server-side on the Anthropic wire.

## Paddock and llama.cpp Responses

Paddock-managed llama.cpp is also qualified for Kin's text-only Responses
path. Select it as an OpenAI custom provider and opt into `responses` on the
provider row:

```toml
# ~/.kin/settings.toml
provider_preset = "kin-one-llamacpp"
max_tokens = 131072
context_window = 262144
compact_threshold = 0.45
enable_thinking = true

[[providers]]
id = "kin-one-llamacpp"
label = "kin-one · llama.cpp"
provider = "openai"
wire_api = "responses"
base_url = "http://model-host:5467/v1"
model = "laguna-s-2.1"
models = ["laguna-s-2.1"]
default_model = "laguna-s-2.1"
notes = "Text-only OpenAI Responses wire."

[providers.model_windows]
"laguna-s-2.1" = 262144
```

`enable_thinking = true` supplies Laguna's normal reasoning posture; you do
not need to set `/effort` before using the model. The optional picker exposes
the custom OpenAI-compatible `auto` / `on` / `off` vocabulary for a live
override.

The on-demand `task live-llamacpp-responses` gate defaults to this endpoint and
model. The 2026-07-22 qualification exercised typed text and reasoning, strict
single and parallel function calls, `text.format` JSON Schema output,
same-endpoint typed-item replay through a reasoning → tool round, journal
resume, and terminal model/usage reporting. Paddock preserves ordered
Responses items and function `call_id` values while adapting llama.cpp's
Chat-backed translator; it also enables the installed reasoning parser for
Kin's reasoning request and normalizes the private GGUF path back to the public
model id.

This is a local compatibility boundary, not hosted OpenAI state. The qualified
llama.cpp build emits plaintext `reasoning_text` plus an empty
`encrypted_content` placeholder. Paddock accepts that pair for same-endpoint
local replay but rejects a non-empty hosted encrypted envelope, which is not
portable to this endpoint. The worker remains text-only and does not add
stored responses, Conversations, background execution, hosted tools, or
server-side response retrieval. Kin continues to own the journal and tool
execution.

### Laguna S 2.1 on vLLM

Laguna is an explicit compatibility profile, not a model-name heuristic. Give
the endpoint a custom provider row and select the Anthropic Messages wire so
Kin retains the model's signed thinking blocks across tool rounds:

```toml
# ~/.kin/settings.toml
provider_preset = "laguna"
model = "default"
context_window = 262144
max_tokens = 131072
compact_threshold = 0.45
enable_thinking = true

[[providers]]
id = "laguna"
label = "Laguna S 2.1 (staged serve)"
provider = "anthropic"
base_url = "http://model-host:8000/v1"
model = "default"
reasoning_profile = "poolside"
```

The profile changes only Poolside-specific request shaping:

- `/effort max` and `/effort off` map to
  `chat_template_kwargs.enable_thinking=true/false`. Kin does not send
  Anthropic-native `thinking` or `output_config` fields to this serve.
- Reasoning is retained in full for history/cursor copy. The collapsed TUI row
  does no body rendering; an expanded row refreshes at most four times per
  second and shows a bounded head/tail preview.
- The 131,072-token output cap is a reservation, not merely a generation
  limit. With the recommended 262,144-token NVFP4 context, Kin admits at most
  129,024 prompt tokens and compacts at 45% (117,964) in this example.
- Before every model round, the Anthropic token counter checks the complete
  wire request. Older completed turns may compact normally, including a
  completed prefix that remains too large under the recent-turn keep policy.
  The current user input always stays verbatim and is refused intact if it
  cannot fit; only a completed tool continuation may use whole-history
  emergency compaction.
- The compaction call itself is capped at 4,096 output tokens and sends
  `enable_thinking=false`, so summarizing a long trace cannot start another
  open-ended reasoning run.
- Forced structured-output calls also send `enable_thinking=false`. They are
  schema-only control-plane requests; leaving Laguna thinking enabled can end
  the response after reasoning without emitting the selected tool.
- Laguna S 2.1 is text-only, so the Poolside profile predeclares nested
  tool-result images unsupported instead of sending a synthetic PNG probe on
  every new Kin process. Canonical image bytes remain available to a separate
  semantic vision route; Laguna receives the normal stable text reference.
Tool-argument validation is *not* part of the profile, though a compatible
serve is what motivated it: on **every** backend, syntactically malformed,
non-object, or JSON-Schema-invalid tool arguments return an error to the model
before any permission prompt or tool execution.

For the NVFP4 checkpoint, Poolside currently recommends vLLM 0.25 or later,
the 256K configuration, temperature 0.7, top-p 0.95, `max-num-seqs=32`, and a
quantization-matched 15-token DFlash draft. The DGX Spark recipe is:

```bash
export CUTE_DSL_ARCH=sm_121a
export MAX_JOBS=4

vllm serve poolside/Laguna-S-2.1-NVFP4 \
  --speculative-config '{"model":"poolside/Laguna-S-2.1-DFlash-NVFP4","num_speculative_tokens":15}' \
  --enable-auto-tool-choice \
  --tool-call-parser poolside_v1 \
  --reasoning-parser poolside_v1 \
  --served-model-name poolside/Laguna-S-2.1-NVFP4 Laguna-S-2.1 default \
  --default-chat-template-kwargs '{"enable_thinking":true}' \
  --override-generation-config '{"temperature":0.7,"top_p":0.95}' \
  --max-num-seqs 32 \
  --max-model-len 262144 \
  --gpu-memory-utilization 0.85 \
  --host 0.0.0.0 --port 8000
```

The three served names preserve Kin's `default` alias while making the short
and canonical identities discoverable. Do not add `min_p` or `logit_bias` to
this speculative configuration; Poolside notes that vLLM rejects them there.
Keep `MAX_JOBS=4` for a cold FlashInfer cache on a 128 GB unified-memory host.
Run `task live-laguna` for a safe qualification that exercises count-tokens,
replays any signed reasoning the model emits, performs synthetic tool-result
replay, forces schema output, and checks thinking-off without executing a real
tool. Max mode may choose a zero-token private budget; set
`KIN_LAGUNA_REQUIRE_SIGNED_REASONING=1` to require the live gate to observe a
signed block. See Poolside's [Laguna S 2.1 model card](https://huggingface.co/poolside/Laguna-S-2.1),
[NVFP4 deployment notes](https://huggingface.co/poolside/Laguna-S-2.1-NVFP4),
and [release post](https://poolside.ai/blog/introducing-laguna-s-2-1).

### The `default` alias pattern

If you register a stable alias for "whatever model is currently loaded" on your local server (e.g. vLLM's `--served-model-name default Ornith-1.0-35B …`), hardcode that alias in settings:

```toml
# ~/.kin/settings.toml
base_url = "http://model-host:8000/v1"
model = "default"
```

The harness sends `model="default"` verbatim on the wire (so the server keeps routing to the active model across swaps) and surfaces the **resolved** name — the canonical first `served-model-name` entry — in the welcome banner and the top bar (left cell, beside `⭘ kin`). Sources of the resolved name:

1. A `GET /v1/models` probe on session start (silent-degrade; transport errors and empty listings are no-ops).
2. `response.model` piggybacked off each completed turn (so a mid-session server swap self-corrects on the next turn without any restart).

The two paths dedup against a per-session cache — a stable server stays quiet. For Anthropic-direct or OpenAI-direct (`base_url` unset) with `model = "default"`, the banner shows the literal `"default"` (real Anthropic / OpenAI have no such alias — the alias only makes sense for local serves that route it).

## Swapping at runtime

Use `/model` inside the session to change the model without restarting:

```text
/model Qwen/Qwen3.6-35B-A3B   # swap to this model id
/model glm-5.2                # cross-provider swap — Z.ai (Anthropic wire, Bearer)
/model                        # open the cross-provider picker
```

### Keys are entered where you need them

A key is stored **per-provider** in `provider_keys[<id>]`; each provider keeps
its own key, so switching between Z.ai and MiniMax never overwrites the
other's. There are two entry surfaces, both committing through the same locked
settings transaction:

- **At swap time.** Picking an unkeyed provider from either `/model` form
  opens a one-field masked key prompt right there; enter saves the key under
  that connection's id and completes the swap in one motion.
- **In Model configuration.** `/models connections` (or the `/providers <id>`
  deep link) manages every connection's credential — replacement, removal,
  and extra headers — behind Save & apply.

### `/model` swaps anywhere — a missing key is one field away

- **Bare `/model`** opens a picker with a `✓ key` badge on every provider that has a key on file (green for a stored per-provider key; dim when it resolves only via an env var or the shared `api_key`). Providers with no resolvable key show a `needs key` hint; selecting one opens the key prompt, and a saved key swaps immediately.
- **`/model <id>`** swaps silently when the key chain resolves (`provider_keys[id]` → matching vendor/generic env → shared `api_key`). If nothing resolves, the same key prompt opens; esc cancels the swap and leaves the current backend untouched.

### The picker (`/model` bare)

Bare `/model` opens a keyboard-driven picker spanning **every** provider — your favorites first (a `★` group), then the built-in presets, your custom `[[providers]]` endpoints, and the active endpoint's live `/v1/models` listing. Arrow keys move, `enter` picks, `f` toggles a favorite (persisted to `model_favorites` in your global settings), `esc` cancels. Each row shows a `✓ key` badge (or a `needs key` hint) so you can see at a glance which providers are ready. Picking a model on a keyed provider does a live cross-wire swap: the harness rebuilds the conversation history into the target wire's native shape and re-points the backend. Picking an unkeyed provider opens the one-field key prompt; the saved key lands in `provider_keys[<id>]` through the hub's locked transaction and the swap completes.

### Inline `/model <id>` (no picker)

`/model <id>` swaps in place. The cross-provider path resolves the key for the target preset from `provider_keys[id]`, a matching environment variable, or the shared `api_key`; if any of those resolves, the swap is silent. Direct OpenAI uses `OPENAI_API_KEY` then `KIN_API_KEY`, while direct Anthropic uses `ANTHROPIC_API_KEY` then `KIN_API_KEY`—neither can accidentally borrow the other vendor's key. Compatibility/custom presets retain the broad historical env chain because their wire does not identify the credential issuer. If no key resolves, the inline key prompt opens: enter saves the key under the row's id and completes the swap, esc cancels and keeps the current backend. The `[1m]` suffix on Z.ai 1M-tier model ids (`glm-5.2[1m]`) is stripped before the catalog lookup so the plain id matches. History is stored in provider-native shapes; the cross-provider path converts it for you.

### Where the keys live

```toml
# ~/.kin/settings.toml
provider_preset = "zai"            # active preset (built-in id or [[providers]] row)
model = "glm-5.2"                  # default model for the active preset
```

```toml
# ~/.kin/credentials.toml — kin writes this file (0600) for you
[provider_keys]                    # one key per provider id — the canonical store
zai = "sk-..."
minimax = "sk-..."
```

```toml
# ~/.kin/settings.toml (continued)

[provider_tiers]                   # model entitlement / subagent capacity tier
zai = "lite"
minimax = "plus"
```

`/models` writes these tables when you paste a key and select a plan (its
`/providers [id]` deep link lands on the same Connections form); the save is one
locked transaction that merges stored secrets, so adding MiniMax never clobbers
Z.ai. Nothing else writes them — `/model` records only the active
`provider_preset` / `model`. The tier also sizes ordinary
subagent capacity for the curated cloud presets. The factory's key-resolution
chain (`make_backend` in `src/kin/harness/backends/factory.py`) is:
caller-supplied `api_key` > `provider_keys[<active_preset_id>]` (via
`_preset_key`) > preset-appropriate environment key(s) > top-level `api_key`.
For direct OpenAI that environment order is `OPENAI_API_KEY` then
`KIN_API_KEY`; for direct Anthropic it is `ANTHROPIC_API_KEY` then
`KIN_API_KEY`. Other presets retain `ANTHROPIC_API_KEY` → `KIN_API_KEY` →
`OPENAI_API_KEY` for compatibility. The active preset is the most
specific selector — a stray `ANTHROPIC_API_KEY` baked into an image can't
silently shadow the Z.ai key you stored in `provider_keys.zai`. See the
[settings-toml reference](/docs/kin/reference/settings-toml/) for the full
precedence + GLOBAL_ONLY rules.

Saved sessions keep the non-secret provider id, endpoint, model, and auth style,
but never the key. On resume, Kin uses that identity to reattach the matching
current `provider_keys[id]` secret while leaving the saved wire configuration
unchanged. Pre-id journals use an exact, unique endpoint/auth match; a custom
provider whose URL changed is not allowed to send its new key to the old URL.

The mode controls and the rest of the in-session verbs live in [Slash commands](/docs/kin/guide/slash-commands/).

## Sampling

Sampling knobs apply on the **OpenAI-compat wire only**. The backend forwards
`temperature` and `top_p` as standard params and rides `top_k` on `extra_body`.
`enable_thinking` normally follows that OpenAI-compatible path; the explicit
Poolside profile is the exception and sends the same chat-template field over
Anthropic Messages:

| Knob | Env | Effect |
|---|---|---|
| `temperature` | `KIN_TEMPERATURE` | 0 = greedy, higher = more random |
| `top_p` | `KIN_TOP_P` | Nucleus sampling |
| `top_k` | `KIN_TOP_K` | Top-k sampling (via `extra_body`) |
| `enable_thinking` | `KIN_ENABLE_THINKING` | Qwen/Poolside chat-template soft switch; `false` hard-disables thinking |

> **Warning**
>
> The Anthropic Messages backend sends **no** per-request sampling — current Opus / Fable models reject `temperature`, `top_p`, and `budget_tokens` with a 400. This includes a vLLM serve reached through the heuristic. Set sampling on the server instead, e.g. with vLLM's `--override-generation-config`. That is also the only place to set server-only knobs like `min_p` or `presence_penalty`.

See [settings.toml reference](/docs/kin/reference/settings-toml/) for the sampling block and the per-knob scope.

## Priority-scheduling passthrough (vLLM)

When several kin surfaces share one self-hosted vLLM (interactive sessions, subagent fan-outs, [headless](/docs/kin/guide/headless/) cron runs), vLLM can schedule them by priority: serve with `--scheduling-policy priority` and requests carrying a lower `priority` integer are scheduled earlier, with genuine KV-swap preemption of lower-priority work when demand spikes (vLLM ≥ 0.23). kin passes this through as `request_priority` (`KIN_REQUEST_PRIORITY` env / settings key) → `extra_body["priority"]` on every request — e.g. `KIN_REQUEST_PRIORITY=10` on your cron jobs keeps them behind your live sessions.

Two hard caveats, both server-side facts rather than kin choices:

- **Serve-gated, and it fails loudly.** vLLM *errors* on any request carrying a non-zero priority when the serve doesn't run `--scheduling-policy priority`. That is why kin's default is **absent from the request** — not `0` — and why you should only set this knob against a serve you know runs the priority policy.
- **OpenAI-compat wire only.** The Anthropic `/v1/messages` protocol has no priority field (verified against vLLM's `entrypoints/anthropic/protocol.py`), so a bare self-hosted URL selected through the [`KIN_BASE_URL` heuristic](#the-kin_base_url-heuristic) cannot carry it — select OpenAI Chat deliberately for a qualified priority-enabled vLLM. DS4 does not implement this vLLM request field at all; keep it unset on the current fleet. Kin doesn't fake priority on either unsupported wire, and the [per-run token budget](/docs/kin/guide/headless/#fleet-safety-the-per-run-token-budget) remains the provider-independent safety mechanism.

## Strict decoding (schema-valid tool calls)

Structured decoding makes the *server* grammar-constrain tool-call arguments to the tool's JSON Schema, so malformed args can't reach the harness. What you actually get depends on the endpoint and the call shape (probed against the fleet's vLLM 0.23.0 — `research/probe_strict_decoding.py` has the receipts):

| Path | Guarantee today |
|---|---|
| **Forced tool calls** (the workflow `schema=` structuring call — `force_tool_call`, both wires) | On vLLM, a named/forced `tool_choice` is **grammar-constrained unconditionally** — on `/v1/chat/completions` *and* the `/v1/messages` bridge. The OpenAI-compat wire additionally ships a strict-normalized schema + `strict: true` so OpenAI-proper-class endpoints enforce too. |
| **The streaming tool registry** (`tool_choice: "auto"`) | Opt-in via `strict_tools` / `KIN_STRICT_TOOLS=1` (OpenAI-compat wire only). Each tool spec is normalized (`additionalProperties: false`, all-required, optionals as `["type","null"]` unions) and stamped `strict: true` at request-build time. vLLM 0.23 ignores `strict` on `auto` (no-op); vLLM ≥ 0.24 and OpenAI-proper grammar-enforce it. |

The Anthropic Messages wire never sends `strict` — real Anthropic rejects unknown tool fields, and the vLLM bridge already constrains forced calls without it. Either way the harness keeps its client-side net: malformed argument JSON still parses to `{}` (the tool then reports the missing args), and the workflow `schema=` result is still validated against your original schema — server enforcement makes that net near-never-fire; it doesn't replace it.

A tool schema that can't be normalized losslessly (an open `additionalProperties`, an unsupported keyword like `uniqueItems`, an empty `enum`) is shipped unmodified without `strict` — better unenforced than a 400. All bundled kin tools normalize cleanly.

Google's [DiffusionGemma 26B A4B](https://huggingface.co/google/diffusiongemma-26B-A4B-it) is a discrete diffusion language model (dLLM): it denoises 256-token canvas blocks in parallel rather than generating one token at a time. vLLM shipped first-class day-0 support in June 2026 (the [vLLM blog post](https://vllm.ai/blog/2026-06-10-diffusion-gemma) walks the architecture — speculative-decoding path, entropy-bound sampler, dynamic per-sequence causal attention). It serves from a normal `vllm serve` with the model id set.

A few practical notes for `kin`:

- **Wire — prefer the heuristic default (Anthropic), with `openai` as the fallback.** vLLM shipped day-0 dLLM support in June 2026 via the speculative-decoding path (originally OpenAI-side), and recent builds (≥ `v0.22.1rc1`) wire it through `/v1/messages` too — **live-verified on this serve with `curl`**: both wires return HTTP 200 for the dLLM model id, and streaming works on both with the expected chunk cadence. For dLLMs that think, the Anthropic wire is actually the **better** default: the response is typed `thinking` + `text` blocks with a signature, so the harness's existing reasoning handling carries the thinking blocks across turns replay-safe the same way it does on Qwen / MiniMax / Z.ai serves. The OpenAI wire puts reasoning in a bare `choices[0].message.reasoning` field with no signature — ephemeral. If your serve only exposes the OpenAI-side dLLM integration (an older vLLM build, or a custom router), set `KIN_PROVIDER=openai` to escape the heuristic; the `curl` probe at the end of this section is the verification.

    The full canonical recipe:

    ```bash
    KIN_BASE_URL=http://model-host:8000/v1 \
    KIN_MODEL=google/diffusiongemma-26B-A4B-it \
    uv run kin
    ```

    `KIN_MODEL=default` also works — it resolves to `RedHatAI/diffusiongemma-26B-A4B-it-FP8-dynamic` on this serve (the first alias in `/v1/models`). The short alias `dgemma-26b` is a cleaner handle if you've registered it; `google/diffusiongemma-26B-A4B-it` is the upstream HF id.

- **Reasoning budget — give it space to think.** DiffusionGemma is a reasoning model (the model card describes configurable `<|think|>` control tokens). A small `max_tokens` will cut the response off mid-thought (`stop_reason: "max_tokens"`, `content: null`) — the same shape the harness already salvages on Qwen via `loop._salvage_reasoning`. Set `KIN_MAX_TOKENS` generously (8k–32k is fine; the dLLM's per-block throughput is high enough that the wall-clock cost stays modest).

- **Sampling — server-side, not request-side.** DiffusionGemma uses an **Entropy-Bound (EB)** sampler rather than temperature / top_p / top_k — the model walks canvas positions from most to least confident, accepting tokens until accumulated entropy exceeds a fixed budget, and commits the block once the argmax has been stable for N steps. Configure EB on the vLLM serve (`--override-generation-config` or the model card's recommended `DiffusionSampler` settings), not in the request body. The harness's existing "sampling is OpenAI-compat-only" precedent works in your favor here: any `KIN_TEMPERATURE` / `KIN_TOP_P` / `KIN_TOP_K` you set are silently ignored by the dLLM backend, so they don't break anything — they just don't do anything.

- **Streaming cadence.** The vLLM dLLM sampler reports `num_sampled = 0` during the denoising loop and only emits `num_sampled = 256` on the **commit** step. From the client's perspective you'll see 256-token chunks landing on the SSE stream rather than token-by-token deltas — the harness consumes them transparently; no `KinApp` changes needed. Total throughput is roughly 5–6× a comparable autoregressive model (~1,000 tok/s on H100, ~1,290 tok/s on H200 for FP8), so the user-perceived cadence is "small delay, then a 256-token block snaps in, then another, then another" rather than a per-character trickle.

- **Wire-support probe.** A quick `curl` against your serve confirms whether dLLMs are wired through `/v1/messages`:

    ```bash
    curl -sS http://model-host:8000/v1/messages \
      -H 'content-type: application/json' \
      -d '{"model":"dgemma-26b","max_tokens":256,"messages":[{"role":"user","content":"hi"}]}' \
      | head -c 400
    ```

    Expected on a healthy dLLM serve: HTTP 200 with a typed `thinking` block + `text` block in the `content` array. A `404 NotFoundError` means the serve doesn't have a dLLM loaded (check `curl /v1/models`); a 4xx validation error means the dLLM is loaded but isn't wired through the Anthropic adapter on this vLLM version — escape with `KIN_PROVIDER=openai`.

## Cost display

Cost display is **opt-in** (default off): kin's primary endpoint is local vLLM (no cost at all), and the subscription providers (MiniMax/z.ai) bill flat-rate, so per-token cost is noise for most sessions. Turn it on with **`/cost on`** (persists to `~/.kin/settings.toml`) or `KIN_PRICING=1` / `pricing_enabled = true`. `/cost` (bare) reports the current state and, when on, the running total; `/cost off` turns it back off and resets the running total.

Once on, `/tokens` shows a running USD cost total alongside the per-component token breakdown, and the StatusBar carries a recessive `$0.04` cell on its right edge (next to the mode badge). The figures come from a local, stdlib-only pricing table in `harness/pricing.py` — keyed by `(provider, sanitize_model_id(model))` so the SAME model-id drift tolerance that protects the wire-side display covers the pricing boundary too.

Coverage and rates (2026-07 snapshot, per **million** tokens, USD):

| Provider | Models | Notes |
|---|---|---|
| Anthropic | Opus 4.6+ / Sonnet 4.x / Haiku 4.x | Incl. prompt-cache read + write rates. The kin default `claude-opus-4-8` is the **current** generation ($5/$25), not the legacy 4.1 premium tier. |
| z.ai | GLM-4.5 / 4.5-Air / 4.6 / 4.7 / 5.x | Fleet default `glm-5.2` at $1.40/$4.40. `glm-4.5-flash` deliberately omitted (not on the current z.ai pricing page). |
| MiniMax | M3 / M2.x | List price (≤512k input tier for M3). |
| OpenAI | gpt-4o / 4.1 family | When kin is pointed at real OpenAI. |

Unknown / unpriced serves — your local vLLM (`Qwen3.6-27B`, `Ornith-1.0-35B`, `dgemma-26b`, the `default` alias), a model id the table doesn't carry — surface **nothing** rather than a misleading `$0.00` (free by omission, never a fake zero). Add a row to `PRICES` in `harness/pricing.py` when a new fleet model lands; the rate is a snapshot, not a live scrape, so revisit the cited provider page when prices change.

### Accuracy boundary (cache-write approximation)

The kin `Usage` shape collapses Anthropic's per-component tokens into three fields: `prompt_tokens = input + cache_read + cache_creation`, `cached_tokens = cache_read`, `completion_tokens = output`. The `cache_creation` count — which Anthropic bills at ~1.25x the input rate — is folded into `prompt_tokens` and indistinguishable from a fresh input token at this layer. `compute_cost` therefore charges cache-creation tokens at the **input rate**, a documented underestimate. The bound is tight in practice: cache writes happen once per cache lifetime (5 min / 1 h), not per round, so for an agentic loop with many cache hits the missed 25% surcharge applies to a small slice of total tokens.

### Edge cases

- **Provider swap** (`/providers` or `/model` cross-provider): the running total **resets** because the new provider's per-token rate differs from the old one (Opus $5/M → GLM $1.40/M makes pre-swap cents incomparable to post-swap). The pre-swap total rides forward in a system-note footnote so you can still see what the prior provider cost.
- **Resume**: usage events aren't durably stored in the journal, so a resumed session starts the counter at $0 and accumulates only the new turns. `/tokens` notes this honestly.
- **Opt-in / off**: `/cost off` (or `KIN_PRICING=0` / `pricing_enabled = false`) disables the accumulator entirely (no table lookup, no display cell) and clears the running total. This is also the **default** — nothing to opt out of unless you've already turned it on. See [`pricing_enabled`](/docs/kin/reference/settings-toml/#observability).

## Effort picker

<a id="effort-picker"></a>

`/effort <level>` flips a per-serve reasoning-depth knob live, and `/effort` (bare) opens a picker that's curated for the active backend — not a server-cap probe. The vocabulary differs by wire *and by endpoint* — the two Anthropic-compat presets don't implement Anthropic's `output_config.effort` at all, so they get their providers' real knobs instead:

| Active backend | Vocabulary | Wire knob |
|---|---|---|
| `AnthropicBackend` (real Anthropic, vLLM Anthropic serve) | `low` / `medium` / `high` / `xhigh` / `max` | `output_config.effort` |
| `AnthropicBackend` against the MiniMax preset | `adaptive` / `enabled` / `disabled` | `thinking.type` (M2.x silently ignores `disabled`) |
| `AnthropicBackend` against the Z.ai preset | `max` / `high` | top-level `reasoning_effort` (the only two operative values on GLM-5.2; `max` is Z.ai's default) |
| `AnthropicBackend` with the Poolside profile | `max` / `off` | `extra_body.chat_template_kwargs.enable_thinking` |
| `OpenAIBackend` against OpenAI / OpenRouter | `low` / `medium` / `high` | top-level `reasoning_effort` |
| `OpenAIBackend` against Z.ai OpenAI-compat (GLM-5.2+) | `auto` / `max` / `xhigh` / `high` / `medium` / `low` / `minimal` / `none` | top-level `reasoning_effort` |
| `OpenAIResponsesBackend` against a custom Responses endpoint (`wire_api = "responses"`) | `max` / `xhigh` / `high` / `medium` / `low` / `minimal` / `none` (pass-through; the serve owns which levels it accepts) | `reasoning: {"effort": ..., "summary": "auto"}` |
| `OpenAIBackend` against a vLLM OpenAI-compat serve (Qwen-style, Chat wire) | `auto` / `on` / `off` | `extra_body.chat_template_kwargs.enable_thinking` (`auto` = unset) |

The picker treats the `(default)` row as "leave it to the server / wire defaults" — pick it on cold start to wipe the override. Picking always invalidates the prompt cache, so inline `/effort <level>` pops a confirmation modal first; the picker is the confirmation by itself.

The setter is **HUMAN-ONLY** — the model has no `set_effort` tool and reaches the knob only through `propose_settings effort=...`, which still requires your approval before anything changes. Resuming from the journal restores the active level; the active wire knob stays put so a `resume_config()` against `anthropic.py` / `openai.py` re-points it on reload. See [Slash commands](/docs/kin/guide/slash-commands/#effort) for the `/effort` row + the cache-invalidation caveat.

## First-party presets

For MiniMax and Z.ai, the harness ships a curated preset that bundles the
URL, model id, and auth style. Set `provider_preset = "minimax"` in
`~/.kin/settings.toml` (or `KIN_PRESET=minimax uv run kin`, or
`uv run kin --preset minimax`) and the harness picks the right wire and
auth header automatically. The `/models connections` hub in the TUI walks the
same catalogue interactively and writes the choice back to settings;
`/providers [name]` is its deep link.

See [Provider presets](/docs/kin/guide/providers/) for the catalogue, the Connections
section, custom endpoints, and the GLOBAL_ONLY rationale.
