The loops
Two questions decide which one
They are separate, and both matter. What must the loop be able to do? Harnesses differ in real ways, and the differences are published as capabilities:Where the tools come from, and the two limits
Every harness now declaresinjected_tools: true, and these families reach every published loop:
- The team tools.
send_to_agent,wait_for_agents,list_agents,board_readandboard_write. A coordinator runs on any harness. - MCP servers declared on the agent, with their tools named
<server>.<tool>as always. - Connected accounts and the tools they contribute, named
<connector>.<tool>, plus the tools for asking for a connection the agent does not yet have. - Social publishing and your apps, including the tools an app declares for itself.
Vetta’s own built-ins do not travel that second road yet — web search and fetch, image and
video generation, skill disclosure,
publish_file and the
managed browser are offered on pi and vetta only. A claude_code or hermes agent has its own
CLI’s web, file and search tools in their place; write your prompt against those, not against
publish_file.tools policy still governs all of it, by the same names. A tool set to deny is never offered to the model on any harness: on these it is not merely refused when called, it is left out of the list the agent is given, so the model never learns it exists.
Two limits are real on this road, and both are consequences of approvals: false:
The values, per harness
Harness capabilities is the matrix: which of the four each
loop declares, what an idle session holds, which pairings are refused and where — and the one
catalogue entry that is still optimistic.
400 naming the field — and where the configuration can be judged earlier, the refusal happens earlier still, at the agent save. It is never a quiet best effort: an ask toolset that silently became allow would remove your human-in-the-loop gate with nothing to notice it by.
What should an idle session cost? This is the execution field, and it is not the same question as who operates the loop:
A long-running task that spends most of its life waiting — for a schedule, for a person, for a webhook — costs very differently on
isolate than on sandbox. That is usually the deciding factor for long-horizon work, and it is why the field is published rather than left for you to infer from a bill.Reading the catalogue
The set is not fixed in the documentation, because a harness needs an adapter compiled in and an environment can have a different set. Read it at runtime:runnable answers for the deploy you are talking to. A harness can be published — so a picker can show it, disabled — while this environment cannot run it, in which case starting a session on it is a 501 naming the field rather than a failure part-way through a turn.
Full field reference: GET /v1/harnesses and client.harnesses.
Changing it
harness is versioned with the rest of the agent, so changing it creates a new agent version and a session pins the version it started on. An in-flight session is never re-hosted onto a different loop mid-run.
The prompt, the skills, the budget and the window all carry across unchanged — that is the point of the seam.
The tools carry across too, and that is newer than the rest of this page. Moving an agent onto a harness that runs a CLI in a machine of its own no longer withdraws its team tools, its MCP servers or its connected accounts — they are served over a session-scoped tool endpoint instead of handed to the loop. What does not carry across is anything that needs a capability the destination does not declare: an ask in your toolset, or structured_output_required. Because harness is create-only you meet that at the agent save, where the refusal names the field to change.