Hosted vs bring-your-own harness for AI agents
In Vetta the harness (the agent loop) is the layer you pick and the runtime is always managed. When to keep the default harness, when to bring your own, and what each choice does to cost per completed task.
TL;DR
- Where an agent's loop comes from and what runs underneath it are separate decisions, and only the first one is yours to make.
- The harness is the agent loop: assemble a turn, call the model, parse tool calls, decide what carries forward. It is one field on the agent, and pi is the default.
- The runtime is the durable loop, scheduling, the budget gate, sandboxed computers and the ledger. It is never a parameter; using Vetta is the runtime.
- Bringing your own harness means running an external coding agent as a process in a micro-VM. Your tools, MCP servers and connections still reach it over a session-scoped tool endpoint, and your policy still governs them.
- Pick by two published facts, not by taste: which capabilities the loop declares (approvals, structured output, streaming) and what an idle session on it holds.
- Cost per completed task is decided across harness, tools and runtime together, which is why the runtime stays managed even when the loop is yours.
Two layers, one field
The question this post used to answer was "hosted runtime or bring your own runtime?" That framing is wrong for Vetta, and getting it right changes which decision you have to make.
Vetta is built in three layers. The harness is the agent loop: assemble a turn, call the model, parse the tool calls, decide what carries into the next turn. The tools are what the loop reaches for. The runtime is everything underneath: the durable loop and scheduler, model routing across completion windows, budget enforcement, sandboxed computers and the ledger. The How Vetta works page walks through all three.
Only one of those layers is yours to select, and it is the harness. It is a single field on the agent:
{ "name": "Reconciler", "model": "zai-org/GLM-5.2-FP8", "harness": "pi" }The runtime is not a parameter and there is nothing to select. Using Vetta's managed agents is the runtime. So the real choice is: run the default harness, or bring your own loop and run it on the same managed runtime?
What the runtime does regardless
Whichever harness you pick, the layer beneath it does not change. That is the point of making harness a field rather than a fork of the product.
- The durable loop. The runtime wakes on a trigger (a user message, a scheduled deployment, a tool result), takes one bounded turn, commits the transcript and cursor to durable storage, and sleeps. A crash loses at most one turn, never the run. See Runtime and durability and sessions.
- The budget gate. Every agent carries a mandatory USD budget, and every call is quoted against the cap before it runs. Over the cap, it is refused.
- Sandboxed computers. A disposable micro-VM per agent, with a shell, a filesystem, networking and a managed browser. A paused micro-VM meters its stored disk and no vCPU.
- Model routing and the completion window. One model, three lanes:
immediate,priority,loose. An unsupported lane is refused, never silently downgraded. See completion windows. - The ledger. Five-tier token accounting in integer micro-USD, per session and per agent.
The policy layer sits here too: every tool call resolves allow, ask or deny and is priced before it runs, on every harness.
The default harness: pi
pi is the default and the only harness with ga status today. It is a coding agent embedded in Vetta's composition, driving our sandbox tools directly. Because we assemble the turn, one of our tools is simply another entry in the toolset the model is offered, and per the harness capabilities matrix it declares all four published capabilities:
approvals: the loop can hold a tool call open and wait for a person. Required if any tool's permission isask, and required forask_operator.structured_output: the loop can enforcestructured_output_requiredand serve a typed delegation.streaming_deltas: the loop emits incrementalmessage.deltaevents rather than one wholemessage.completed.injected_tools: the platform's tools reach the model.
It also receives every one of Vetta's own built-ins: web search and fetch, image and video generation, skill disclosure, publish_file and the managed browser. Those are offered on pi and vetta only.
Creating an agent on it:
vetta agent create \
--name Refunder \
--model zai-org/GLM-5.2-FP8 \
--harness pi \
--budget-usd 50 --max-task-usd 5 --budget-period month \
--window immediate \
--system "You process refunds."If you omit --harness, you get pi.
Bringing your own harness
The other published loops are vetta, claude_code and hermes, all in preview. vetta is our own loop, held to a small measured core, and the only one that runs in the session itself. claude_code and hermes are the bring-your-own case: an external coding agent, run as a process in a micro-VM, driven by its own command line and carrying its own toolset.
Bringing your own harness does not mean bringing your own infrastructure. The process runs on the same runtime; the durable loop, the budget gate, the event log, the ledger and your tools policy are unchanged.
What changes is the road our tools take to reach the model. On pi we hand them into the turn. On a harness whose agent is a CLI in a machine of its own, the loop is given a session-scoped tool endpoint at the start of every turn, scoped to that session by a credential minted for the turn, and calls our tools over it alongside its own file, shell and search tools. You configure nothing. The team tools (send_to_agent, wait_for_agents, list_agents, board_read, board_write), your MCP servers, your connected accounts and your apps all travel that road, so a coordinator can run on any published harness and a team may mix harnesses freely.
Two limits are real on that road, and both follow from those harnesses declaring approvals: false:
A tool whose permission is
askis left out entirely, not gated. Nothing in that machine can hold a call open while a person decides, so a session whose toolset carries anaskis refused when it starts, namingharness.
wait_for_agentsdoes not pause the turn. A coordinator that fans work out parks once the turn goes idle. The cost is a few extra model calls, not a wrong answer.
There is a third: Vetta's own built-ins do not travel the second road. A claude_code or hermes agent has its CLI's own web, file and search tools instead, and a prompt that tells it to call publish_file will read as though the model ignored it.
Two questions decide it
Two separate questions decide the choice, and both are answered by published fields rather than inferred from a bill.
What must the loop be able to do? If your toolset uses ask, you need approvals. If you require a typed answer, you need structured_output. If you want the answer as it is written, you need streaming_deltas. Every published harness declares injected_tools, so in practice this question is about the limits that come with approvals: false.
What should an idle session cost? This is the execution field. isolate runs the loop in the session object itself, so a session between turns holds no machine and bills storage only. sandbox runs the agent as a process on a micro-VM, which the session holds across turns. For long-horizon work that spends most of its life waiting, this is usually the deciding field.
pi | vetta | claude_code | hermes | |
|---|---|---|---|---|
| Status | ga | preview | preview | preview |
| Execution | sandbox | isolate | sandbox | sandbox |
| Idle session holds | a micro-VM | nothing | a micro-VM | a micro-VM |
approvals | yes | no | no | no |
structured_output | yes | declared, not yet served | no | no |
streaming_deltas | yes | no | no | no |
injected_tools | yes | yes | yes | yes |
| Vetta built-ins offered | yes | yes | no | no |
Nothing here is a quiet best effort. A session that asks for a capability its harness does not declare is refused when it starts, with a 400 naming harness. An ask toolset that silently became allow would remove your human-in-the-loop gate with nothing to notice it by.
The catalogue is served. runnable is the one field that varies by environment, because a harness that runs a process also needs a base image built for that deploy:
const harnesses = await client.harnesses.list();
for (const h of harnesses.data) {
console.log(h.base, h.execution, h.capabilities, h.runnable ? "available" : "not on this deploy");
}A published harness with runnable: false is refused at session start with a 501 naming the field. The wire contract is GET /v1/harnesses.
The cost-per-completed-task angle
Vetta is sold on cost per completed task, and the thesis behind it is that the model is a commodity: everything around the model decides what a finished piece of work costs. That is why the runtime stays managed even when the loop is yours. The four levers that earn the efficiency all live beneath the harness seam:
- A micro-VM sandbox that pauses between turns, so idle bills storage only.
- An alarm-driven durable loop: wake, turn, commit, sleep.
- A three-tier completion window, chosen per task and fail-closed.
- Model choice plus sub-agent delegation, so the cheapest capable model does each unit of work.
Bringing your own harness keeps all four. What it changes is the cost profile of the loop itself: how much context it carries per turn, how often it calls the model, whether it holds a machine while it waits. Two of those are visible before you spend a cent: execution tells you what an idle session holds, and the capability rows tell you whether a coordinator on that loop pays a few extra model calls at every fan-out because wait_for_agents cannot pause the turn.
The rest you measure. harness is chosen at create and does not change on a live agent, so an A/B is two agents from the same .agent.yaml differing in that one field, compared with vetta agent spend <id> broken down by component. Prompt, skills, budget and window carry across unchanged; the ledger is per session. We publish our own numbers on the benchmark page.
Choosing
Keep the default harness when:
- any tool in the toolset is
ask, or the agent usesask_operator - you require
structured_output_requiredor a typed delegation - a Vetta built-in is load-bearing: web search and fetch, media generation,
publish_file, the managed browser - you want a running transcript with
message.deltaevents
Bring your own harness when:
- your team already lives in that coding agent and wants the same loop running unattended, under a budget
- every tool the agent needs is either its own or reaches it over the injected-tools road
- the toolset is
allowanddenyonly, with noask - the deploy you are on reports it as
runnable
Either way, the budget gate, the policy layer, the event log and the ledger are the same. There is no path that bypasses them, because there is no self-hosted runtime.
Where to go next
The concept page on harnesses explains what each loop is and where the tools come from on each; the capabilities matrix is the table above with the known gaps spelled out. To keep your existing framework and use Naïve's primitives from inside it, read Naïve inside your agent framework. For the whole stack, start with Introducing Vetta.
FAQ
- What is the difference between a harness and the runtime in Vetta?
- The harness is the agent loop: how a turn is assembled, how the model is called, how tool calls are parsed and what carries into the next turn. It is one field on an agent and you choose it. The runtime is the managed infrastructure under that seam: the durable wake, turn, commit, sleep loop, scheduling, model routing across completion windows, budget enforcement, sandboxed computers and the ledger. It is not selectable.
- What does bring-your-own harness mean?
- It means choosing a harness whose agent is an external coding agent run as a process in a micro-VM, such as claude_code or hermes, instead of the default pi loop. The runtime, budget gate, policy layer and event log are the same either way. Vetta's team tools, MCP servers and connected accounts reach that process over a session-scoped tool endpoint that is written into the machine at the start of every turn.
- Does bringing my own harness bypass budgets or policy?
- No. Budgets are enforced by the runtime before a call runs, and the tools policy governs every tool by the same names on every harness. On a harness that runs a CLI in its own machine, a tool set to deny is never even put in the list the agent is given.
- When should I keep the default harness?
- Keep pi whenever a tool in your toolset is set to ask, whenever you require structured_output_required, whenever you want incremental message deltas, or whenever a Vetta built-in such as web search, the managed browser or publish_file is load-bearing. Those capabilities are declared per harness, and a session that asks for one its harness does not declare is refused at start.
- How do I see which harnesses my deployment can run?
- Read GET /v1/harnesses or call client.harnesses.list(). Every entry carries base, status, execution, capabilities and a runnable flag that answers for the deploy you are talking to. A harness that is published but not runnable here is refused at session start with a 501 naming the field.