Skip to main content
An agent is what acts inside an environment: it works a task through the environment’s capabilities and produces the answer that gets graded. Concretely it’s a model wrapped in a harness - the loop that feeds the model observations and turns its output into actions. In the framework an agent is anything callable as await agent(run), where the run is the live handle for one task: its prompt, its connection to the environment, and the trace it fills. Because an environment only exposes capabilities, the agent isn’t baked in - use a built-in agent for a standard model, or bring your own harness for a custom loop.

Built-in agents

The SDK ships one agent per major provider, reached two ways:
  • create_agent(model) - the preferred path. It selects the matching provider agent for a model id and routes every call through the HUD gateway.
  • a provider agent directly (e.g. ClaudeAgent(ClaudeConfig(...))) - the same class constructed yourself, for full config control or to call the provider with your own key instead of the gateway.
The HUD gateway is an OpenAI-compatible endpoint (settings.hud_gateway_url, default https://inference.hud.ai) that fronts every provider behind your single HUD_API_KEY, so you switch between Claude, GPT, and Gemini by name alone, with unified tracing. create_agent accepts any id the gateway knows (claude-..., gpt-..., gemini-...); extra kwargs pass through to the agent’s config. Built-in agents are catalog-driven: each run they read the environment’s manifest, open the capabilities they support, build the matching provider tools, and loop against run.prompt_messages. Declaring a capability on the environment is enough; you never wire tools.

Provider agents

Each model maps to a provider agent - the class that speaks that provider’s API. Construct one directly to set its full config or use your own provider key:
Each config lives in hud.agents.types. OpenAIChatAgent speaks the OpenAI Chat Completions API, so it points at any compatible server (vLLM, a local model) via base_url. ClaudeCLIAgent and CodexCLIAgent run the claude and codex exec CLIs over an ssh capability, against the env’s filesystem. SSH-only runs support POSIX and Windows workspaces; ClaudeCLIAgent computer use over an rfb capability currently requires a POSIX workspace. Every knob (model, max_steps, timeout_seconds, tool_timeout_seconds, system_prompt, citations_enabled, stop_on) lives on the config; __call__(run) takes only the run. OpenAIConfig.max_output_tokens and GeminiConfig.max_output_tokens are unset by default, so each request gets the model’s own output limit. Set them to cap output per turn. Gemini counts thinking tokens toward the cap, so a low cap can truncate a thinking model before it answers. GeminiConfig.temperature, top_p, and top_k are unset by default, so each request uses the model’s own sampling; Gemini 4 models ignore them. GeminiConfig.thinking_level accepts minimal, low, medium, or high, subject to the model (Gemini 4 Argon rejects minimal); unset uses the model’s default. GeminiAgent streams each turn. For Gemini 4 long decode, set max_output_tokens above 65,536 and install google-genai 2.30.1 or later; the turn then runs past the per-request output cap and is recorded as one turn. Otherwise a turn that reaches the cap ends the run with stop_reason="length". Keep include_thoughts on for long-thinking runs, since without it the model sends nothing while it thinks and a long think can hit the stream’s first-chunk deadline. OpenAIConfig generates a stable prompt_cache_key for the agent and sends it with every Responses request. Pass your own key when multiple agent instances should share the same cached prompt prefix. timeout_seconds bounds the complete agent phase. For provider tool agents, tool_timeout_seconds bounds each complete SSH-backed tool call, including multi-operation editor calls. It is unset by default except on ClaudeConfig, where it defaults to 120 seconds. A timeout is returned to the model as a tool error so the agent can continue. The CLI agents do not apply this setting because each SSH process is the complete CLI agent, not one tool call.

Create agent

The create_agent function resolves model to a gateway agent type and returns that provider agent wired to the gateway. Extra kwargs forward to the matched config (the *Config for the resolved type), with model filled in, gateway=True set, and model_client left unset; the agent routes through the gateway at runtime, even when that provider’s own key is also set. Passing an agent type itself (create_agent("claude_cli")) keeps that config’s default model. This keeps the config serializable for HostedRuntime. Constructed directly (ClaudeAgent(ClaudeConfig(...))), an agent uses the provider key when one is set and the gateway otherwise; gateway=True on the config forces the gateway there too. api_key and base_url are rejected here; instantiate the provider agent directly for custom/BYOK credentials. Passing completion_kwargs only works when model resolves to OpenAIChatAgent, since completion_kwargs lives only on OpenAIChatConfig. A model id or agent type name maps to one of the registered agent types (AgentType): For a provider key instead of the gateway, construct the agent directly.

Agent

Every agent implements the Agent base class (hud.agents.base): one abstract method, __call__(run: Run) -> None. An agent drives the live Run to completion, filling run.trace in place; the graded answer is run.trace.content. An agent is stateless per run - everything comes from run - so one instance drives many concurrent rollouts. Every agent also carries an AgentConfig as agent.config (timeout_seconds, model, …), typed by its parameter: Agent[CodexCLIConfig]. agent.dump() serializes that config to JSON and AgentClass.load(data) rebuilds the agent from it; HostedRuntime sends the registered agent type plus dump(). A subclass that overrides __init__ calls super().__init__(config).

Running an agent

Run a task with an agent two ways. Programmatically, pass the agent to Task.run or Taskset.run with a runtime:
From the CLI, hud eval takes a task source and an agent name (claude, claude_cli, codex_cli, openai, gemini, openai_compatible); see running an eval for the walkthrough and the CLI reference for the full flag set.

Bring your own harness

Any loop or framework can be an agent: subclass Agent, drive the environment off the run, and write the final answer to run.trace.content (what gets graded). Since this is outside the standard workflow, the seam, the Run object you work with, the step types you record, and worked examples live in Extending HUD. To run an agent end to end see running an eval; for where each rollout runs see runtimes, and for the Run an agent fills see types.