Your agent tried the same fix four times. Nobody was watching.
{{ app_name }} watches unattended AI agents, notices when they stop making progress, and pauses them before more money goes into a loop. You get the evidence, not a guess.
A spend cap can't tell stuck from busy
An agent working through a hard bug and an agent going in circles both just look like money being spent. {{ app_name }} watches what each attempt achieved, so a run that's making progress keeps going, and a run that isn't gets stopped with an explanation.
Install the SDK
pip install agent-supra. The SDK has no dependencies and works with Gemini, OpenAI and Anthropic clients. Install your model provider's client separately.Wrap your agent
Wrap your model client, then report each test run. A few lines in the loop you already have.
Review what it catches
Start in observation mode. When the flags look right, let it pause runs, and get a Slack message when it does.
What it watches for
Plain rules you can read, not a second AI guessing. Each one can pause, only flag, or be switched off.
No progress
The same tests keep failing while the agent keeps changing the code.
Exact repeats
The same failure again with nothing changed, or the same tool failing the same way.
Going in circles
The agent flips between two versions of the code.
Context running away
Every call sends more tokens than the last, with nothing to show for it.
Budget and call caps
A hard limit per task, shared with any sub-agents it starts.
Add it to a Gemini agent
Anthropic and OpenAI clients work the same way.
from google import genai
from agent_supra import (
Client, SupervisedRun, SupervisorPaused)
sup = Client() # reads your API key from env
task = sup.create_task("Fix checkout tests",
budget_usd=3)
with SupervisedRun(sup, task) as run:
ai = run.wrap_gemini(genai.Client())
try:
while True:
run.check()
reply = ai.models.generate_content(
model="gemini-3.5-flash-lite",
contents=prompt)
apply_fix(reply.text)
t = run_tests()
run.report_tests(t.all, t.failing,
rev=git_rev())
if not t.failing:
run.finish("success")
break
except SupervisorPaused as paused:
print(paused.reason)What it stores
Token counts, test names, pass or fail, and fingerprints of tool arguments and errors. Not your prompts, not the model's replies, not your code. API keys are stored only as hashes. Read the privacy page.
Free while in beta
You keep paying your model provider directly with your own keys. {{ app_name }} never sees those keys. If paid plans come later, you'll hear about them well before anything changes.