Runtimes

Agent CLIs and API providers

How a run actually executes — local CLI sessions, API providers, model selection, and version pins.

Every agent has a Provider, chosen on its Settings tab, and that choice decides how its sessions actually run: as a local process on this machine, or as an HTTP call to a model API.

The four local CLIs

Claude Code, Cursor, Antigravity and OpenCode all run the same way: as a headless CLI session on this host, using whatever subscription or account that CLI is already signed in with on this machine — not an API key TaskTrooper holds. There is no "enabled" flag for any of them; the switch is simply whether the binary is on PATH (or the path named by its own environment variable — CLAUDE_CODE_BIN, CURSOR_AGENT_BIN, ANTIGRAVITY_BIN, OPENCODE_BIN). A provider whose binary is missing is still selectable on an agent, but every run on it fails with one sentence naming where it can actually run — never a silent fallback to some other provider or endpoint.

CLIProvider valueBinary
Claude Codeclaude_codeclaude
Cursorcursor_agentcursor-agent
Antigravityantigravityagy
OpenCodeopencodeopencode

Because these are local processes rather than endpoints, none of them can be "connected", "tested", or used for embeddings the way an API provider can — there is no base URL and no key to test. They are simply available on an agent, or not, and every run's deadline (run_timeout, one hour by default for all four) is what catches a session that never exits on its own.

How a run is started

A run on any of the four CLIs is started headless (claude -p, cursor-agent -p --force, agy -p, opencode run) with the task's prompt, inside the same workspace directory the run's own tools are scoped to — a board run's task checkout, or nothing at all for a call that touches no repository. No workspace is ever a hard refusal rather than a guess: running inside the server's own directory would let a session edit the code hosting it.

TaskTrooper's own board tools reach the session over MCP, at a fresh /mcp endpoint the server mounts for the run, bound to 127.0.0.1 and guarded by a token minted for that one run (or, in chat, that one turn) and revoked when it ends. The session sees these as mcp__tasktrooper__<name> tools, and its config is written with --strict-mcp-config — which also means neither a repository's own checked-in .mcp.json nor your personal Claude Code settings under ~/.claude are ever loaded into a TaskTrooper session. Nothing there was chosen for this product, and letting it in would mean a board task silently ran with hooks, hooks hosts, or hosts, permission rules you never intended for it. If you want an agent to reach your own MCP servers, add them explicitly instead.

If you connect your own MCP servers to TaskTrooper, those are the "other direction" — a session calling out to servers you configured — and are namespaced mcp_<server_id>_<tool_name> rather than mcp__tasktrooper__<name>; see MCP servers for that half.

What the child process can and cannot see

A local CLI session runs as a child process with a deliberately narrow environment: an allow-list that excludes DATABASE_URL, SERVER_API_KEY and the secrets-encryption key, plus an explicit pass-through of CLAUDE_CONFIG_DIR, CLAUDE_CODE_OAUTH_TOKEN and your proxy variables. ANTHROPIC_API_KEY is deliberately never forwarded, even if you have one set on this machine for something else — handing a Claude Code session a key it was never given would move it onto metered billing instead of the subscription it is actually signed in with.

Model selection

Each agent picks its Model and, optionally, a stronger Model (heavy) independently of its provider — see Your own agents for the field. On a local CLI, both may be left empty: the session then runs with no --model flag at all, which is what a subscription user typically wants, since the CLI itself picks whichever model the account defaults to. None of the four CLIs validate the model name you give them, so a typo does not fail fast — it costs the run.

API providers

For agents that are not backed by a local CLI, TaskTrooper talks to a model API directly:

ProviderNotes
OpenAI (GPT)Requires an API key
Anthropic (Claude)Requires an API key; native API, not the CLI
Google GeminiRequires an API key; with none, falls back to Vertex AI via GCP application default credentials
GroqRequires an API key; free-tier friendly, fast tool-calling models
Custom (OpenAI-compatible)Any OpenAI-compatible /v1 endpoint — LM Studio, Ollama, vLLM, OpenRouter, or your own IP

These are configured on the LLM Connection settings page (Settings → LLM Connection): add a named endpoint with its base URL, optional API key, default model and timeout, and it becomes selectable as a Provider on any agent once connected. Keys are stored encrypted at rest, never on argv or in a config file you'd check in. You can add as many named OpenAI-compatible endpoints as you like — one entry per LM Studio instance, per OpenRouter account, and so on — alongside the four built-in native providers.

Version pins

A handful of environment variables can be set on a task's session to steer a version manager the session already trusts toward the right toolchain version for that repository — GOTOOLCHAIN, NODE_VERSION, PYTHON_VERSION, RUBY_VERSION, JAVA_VERSION, FLUTTER_VERSION, RUST_TOOLCHAIN, RUSTUP_TOOLCHAIN, plus a handful of terminal/locale variables (CI, TERM, NO_COLOR, FORCE_COLOR, TZ, and others) and your own TT_-prefixed names. TaskTrooper does not itself parse a repository's .tool-versions, go.mod or .nvmrc — it passes these allow-listed names through to the child process environment, and whatever version manager the repository already relies on (asdf, nvm, rbenv, and so on) resolves the actual version from those pin files the normal way. Anything outside this allow-list is refused rather than passed through silently, since a name that points at a path, a library, a config file or a proxy could change what the session actually runs, not just how it reports itself.

Executor configuration

Each of the four local CLIs has its own small config section (claude_code, antigravity, cursor_agent, opencode in the server's configuration) covering things like the binary path, the run timeout, and — for Claude Code specifically — the maximum turns per session (100 by default), which CLI settings sources are loaded (project,local by default — your own user-level ~/.claude is deliberately excluded, for the same reason a repository's .mcp.json is), and how many board sessions may run concurrently on that CLI at once (see Usage limits and concurrency for what that cap is for). These are host-level settings, not per-agent ones — every agent on a given CLI shares them.

Chat vs. board sessions

A board task and a chat conversation both run on the same executor once an agent is assigned, but they carry the run differently. A board run is a fresh session scoped to the task's own workspace. A chat is multi-turn: the CLI's own conversation id is kept alongside the session, and each new message resumes it (--resume <id> on Claude Code) rather than starting over — so an agent chat that has been open for days does not replay its whole history into the model on every message, and the CLI's own in-conversation state (files it opened earlier in the thread, for example) carries forward naturally.

Follow-up steps inside one run

A single board run is not always one CLI invocation end to end. Steps that happen after the main session finishes — fixing a failed build, settling open acceptance criteria, answering a review verdict — resume that same CLI session with just the new instruction, rather than replaying the whole flattened conversation back into a brand-new one. This is also why a run's --resume target and its MCP token are tracked per run rather than per message: a follow-up step is a continuation of the same session, holding the same context the main step already built up, not a fresh one paying to rediscover it.

Edit this page on GitHub