Skip to content

Many backends, one agent

Most humanize backends do not talk to a model provider. They drive a supported coding agent under its existing login, through a built-in adapter or the Agent Client Protocol, so no provider API key is needed for them.

The exception ships inside it: DeepSeek Harness arrives as an SDK-backed agent and uses its own DeepSeek provider credentials because it has no subscription login to reuse.

what a flow may ask an agent for
13 of 13
backenddriven throughhardest effortmid-turna shapea goaltrace
claude · its own ladder, hardest first
ultracodemaxxhighhighmediumlow

A model narrows this to the rungs that model takes, in the ladder's own order, and it is the backend that says which — asked, not written down.

driven through one process, held open across its turns and spoken to a line at a time

trace read-back humanize can collect this backend’s session log into a Chrome trace

skills it would load its own, and the project’s

“ultracode” is “xhigh” with the turn opted into orchestrating a fleet of its own. It is real, undocumented, and no listing the CLI answers with will ever name it — so humanize writes it down.

An agent is four things

A backend, a model, an effort, and the account its turns run as. Two agents of one spelling are two agents, so a flow of an actor and a reviewer at one configuration is what it says it is.

What it runs is discovered for the account

A model id is not usually a fact that keeps. Coding agents add models, and which of them an account may name is the account's business. Wherever a backend can report its catalogue, humanize asks it under that account and keeps the answer:

  • It is asked as the account whose it would be — under that account's own credential paths and variables, and without the ones its backend would otherwise take an account from. Which is exactly how a turn of that account is run. Two accounts of one CLI are two catalogues.
  • An account on somebody's endpoint is asked the endpoint, not the CLI. A coding agent handed a base URL answers with the models it ships — it has nothing that goes and looks at the other end — so that answer is wrong the moment it is given, and refreshing it changes nothing. GET {base}/v1/models under the account's own credentials is what a turn could actually name: for a gateway that routes to several clouds, that means route-prefixed ids like azure/anthropic/claude-haiku-4-5 rather than the vendor's own spelling.
  • Kept with the account, so taking the account away takes its catalogue with it.
  • Never asked at a prompt. Asking is a coding agent starting up, or a request to somebody's endpoint, and neither is a thing to do while a list is being drawn; reading what was kept costs one file read and reaches nothing.
  • An account is asked as soon as it is made, that being the first moment there is anything to ask. A backend that would not answer leaves the account made — an account whose models are not known yet is one to ask again, not one that failed.

The endpoint is asked only where the account sets the variable that backend routes its turns by, and only where its ids are ids that CLI could name. pi, opencode, mimocode and zcode name a model provider/id out of several endpoints at once, so one endpoint's ids would be a list they cannot use, and their own answer is already the account's. Where the variable is unset, where the endpoint will not answer, or where what comes back is not a list of models, the CLI is asked exactly as before: an endpoint that is down is one to ask again, not a reason to have no catalogue.

DeepSeek Harness and Qwen Code cannot list their models dynamically. Their adapters provide small advisory catalogues instead: the official DeepSeek adapter's current models, and the models Qwen Code ships pointed at. Those lists make initial setup possible; they are not proof of what an account or compatible endpoint will accept — which is why an account of either that names an endpoint is asked the endpoint instead.

To a catalogue that was discovered, nothing is added. Claude Code may report a custom alias without proving the account can run it, so the alias is preserved exactly as that account supplied it rather than another model entry being manufactured.

The efforts are a vocabulary, so they are written down

An effort is the backend's own word for how hard to think, and a ladder keeps in a way a catalogue does not: xhigh means the same thing next release. So the ladders are written down, hardest first, and a model narrows its backend's ladder to the rungs that model takes — in the ladder's own order, and to the whole of it where the backend said nothing about that model.

Three of them are worth knowing about:

  • A rung a backend takes but does not document is written down as one. Claude Code's ultracode is xhigh with the turn opted into orchestrating a fleet of its own. No listing the CLI answers with will ever name it, so a model asked about would otherwise lose it.
  • Width is not depth. Kimi Code's max is one agent and swarmmax is the same thinking at the width of a fleet — a second thing to say about a turn rather than a harder version of the first, so it is chosen beside the effort rather than among the rungs.
  • One ladder can hold two vocabularies. ZCode's models do not agree on what an effort is: the ones that take a thinking budget answer max, high, low and nothink, and the ones that only take thinking-or-not answer enabled and disabled. Both are rungs of the one ladder, and a model narrows it to the half that model speaks.

Driven through whatever each one actually offers

A backend is driven through its command line where that can express what an agent is configured with, and through the app server it serves its own client from where it cannot. A model, an effort, a mode or a goal that has no flag is a setting of a session there — and asking the model for it in the prompt is not the same feature. A turn that has to stay open to be talked to is such a case: a command line run per turn has ended by the time there is anything to say to it.

Where a server is needed it is started only when a turn first needs one, so a flow that needs none starts none, and one server serves more than one session of its agent. Where the backend takes one turn at a time, that is one server per agent and calls on it are serialized: two turns interleaved on one stream would each take the other's answers. Codex and ZCode run the turns of separate conversations at the same time, so their drivers sort the one stream instead of reading it in turn — an answer goes to whoever made the call it is numbered for, and a session's events go to the turn running on that session, so the second session of an agent does not wait out the whole of the first. On Codex a conversation belongs to the server that opened it, and a session is opened on a server no turn is running on: an agent runs one server for a flow that takes its turns in sequence, however many sessions it opens and drops, and one apiece for a fleet that works its sessions at once.

Skills are read where that CLI reads them

Nothing is asked of the CLI. Starting one costs seconds, so the skills are found where that CLI looks for them — its own home, the shared directory more than one of them has agreed to read, the project's own — and each is named as the CLI names it.

This is a reading and nothing else. What you installed is yours: humanize does not rewrite, override or switch off any of it, and offers no way to. What a flow brings is different — its own skills are mounted into the directory that backend reads for the length of a session, and taken away with it. A backend that reads none is a turn run without them rather than a run that will not start.

Adding a CLI of your own

Anything speaking the Agent Client Protocol is a backend from the moment it is written down: a name and the command that starts it. The protocol says nothing about which models such an agent runs or how hard it may be asked to think — both are the agent's own — so one rung is offered and none is sent.

Where the detail is

Released under the Apache-2.0 licence.