Guard

Stop a runaway run before its next step.

Run fuses hold each agent run to limits on cost, tokens, steps, LLM and tool calls, duration, repeated calls, consecutive failures, agents bouncing work back and forth — and the odds that runs like it succeed. The verdict is arithmetic over what was recorded: nothing calls a model, nothing runs a judge.

Three verdicts, one of them sticky.

A run is one unit of agent work: a session id, or a single trace. Every span that ends adds to what the run has used, and the run gets a verdict.

continue

Nothing is near a limit.

warn

At 80% or more of some limit, or at a limit under a warn policy. The run may go on.

stop

A limit reached under a stop policy, or an operator pulled the kill switch. It stays stopped until someone resets it.

The pre-call gate

Ask before the step, not after the bill.

By the time a model call’s span reaches any trace store, the call has been made and paid for. So Guard’s primary surface is a question the agent asks before each step, optionally with the step’s estimated cost and tool. A step that would go past a stop limit answers stop. The check changes nothing about the run.

A read key is enough to ask, so an agent that may only read can still be told to stop. Wire it into your framework’s loop, or a hook like the one the engine ships for Claude Code.

Before the next step
curl -s https://api.trazadb.com/v1/fuses/check -H "Authorization: Bearer $TRAZA_API_KEY" \
  -d '{"run": "sess-9c21", "next": {"est_cost_usd": 0.40}}'
Engine output, from the Guard guide
{"verdict": "stop",
 "policy": "support",
 "reasons": ["max_cost_usd 2 would be exceeded by the next step: $1.7 spent + $0.4 estimated"],
 "usage": {"cost_usd": 1.7, "steps": 1, "tokens": 1920, "llm_calls": 1, "tool_calls": 0, …},
 "source": "live", …}

The backstop

Every acknowledgement carries the verdict.

When a batch touches a run that is not continue, the ingest response names it, in the body and in a Traza-Fuse header. A client that does nothing but export still learns its run was stopped. The spans are stored either way: a verdict is a statement about the run’s next step, never a refusal of the batch it rides on.

It is late by construction, by at least one step and more with exporter batching. That is why the check exists.

step N runsits span endsexporter sends itack says stopstep N+1 must not start
An ingest acknowledgement, from the Guard guide
HTTP/1.1 200 OK
Traza-Fuse: stop; run=sess-7f3a

{"accepted":1,"durability":"wal","fuses":[{"reasons":["max_repeats 4 reached: tool:search_orders#465a6bab9a1c447d repeated 4 times"],"run":"sess-7f3a","tenant":"","verdict":"stop"}]}

Ten limits, one policy per run.

A policy is a name, the service it matches, a set of limits and an action. Edits apply to live runs at once. Every limit is optional; limits do not inherit, so a policy holds its runs to exactly what it says.

A repeat is the same call with the same input: a step’s kind and name plus a hash of what went in, like tool:search_orders#465a6bab9a1c447d. That is what a loop looks like from outside.

LimitReached when
max_cost_usdcost recorded ≥ limit
max_tokenstokens ≥ limit
max_stepsLLM, tool and agent steps ≥ limit
max_llm_callsmodel calls ≥ limit
max_tool_callstool calls ≥ limit
max_duration_secondsfirst start to last end ≥ limit
max_repeatsthe same step fingerprint ≥ limit times
max_error_streakconsecutive failed steps ≥ limit
max_handoff_cyclescontrol returned to an agent straight from the one it handed off to ≥ limit times
min_success_oddsthe 95% interval’s upper bound of how often runs on this path succeeded is below the limit

Kill switch, reset, grant.

The kill switch stops any run at once, whatever its usage, and survives restarts. A reset clears the stop but does not refill a cumulative budget: a run reset while still over its cost limit stops again immediately. To let it go on, reset with a grant that raises that run’s limits.

# stop this run now
curl -X POST https://api.trazadb.com/v1/fuses/runs/sess-7f3a/trip -H "Authorization: Bearer $TRAZA_API_KEY" \
  -d '{"reason": "looping on search_orders"}'

# approve it to continue, with a bigger budget
curl -X POST https://api.trazadb.com/v1/fuses/runs/sess-7f3a/reset -H "Authorization: Bearer $TRAZA_API_KEY" \
  -d '{"grant": {"max_cost_usd": 5}}'

Trip, reset and policy writes need a full key; the check needs only read.

Predictive limits

Stop a run because of where it is heading, not only what it has spent.

min_success_odds reads the run’s foresight: how often earlier runs of its agent that took the same path succeeded. When that stands on at least 30 runs, the run has at least 3 steps, and the 95% interval’s upper bound is below the odds, the policy acts. Name the next tool in the check and the path is judged with that step appended.

It is evaluated at the pre-call check and on verdict reads, never on ingest, and it is not latched: the next question makes the prediction again.

POST /v1/fuses/check {"run":"live-doomed-0","next":{}}

stopforesight: runs that took this path succeeded 5 of 50 times (≤21.4%)

POST /v1/fuses/check {"run":"live-healthy-0","next":{}}

continue

POST /v1/fuses/check {"run":"live-healthy-0","next":{"tool":"issue_refund"}}

stopforesight: runs that took this path and then called tool:issue_refund succeeded 15 of 30 times (≤66.8%)

Policy {"min_success_odds":0.7} on the demo corpus.

Nudges ride the same check

The check is also how a person reaches the agent.

The check that asks whether a step may run also delivers the run’s pending nudges, once. A block nudge answers stop with its message as the first reason, for two seconds — a veto of the next step that trips nothing. The kill switch is still what stops a run for good.

After a block nudge on live-doomed-0
{"verdict": "stop",
 "reasons": ["Do not call search_orders again."],
 "nudges": [{"kind": "block", "message": "Do not call search_orders again."}]}

Demo corpus Real output of traza-server 0.28.0 over the engine’s seeded behaviour corpus (226 runs, 1,342 spans), recorded 2026-10-18 by scripts/demo-numbers.mjs. Synthetic runs with known outcomes, not customer data.

On Traza Cloud

Find out when a run stops, and decide from wherever you are.

Alerts on every transition

Traza Cloud follows each cell’s fuse event feed and delivers fuse.stop, fuse.warn and fuse.manual_stop to the destinations you choose. Free: email, push. Pro and up: email, push, interactive slack, webhook, retried with backoff.

Approve, kill or nudge from anywhere

A stop can reach your phone as a push notification or Slack as an interactive message, with Approve (reset with a grant), Kill and Nudge… buttons. The console’s Guard tab and Mission Control list stopped and warned runs against their limits, with the kill switch one click away.

Signed webhooks

Webhook deliveries are JSON with a Traza-Signature: t=…,v1=… header, an HMAC-SHA256 over the timestamp and body with your destination’s secret, so your endpoint can refuse anything we did not send.

Stated plainly

What Guard does not do.

  • A step is never interrupted. A single model call or tool run that runs away on its own is not stopped mid-flight; fuses act between steps.
  • A post-ack stop is late. At least one step, more with exporter batching. Only the pre-call check refuses a step before it runs, and only if the agent asks.
  • The check sees recorded usage only. Steps started but not yet exported are invisible to it, so parallel tool calls can all pass the gate before any of them is recorded.
  • Membership is per span. Instrument every span of a run with its session id, or the run the fuse sees is smaller than the one you meant.
  • A very long run can only be stopped, not cleared, once it must be rebuilt. After a restart or an eviction, a run of more than 100,000 stored spans is judged on its earliest 100,000: enough to establish a stop, never to clear one. The check then answers with an error, and your hook decides whether to fail open or closed.

The full Guard guide, limitations included