> ## Documentation Index
> Fetch the complete documentation index at: https://vetta.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Harnesses

> The agent loops an agent can run on — what each one can be admitted for, and what an idle session on it costs.

<Info>A **harness** is the agent loop itself: how context is managed, how tools are chosen, how a long task is decomposed and resumed. It is one field on an [agent](/docs/api/agents), and nothing else about your call changes when you change it. Unlike [models](/docs/api/models), this catalogue is a constant of the deploy — a harness needs an adapter compiled in — so it does not change without a release.</Info>

<Note>The loops themselves are described in [Harnesses](/docs/concepts/harnesses) — what each one is, and
which of them holds a sandbox between turns. This page is the wire contract.</Note>

## The harness object

<ResponseField name="base" type="string">The value you pass as `harness` on an agent.</ResponseField>
<ResponseField name="label" type="string">A display name, for a picker.</ResponseField>
<ResponseField name="location" type="string">`hosted` runs on our infrastructure. `local` runs on your own machine against your own logged-in account, and is not available to a hosted session.</ResponseField>
<ResponseField name="billing" type="string">`usage` is metered to your credit balance. `account` bills the account you are already signed in to, so it does not touch your balance.</ResponseField>
<ResponseField name="status" type="string">`ga`, or `preview` when the loop runs but its contract may still move.</ResponseField>
<ResponseField name="execution" type="string">Where the loop runs, and therefore **what an idle session costs**. `isolate` runs in the session itself and holds no sandbox between turns. `sandbox` runs the agent on a micro-VM, which the session holds across turns. `local` runs on your machine. This is a different question from `location`, which is about who operates it.</ResponseField>
<ResponseField name="runnable" type="boolean">Whether a session on it runs **on this deploy**. A harness whose agent is a process also needs a base image, which is per-environment — so the same harness can be runnable in one environment and not another, and this field answers for the one you are talking to. A published harness with `false` is refused when the session starts, with `501` naming the field — so a picker can show it disabled rather than let someone discover it by trying.</ResponseField>
<ResponseField name="capabilities" type="object">What the loop can be admitted for. See below.</ResponseField>

## Capabilities

Harnesses differ, and the differences are load-bearing. Read them before you create an agent.

<ResponseField name="approvals" type="boolean">The loop can hold a tool call and wait for a decision on [tool confirmations](/docs/api/sessions). A harness with `false` cannot serve a toolset whose permission is `ask`.</ResponseField>
<ResponseField name="structured_output" type="boolean">The loop can enforce `structured_output_required`. **`vetta` publishes `true` here and cannot yet produce one** — the loop has no submit path, so the pairing is admitted by every gate and fails at the turn. Use `pi` for a typed answer. See [Known gaps](/docs/concepts/harness-capabilities#known-gaps).</ResponseField>
<ResponseField name="streaming_deltas" type="boolean">The loop emits incremental `message.delta` [events](/docs/api/events). With `false` you still get `message.completed` — the answer arrives whole rather than as it is written.</ResponseField>
<ResponseField name="injected_tools" type="boolean">The tools **Vetta** contributes reach the loop's model — the [team tools](/docs/team/delegation), an agent's [`mcp_servers`](/docs/api/mcp), a [connected account's](/docs/api/connections) tools, social publishing and apps — all governed by the agent's `tools` policy under the same names. It does not say by which road: a loop Vetta hosts is handed them for the turn, while an agent that is a CLI process in a machine of its own is given a session-scoped tool endpoint to call and keeps its own file, shell and search tools alongside ours. Three limits apply on the second road: Vetta's own built-ins (web search and fetch, media generation, skill disclosure, `publish_file`, the managed browser) do not travel it, and — both following from `approvals: false` on those harnesses — a tool whose permission is `ask` is left out rather than gated, and `wait_for_agents` does not pause the turn (the coordinator parks once it goes idle and still opens its threads). See [Harness capabilities](/docs/concepts/harness-capabilities#platform-injected-tools).</ResponseField>

<Note>Two configurations are refused earlier than the rest, at `POST`/`PATCH` `/v1/agents`, because the agent's own configuration settles them: `multiagent` on a harness with `injected_tools: false` (the coordinator would have no `send_to_agent`) and a non-empty `mcp_servers` on the same (nothing would surface them). Both answer `400` `validation_failed` naming the field to change. **No published harness declares `false`,** so neither refusal fires today; they stand as the guard for a base added later.</Note>

<Note>All four are on the object. `injected_tools` is the one that decides whether an agent can lead a [team](/docs/team/overview), because the team tools arrive the same way as [MCP servers](/docs/api/mcp) and [Connections](/docs/api/connections). The per-harness values for all four are in [Harness capabilities](/docs/concepts/harness-capabilities).</Note>

<Note>A session asking for a capability its harness does not declare is refused **at start** with a `400` naming `harness`. It is never a quiet best effort that looks like it worked — an `ask` toolset that silently became `allow` would remove your human-in-the-loop gate with no event and no error to notice it by.</Note>

## GET /v1/harnesses

Every harness, including those in preview and those not yet runnable.

```bash theme={"system"}
curl https://api.vetta.sh/v1/harnesses \
  -H "authorization: Bearer sk_live_..."
```

```json theme={"system"}
{
  "data": [
    {
      "base": "pi",
      "label": "Pi",
      "location": "hosted",
      "billing": "usage",
      "status": "ga",
      "execution": "sandbox",
      "runnable": true,
      "capabilities": {
        "approvals": true,
        "structured_output": true,
        "streaming_deltas": true,
        "injected_tools": true
      }
    }
  ],
  "has_more": false,
  "next_cursor": null
}
```

The list is a hand-written table and fits one page, so `has_more` is always `false` — but the envelope is the same as every other listing, so one loop reads both this and [models](/docs/api/models).

## GET /v1/harnesses/\{id}

One harness, for when you already hold a base and want to know what it can be admitted for.

A base the catalogue does not publish is `404` — including an id reserved in a picker but not yet runnable as a harness.

## Choosing one

Two questions decide it, and they are separate:

* **What must the loop be able to do?** If your toolset uses `ask`, you need `approvals`. If you require a structured answer, you need `structured_output`. If the agent leads a team, declares MCP servers, or acts through a connected account, you need `injected_tools` — every published harness has it, so in practice this question is about the two limits that come with `approvals: false`.
* **What should an idle session cost?** A harness with `execution: "isolate"` holds no sandbox between turns. One with `"sandbox"` holds a micro-VM, because the agent *is* a process running on it.
