The runner reaches each provider through the CLI people already have, on the plan they already pay for. API keys are the fallback. One provider is fine, and tiers can mix: --models director=anthropic/opus,builder=openai/nano, or the same in .desk/project.json.
desk-run doctor prints, for each provider, whether the CLI is on PATH and works, whether it is signed in or a key is set, which path the runner will take, the models per tier, and the defaults it would choose for this machine. desk-run doctor --probe adds one short real call per reachable provider, which is the only way to catch a sign-in whose token has expired. desk-run providers prints the model table.
Every adapter exports the same thing: runCli({ model, system, prompt, images, cwd, timeoutMs }) and runApi(...), each returning { text, usage, costUsd, provider, model, via }. images is a list of PNG paths. usage is { input, output, cacheRead, cacheWrite } with input the uncached part, normalised to the shape the native desk.stop event carries (total added). runner/providers/index.mjs picks the path (CLI when signed in, else key), prices token counts when the provider reports no dollars, and adds modelId.
- CLI:
claude(Claude Code). Sign in withclaude auth login, checked withclaude auth status. - Key:
ANTHROPIC_API_KEY(andANTHROPIC_BASE_URLif set) through the Messages API withfetch. - Flags used:
claude -p --verbose --model <alias> --input-format stream-json --output-format stream-json --max-turns 1 --tools "" --no-session-persistence --setting-sources "" --system-prompt-file <file>. The user message goes in on stdin as one stream-json line with base64 image blocks before the text. The result event carriestotal_cost_usd,usageandmodelUsage. The runner removesCLAUDECODEfrom the child's environment so a claude started from inside a Claude Code session does not refuse to nest. - Tiers: director
opus, seniorsonnet, builderhaiku.fableis there for thebestpreset. The CLI takes the alias, the API takes the id. - Cost: dollars straight from the CLI. The API path is priced from the table.
- Verified run: two real Codex CLI jobs on 2026-10-19, including an OpenAI-only job signed off at 8.75 after two polish rounds with both sizes passing proof. Director
large, seniormini, buildermini. The earliernanobuilder job reached the cap under the bar and is preserved too. - CLI:
codex(Codex CLI). Sign in withcodex login, checked withcodex login status. A revoked token still reports "Logged in", only--probeor the first real call shows it. - Key:
OPENAI_API_KEYthrough the Responses API withfetch(instructionsfor the brief,input_imagedata URLs for screenshots). - Flags used:
codex exec --json --skip-git-repo-check --ephemeral -s read-only -C <cwd> -m <model> -c model_reasoning_effort="medium" -o <file> [-i <png>]... -. The prompt is read from stdin. Codex has no system prompt flag, so the brief goes first in the prompt under its own heading.--jsonprints JSONL:item.completedwith anagent_messageitem carries the text,turn.completedcarriesusage(input_tokens,cached_input_tokens,output_tokens,reasoning_output_tokens),turn.failedcarries the error.DESK_OPENAI_EFFORTchanges the reasoning effort. - Tiers: director
large, seniormini, buildernano, which map to the frontier, workhorse and fast models the CLI lists. - Cost: tokens from the CLI, priced from the table.
- CLI:
agy(Antigravity CLI). Install from Google's CLI page, runagyto sign in, and check the available slugs withagy models. Google rejected individual-account sign-in through Gemini CLI 0.64.0 on 2026-10-19 and directed users to Antigravity. The runner checks Antigravity through its CLI without reading the credential store. - Key:
GEMINI_API_KEYorGOOGLE_API_KEYthroughgenerateContentwithfetch(systemInstructionfor the brief,inline_dataparts for screenshots). - Flags used:
agy --input-format stream-json --output-format stream-json --model <slug> --effort <level> --disable-slash-commands --print-timeout <duration>. Registered aliases pin the listed effort as well as the model. The complete role brief and job go on stdin as oneuserevent, under separate headings. Plain piped input alongside--printis ignored by Antigravity 1.4.2. The result event suppliesstatus,responseandusage; failures are rejected even if the process exits zero. - Workspace and screenshots: every call gets a temporary workspace with copies of the supplied PNGs. The host project is not the CLI's working directory. Antigravity reads images with
view_file(AbsolutePath) orread_file(Path), and the adapter verifies a completed read for every screenshot before accepting the response. The temporary files are removed after success or failure. Desks return HTML in their response. - Tiers: director
flash-high(Gemini 3.8 Flash High), seniorflash(Medium), builderflash-low(Low).argonselects Gemini 4 Argon High when the account has quota.proremains for plans offering Gemini 3.1 Pro and for the API.flash-literemains an API model. Checkagy modelsandagy -p /usagefor your account before choosing other slugs. - Cost: token counts from the CLI are priced where the table has a price. Argon's price is unknown, so its dollar cost is null and the handback marks the total incomplete. Argon is available through Antigravity, and is rejected on the Gemini API path with guidance to choose
google/proorgoogle/flash. - Verified runs: a Google-only job and a mixed OpenAI/Google job both passed at 8.75, after three and two polish rounds respectively, with both sizes passing proof. The Google-only job used explicit steering notes, retained with its artifacts. The API-key path has not finished an end-to-end job.
runner/models.json, with checkedAt. Each provider lists its CLI name, key variables, the three tiers and its models: an id for the API, the name the CLI takes, list prices per million tokens (input, output, and cacheRead and cacheWrite where published. The Anthropic rows match scripts/prices.json) and whether the model takes images. The default role per tier comes from skills/desk/desks.json, not from here. desk-run providers prints it.
| Provider | Tier | Alias | Model | List price in / out per 1M |
|---|---|---|---|---|
| anthropic | director | opus | claude-opus-5-5 | 4 / 20 |
| anthropic | senior | sonnet | claude-sonnet-5-5 | 2 / 10 |
| anthropic | builder | haiku | claude-haiku-5-5 | 0.10 / 0.50 |
| anthropic | (best) | fable | claude-fable-5-1 | 10 / 50 |
| openai | director | large | gpt-6-astra | 10 / 50 |
| openai | senior | mini | gpt-6.1-sol | 2 / 10 |
| openai | builder | nano | gpt-6-luna | 0.10 / 0.50 |
| director | flash-high | gemini-3.8-flash (High) | 0.75 / 3.75 | |
| senior | flash | gemini-3.8-flash | 0.75 / 3.75 | |
| builder | flash-low | gemini-3.8-flash (Low) | 0.75 / 3.75 | |
| optional | argon | gemini-4-argon (High) | unknown | |
| API / some plans | pro | gemini-3.1-pro-preview | 2 / 12 | |
| API | flash-lite | gemini-3.5-flash-lite | 0.30 / 2.50 |
Anthropic and OpenAI were checked on 2026-10-18 against their CLI and pricing tables. Google's API prices were checked on that date, and Antigravity slugs were checked with agy models on 2026-10-19. A full id also works in a spec (openai/gpt-6-luna), and an id the table does not list runs with its price unknown.
desk-run doctor and /desk:init pick the defaults from what is on the machine: Anthropic for every tier when claude is signed in, otherwise the strongest provider found for director and senior and the cheapest for builder. With no CLI and no key the runner cannot run a job, and the host agent plays every desk itself with the briefs as instructions, one model, no tiering, and the handback note says so.
studio is opus, sonnet, haiku. best is fable, opus, sonnet. thrifty is sonnet, haiku, haiku. Presets are Anthropic only. Mixing providers is done with --models or in project.json, where the presets map can be edited to anything.