Action Budgets, Iteration Caps, and Cost Governors
A mid-sized e-commerce agent's monthly LLM cost jumped from $1,200 to $4,800 after its team enabled the "order-tracking" skill. Nothing was wrong with the pricing. The agent had found a new loop and kept spending inside it. Microsoft's Agent Governance Toolkit (April 2, 2026) lists the defenses for this class: per-request `max_tokens`, per-task token and dollar budgets, per-day and per-month caps, iteration caps, tiered model routing, prompt caching, context windowing, human checkpoints on expensive actions, and kill switches on budget breach. Anthropic's Claude Code Agent SDK ships the same primitives under different names. A financial velocity limit, such as cutting access at more than $50 in 10 minutes, catches a loop faster than a monthly cap can.
Learning Objectives
- Explain why autonomous agents need a stack of cost limits at different time scales instead of one cap, and name the Denial of Wallet failure.
- Map each failure mode (runaway loop, slow leak, bad release, legitimate surge) to the layer that catches it first.
- Configure the Claude Code budget surface: max_turns, max_budget_usd, tool allow and deny lists, and pre-tool hooks.
- Run the simulator and read its output to see which governor actually stops a polling loop, and tune a velocity limit from an observed spend rate.
The Problem
Autonomous agents spend money on every turn. A chatbot's bad output is a bad reply; an agent's bad loop is a bill. The failure has a name, "Denial of Wallet": the agent keeps reasoning, keeps calling tools, keeps billing, and nothing stops it because nothing was built to.
One number won't fix it. You need a stack of limits at different time scales and granularities: per-request, per-task, per-hour, per-day, per-month. A good stack catches a runaway loop within minutes, a slow leak within hours, and a bad release within a day. It also gives a long-horizon, autonomous agent a budget at all.
The math is trivial. The discipline is where teams fail. Every limit below is named in either the Microsoft Agent Governance Toolkit or the Anthropic Claude Code Agent SDK docs.
The Concept
The cost-governor stack
max_tokensper request. Simple. Prevents any one call from emitting an unbounded completion.- Per-task token budget. Across the whole run, do not exceed N tokens. Hard stop at the cap.
- Per-task dollar budget. Same as tokens but in currency.
max_budget_usdin Claude Code. - Per-tool call cap. No more than N
WebFetchcalls, Nshell_execcalls, etc. - Iteration cap (
max_turns). Total agent loop iterations; prevents infinite reasoning loops. - Per-minute / per-hour / per-day / per-month cap. Rolling windows. Catches leaks at different time scales.
- Financial velocity limit. E.g., "if spend exceeds $50 in 10 minutes, cut access." Catches loop-based burn before monthly caps fire.
- Tiered model routing. Default to a smaller model; escalate to a larger one only when a classifier judges the task warrants it.
- Prompt caching. System prompt and stable context stored in provider cache; token cost of re-sending is near zero.
- Context windowing. Compaction / summarization to keep the active context below a threshold; direct token-cost reduction.
- HITL checkpoints on expensive actions. Before an action known to be expensive (long tool call, large download, a costly model upgrade), require a human tap.
- Kill switch on budget breach. Session aborts when any cap fires. Cap is recorded; requires a separate re-enable path.
Why the stack, not one cap
A monthly cap on its own fires only after the wallet is gone. A per-request cap on its own sees nothing at the session level. Each failure mode lives at a different time scale:
- Runaway loop (agent stuck in a 5-second retry): caught by velocity limit.
- Slow leak (agent doing ~2x expected work per task): caught by daily cap.
- Bad release (new version uses 5x tokens): caught by weekly / monthly cap.
- Legitimate surge (real demand, not a bug): caught by hour / day cap with clear log.
Claude Code's budget surface
The Claude Code Agent SDK exposes (public docs):
max_turns: the iteration cap.max_budget_usd: the dollar cap. The session aborts on breach.allowed_tools/disallowed_tools: tool allowlist and denylist.- Hook points before tool use, for custom cost accounting.
Combine these with the permission-mode ladder (Lesson 10). An autoMode session without max_budget_usd is ungoverned autonomy. Anthropic frames Auto Mode as needing budget controls, and the classifier has nothing to do with cost.
EU AI Act, OWASP Agentic Top 10
Microsoft's Agent Governance Toolkit covers the OWASP Agentic Top 10 and the EU AI Act Article 14 (human oversight) requirements. For production in the EU, treat logging and cap enforcement as required.
The observed $1,200 → $4,800 case
The case from the Microsoft docs: an e-commerce agent whose monthly cost quadrupled after a new tool was added. The tool let the agent poll order status during every session, and the setup had no loop detection, no per-tool cap, and no alert on week-over-week growth. The fix was a per-tool cap plus a daily-growth alert. Treat it as a template. Every new tool surface is a new potential loop, so each new tool gets its own cap and its own alert.
Use It
main.py simulates an agent that drifts into a polling loop at turn 30, where each turn jumps from 2,500 to 8,000 tokens. It runs that trajectory under three configurations: no caps, a monthly cap only, and the layered stack. Run it and read the last column.
With no caps, the run burns through 10,000 simulated turns and about $240. The monthly-cap-only run ends identically, because the $500 cap sits above the whole run's cost and never fires. The layered stack stops at turn 200 on max_turns, for about $4.32.
The velocity limit does not trip on this trajectory at its default of $5/min. The loop spends about $0.048/min (8,000 tokens at $0.003 per thousand, one turn every 30 seconds), so the threshold is two orders of magnitude too loose. Exercise 1 has you tune it.
Ship It
outputs/skill-agent-budget-audit.md audits a proposed agent deployment's cost-governor stack and flags missing layers.
I'd run main.py and read the last column before the headline text. All three runs hit the same polling loop at turn 30, and the only difference is which governors are switched on. The no-caps run and the monthly-cap-only run come out identical at 79,840,500 tokens, because the $500 monthly cap sits above the roughly $240 the whole run costs. A cap set higher than anything the run can reach is the same as no cap.
The part I'd poke at is the velocity limit. The printed headline says it catches the loop within minutes, but with the defaults it never trips here, and max_turns does the stopping at turn 200. The loop's rate is about $0.048/min against a $5/min threshold. A velocity threshold only works if you set it from the agent's observed spend rate, so measure a normal run first. velocity_exceeded divides by the elapsed time inside the window rather than the nominal ten minutes, which keeps the rate from being under-reported early in a run.
Keep the simulator in proportion, too. It prices every token at a flat $0.003 per thousand and gives every turn a fixed 30 seconds. Real runs vary on both, so I'd take the ordering of the three outcomes from it and ignore the dollar figures.
Exercises
-
Run
main.py. The layered stack stops onmax_turns, not the velocity limit. Lowervelocity_usd_per_minuntil the velocity limit fires during the loop but not during normal 2,500-token turns. How many dollars does the run spend before it stops, compared with the $4.32 the iteration cap allows? -
Design a per-tool cap set for a browser agent (Lesson 11). Which tool needs the tightest cap? Which tool can run unbounded without risk?
-
Read the Microsoft Agent Governance Toolkit docs. List every cap type the toolkit names. Map each to one of the failure modes (runaway loop, slow leak, bad release, surge).
-
Price an overnight unattended run for a realistic task (e.g., "triage 50 issues in a repo"). Set
max_budget_usdat 2x your point estimate. Justify the 2x. -
Claude Code's
max_budget_usdfires on session aggregate cost. Design a complementary velocity limit you would enforce externally. What triggers the cut-off, and what does re-enable look like?
Key Terms
| Term | What people say | What it actually means |
|---|---|---|
| Denial of Wallet | "Runaway bill" | Agent loop generating spend with no cap to stop it |
| max_tokens | "Per-request cap" | Ceiling on a single completion's size |
| max_turns | "Iteration cap" | Ceiling on agent loop iterations in a session |
| max_budget_usd | "Dollar kill switch" | Session cost cap; aborts on breach |
| Velocity limit | "Rate cap" | Limit on spend per short window (e.g., $50 / 10 min) |
| Tiered routing | "Small model first" | Cheap model default; escalate only when classifier warrants |
| Prompt caching | "Cached system prompt" | Provider-side cache reduces re-send token cost to near zero |
| HITL checkpoint | "Human approval gate" | Human tap required before expensive action |
Further Reading
- Anthropic Claude Code Agent SDK: agent loop and budgets:
max_turns,max_budget_usd, tool allowlists. - Microsoft Agent Framework: human-in-the-loop and governance: cost-governor checkpoints.
- Anthropic: Claude Managed Agents overview: provider-side cost controls.
- Anthropic: Prompt caching (Claude API docs): caching mechanics.
- Anthropic: Measuring agent autonomy in practice: cost profile for long-horizon agents.
Code
- main.py (python, 166 lines)