The budget module enforces per-run cost limits and provides long-term cost observability. It is composed of four components that operate at different timescales:
| Component | Timescale | Enforcement |
|---|---|---|
RunBudget |
per-run | hard limit; raises exception |
BudgetMonitor |
per-run | soft thresholds; callbacks/warnings |
CostTracker |
historical | read-only audit aggregation |
daily_cost ergonomic |
per-day | hard cap via SystemExit |
- Guarantees: Once constructed with valid limits,
check_cost_preflight()always raisesBudgetExceededErrorif the pre-flight estimate exceedsmax_estimated_cost_cents. It never silently swallows over-budget conditions. - Pre-flight bypass: If
max_estimated_cost_cents <= 0, the check is skipped entirely (budget enforcement disabled). - No mutable state:
RunBudgetis afrozen=Truedataclass. All budget checks are pure reads.
- Guarantees: Each configured threshold (50%, 80%, 90%, 100%) fires at most once per monitor instance. Re-checking the same cost value never re-fires a previously emitted threshold.
- Idempotency:
_emitted_levelstracks fired thresholds asintbucket values. Once added, they are never re-fired untilreset()is called. - Action ordering:
check()returns the highest-priorityBudgetActionacross all newly crossed thresholds in a single call. Priority:NONE < WARN < PROMPT_CONFIRM < SUGGEST_READ_ONLY. - Non-interactive mode: At 90%, the monitor auto-continues (returns
WARN) instead of callingon_prompt. Controlled by env varsTEAAGENT_NO_SUMMARY=1orTEAAGENT_INTERACTIVE=0. - Prompt guard:
on_promptis called at most once per monitor instance per run (guarded by_prompted).
- Guarantees: Always returns well-formed summary dicts even if no run data exists (returns
{'runs': 0, 'cost_cents': 0.0, ...}). - No side effects: All methods are read-only. The tracker never writes or modifies audit logs.
- Fallback timestamps: If no
created_atis found in a run's events, the file's mtime is used.
- Guarantees:
check_daily_cost_cap()terminates the process withSystemExitif daily spend meets or exceedscap_cents. It never raises any other exception type for the cap condition. - Bypass:
cap_cents <= 0disables the check entirely.
RunBudget.max_iterations >= 1always (enforced byvalidate()).RunBudget.max_tool_calls >= 0always (enforced byvalidate()).RunBudget.max_estimated_cost_cents >= 0always (enforced byvalidate()).BudgetMonitor._emitted_levelsgrows monotonically untilreset().BudgetMonitor._promptedis set toTrueat most once per run.CostTracker._parse_runs()never raises; it silently skips unreadable files.- All
CostTrackerreports are derived solely from.teaagent/runs/*.jsonlunder the configured root.
reset()
┌────────────────────────────────────────┐
▼ │
[IDLE / 0 thresholds emitted]
│
│ check(cost_cents) → percent >= 50%
▼
[50% emitted] → BudgetAction.WARN
│
│ check(cost_cents) → percent >= 80%
▼
[80% emitted] → BudgetAction.WARN
│
│ check(cost_cents) → percent >= 90%
▼ ┌──────────────────┐
[90% emitted] │ non-interactive │
│ │ → BudgetAction. │
├──[interactive + on_prompt]────────│ WARN │
│ _prompted=True └──────────────────┘
│ on_prompt(payload) → bool
│ False → BudgetAction.PROMPT_CONFIRM
│ True → BudgetAction.WARN
│
│ check(cost_cents) → percent >= 100%
▼
[100% emitted] → BudgetAction.SUGGEST_READ_ONLY
check_cost_preflight(provider, model, input_chars, max_output_tokens)
│
├─ max_estimated_cost_cents <= 0 ──→ return (no check)
│
├─ estimated > max_estimated_cost_cents ──→ raise BudgetExceededError
│
└─ estimated <= max_estimated_cost_cents ──→ return (OK)