Docs
Models
Which model answers, how each call reaches it and who pays, every model setting, Auto, and picking one from a chat.
A model is named as provider and name, "anthropic/claude-sonnet-5". An agent,
a job or a single step can each name its own, so a cheap step can ask a small
model while the rest of the agent asks a large one.
Which model answers
The first of these that is set wins.
| Set with | |
|---|---|
| One step | model on work.model() or work.agent() |
| A pick for one job | /model <name> for <job> in a chat |
| One job | model in defineJob, or in a markdown job's frontmatter |
| A pick for one chat | /model <name>, or /model auto, in that chat (chat turns only) |
| The agent | model in defineAgent, or the quick model when it has auto: true (see Auto, below) |
| Every agent | model.defaultModel in settings, which npx chloe setup writes |
An agent with none of these is refused as it loads. A model given as an AI SDK
model, like anthropic("claude-opus-5-5"), always goes to that provider's own
API, on whatever key that package reads.
How a call reaches it, and who pays
There are six routes. Each call goes by the first route in
model.preferredRoute that can carry the model's provider and is set up on this
machine. A route with nothing set up is skipped.
| Route | Goes through | Carries | Paid by | What to set up |
|---|---|---|---|---|
claude |
the claude command |
Anthropic models | your Claude subscription | install Claude Code and sign in |
codex |
the codex command |
the OpenAI models your plan allows | your ChatGPT plan | install Codex and sign in |
antigravity |
the agy command |
Google models | your Google AI Pro plan | install Antigravity CLI and sign in with Google |
opencode |
the opencode command |
whatever it is signed in to | that account | install opencode and sign in |
direct |
the provider's own API | Anthropic and OpenAI | your key, per call | model.keys.anthropic, model.keys.openai |
gateway |
an AI gateway | any provider it carries | its key, per call | model.key |
The default order is the one above, so a subscription is used before a key that
charges per call. To pay with a key instead, put direct first:
model: { preferredRoute: ["direct", "claude", "codex", "antigravity", "opencode", "gateway"] },
which is what setup writes when you give it a key.
One model can go by another route than the order says by naming it at the end:
"openai/gpt-5.5 via gateway". That works anywhere a model is named, in an
agent, a job or a pick from a chat, and the route is used even when it is not
set up, so the run fails saying what is missing rather than charging another
account.
A key is a secret, so it goes in .env, and the config hands it over:
model: { keys: { anthropic: process.env.ANTHROPIC_API_KEY } }, or
model: { key: process.env.CHLOE_MODEL_KEY } for a gateway.
| Setting | Default | What it controls |
|---|---|---|
model.defaultModel |
none | The model of an agent that names none. |
model.preferredRoute |
["claude", "codex", "antigravity", "opencode", "direct", "gateway"] |
The order routes are tried in. |
model.keys |
none | Anthropic's and OpenAI's own keys, for direct. |
model.key |
none | The gateway's key. |
model.gatewayUrl |
the Vercel AI Gateway | Any gateway that speaks the OpenAI chat shape. |
model.models |
ask each route | The shortlist a person may pick from in a chat. Empty offers everything this machine can reach, often hundreds: each command route and the gateway are asked what they run when chloe starts and when the config changes. |
model.program |
claude, codex, agy, opencode |
Where each command is, when it is not on the path under that name. |
model.timeLimit |
10 |
The most minutes one model call may take, on any route. A call still going is stopped, its program killed, and the run fails saying so, instead of holding its job. 0 is no limit. A whole step and one tool call have a limit of their own: see Step, model, agent. |
model.judgeModel |
"anthropic/claude-sonnet-5" |
Who marks an eval. |
model.quickModel |
the route's own | What Auto hands an easy message, and who names a new conversation on the dashboard. Empty takes the quick model of the route the agent's own model goes by: the claude command's haiku, codex's gpt-6-luna while it lists it. The direct, gateway, antigravity and opencode routes have none, so name one here to use Auto on them. With none, the agent's own model names conversations. |
A call that fails in a way a fresh try might fix, such as the provider cutting the stream or dropping the connection, is tried again twice, a few seconds apart, before the run fails. So is an answer on the claude or opencode route that writes tool calls out as text instead of making them. A refusal in words, a missing credential and a usage limit fail at once. The direct and gateway routes also retry a busy or failing provider four times within each call.
No model and no key yet
OpenRouter has free models with no card: make a key at
openrouter.ai/keys, set model.gatewayUrl to
https://openrouter.ai/api/v1/chat/completions, and ask for openrouter/free,
which picks a free model that can call tools. It is limited to a few dozen calls
a day, a different model may answer each time, and the runs cost 0 in the
record.
A job whose steps are all code needs no model at all, which is why setup runs one before it asks for one.
What the command routes do
Each one runs the command once per step, with the command's own tools switched off, so every tool call is chloe's and is written down.
- claude hands the tools over as real tools. It is never given
ANTHROPIC_API_KEY, so a key in.envcannot quietly pay for a subscription run. - antigravity describes the tools in the prompt too, like codex: the CLI
refuses every tool of its own in headless mode, and real MCP tools are not
used (the CLI reads its MCP servers from the account's own settings, which is
the person's, not chloe's). The prompt goes in over stdin (
agy -phangs in a pipe), and a custom agent written into the run folder makes chloe's instructions the model's system prompt, which also keeps the CLI's own instructions out of the call (about 2k tokens a step, not 12k). The CLI still offers the model its own sixty tools and sometimes the model uses one, which headless refuses and which ends the turn, and its own call format is the same{"tool": ...}shape the prompt asks for: the route is not reliable yet. - opencode hands the tools over as real tools too. For each step it starts
an opencode server of its own, reachable from this machine only, and closes it
after. It needs opencode 2 or later, and adds a second or two to each step.
The conversation so far goes over as real past turns (opencode's
session/import), so past calls are calls to the model and not text it might copy; only the newest turn is sent as the prompt. That endpoint is marked experimental, and an opencode without it gets the whole conversation as one block of text instead. - codex describes the tools in the prompt and reads the calls back from the reply, which is a little less reliable and costs about 500 tokens a step. It adds about 7,000 tokens of its own instructions to every call.
A run on the claude route shows what it would have cost on the API, as a price,
not a charge. opencode names the price its own list gives the model: a charge
on an account that pays per call, only a price on a plan. codex and antigravity
name no price, so what their runs cost is unknown, and a budget or a web
chat's dollar limit cannot be kept on them.
Auto
An agent with auto: true in defineAgent, or a chat picked onto it with
/model auto, asks the quick model about each message before it answers:
does this need the agent's own model? The quick model sees the message and
the last few in the conversation, never the agent's tools, and answers in a
line. An easy message is then answered by the quick model, and anything else
by the agent's own, in the same way as without Auto.
export default defineAgent({
model: "anthropic/claude-opus-5-5",
auto: true,
identity: "Answers the shop's customers.",
});- The quick model is found from the route the agent's own model goes by, so
the same account pays for both: Opus through the claude command gets the
command's Haiku.
model.quickModelnames one instead. - A conversation that went to the agent's own model stays there for the hour, without asking again.
- When the quick model fails, or answers in words it cannot be read from, the agent's own model answers.
- The run's first line says which model it went to and why, and what asking cost is in the run's cost.
- Jobs are not on Auto: each keeps the model it names.
The owner can change an agent's model, and switch Auto on or off, from the
model on the agent's page on the dashboard, or with
POST /api/agents/<id>/own-model and { "model", "auto" }. Either writes its
agent.ts, and leaves a model the file says as code, like
anthropic("claude-opus-5-5"), for the file.
On a subscription, a quick model saves the plan's limits and time, not money.
Picking one from a chat
/models lists what may be picked, with a button each on Telegram. A model
that more than one route here can run is listed once for each, the others as
<name> via <route>, and so it is on the dashboard.
/model <name> picks one for that chat, /model <name> for <job> for one job,
/model auto puts that chat on Auto, and /model default takes a pick back, with the same for. A pick is a trial
and is not how an agent's model is set: what it runs on is model in its own
definition, and there is no pick for the whole agent. The run record still says
which model made each run.
Over the API: GET /api/models, and POST /api/agents/<id>/model with
{ "scope", "model" }, where scope is job:<id> or chat:<thread>,
"auto" puts a chat on Auto, and an empty model takes the pick back.