Cost & budget
What costs money
Section titled “What costs money”Only vk ai and vk suite ever call a model. Every other command — tap, text, ui,
find, assert, swipe, screenshot, batch, run archive, device set, devices — drives
the device directly and costs nothing, ever. They need no API key and have no budget.
Within vk ai, a model is called in exactly two phases, and neither of them is the phase that
does the work:
| Phase | Model calls | Bounded by |
|---|---|---|
| Compile — prose → plan IR | 1 on a cache miss, plus 1 if the plan lint asks for a guided retry | --max-cost-usd |
| Repair — a step’s selector stopped resolving | up to 3 per failing step | --max-cost-usd, --timeout |
| Replay — running the plan on the device | none — always $0 |
— |
Pay once to compile, replay free, pay again only when the app drifts under a step. See Natural-language tests.
When the model is called
Section titled “When the model is called”Compile
Section titled “Compile”A compile happens when the plan cache misses. Four things cause a miss:
| Cause | Detail |
|---|---|
| The cache key changed | Key is the test prose byte-exact, plus --package, --app-build and the platform. A reworded sentence is a new key. |
| The compiler fingerprint rotated | verikun’s version plus its grammar and repair-prompt text. A verikun upgrade re-spends the whole suite. |
--recompile / --no-cache |
Skips the read outright. |
| No cache on disk | A fresh CI runner has no ./.verikun/plans/ — see the cold cache. |
The lint retry can add a second call: after compiling, verikun checks the plan against the prose, and one finding triggers exactly one guided recompile. It is skipped if the budget is already spent, in which case the first plan is kept rather than paying for a better one.
Two spenders that surprise people:
--show-planstill compiles, and still spends. It skips the device, not the model.- Seeding from a prior build is still a paid compile. A prior plan goes into the prompt as a starting point, which makes the result better, not the call cheaper.
Repair
Section titled “Repair”A repair is triggered only by a selector that misses or resolves ambiguously. It is capped at 3 attempts per failing step — a fixed number with no flag.
An assert failure never heals, so it never costs anything. Neither does an environment error,
a guard, or a read. See Self-healing in CI.
How the estimate is calculated
Section titled “How the estimate is calculated”Each API response reports its token usage. Every response is priced with the same formula:
(input × in$ + output × out$ + cache_write × in$ × 1.25 + cache_read × in$ × 0.1) ÷ 1,000,000where in$ and out$ are the model’s price per 1M tokens
(where they come from). At an illustrative $3 in / $15 out, a
compile with 1,500 input, 2,000 output and 6,000 cache-write tokens costs about $0.057.
Four things about that formula are not visible from the outside:
- Cache write is
1.25 ×the input rate and is added to normal input, so caching pays off only from the second call — and compile and repair use different prefixes. - Cache read is
0.1 ×the input rate, exceptgpt-4.1at0.25 ×. --effortraises cost through the output rate (reasoning tokens bill as completion tokens); it has no effect ongpt-4.1or the CLI backends.- It is an estimate, and it under-counts: a retried attempt and a response without a usage
block both count as zero. The repair cap and
--timeoutkeep that bounded.
Where the rates come from
Section titled “Where the rates come from”Every model carries two numbers: USD per 1M input tokens and USD per 1M output tokens.
The single source of truth is
src/agent/cost.ts — read your
model’s rate there. The file also records the date each vendor’s prices were captured. For which
models exist, which backend serves each, and which key it needs, see
AI plans & models.
The budget
Section titled “The budget”--max-cost-usd <n>, default $3, aborts the run when the running estimate reaches the
ceiling. --timeout <dur>, default 15m, is the second bound.
When it is checked
Section titled “When it is checked”The budget is a pre-spend gate: it is tested before each further compile of a multi-chunk test, before the lint retry (which is skipped rather than failed), after compile but before the device run, and before each repair attempt. Because the check happens before a call, the actual spend can overshoot the ceiling by up to one call. It is never checked during replay, because replay never spends.
What a breach looks like
Section titled “What a breach looks like”[ai] ABORTED — cost ceiling $3 reached · compile=$3.0142 · repairs=$0.0000 · replay=$0 · cache_read=0 tok · est $3.0142- Exit
1— the same code as an ordinary test failure. There is no distinct budget exit code;abortedForBudget: truein--jsonis the discriminator. See Exit codes. - The run is recorded as failed, so the JUnit and HTML report show it red. A budget abort can never archive green.
--retriesnever retries it — each attempt would get its own fresh ceiling and re-abort.- A compile-time abort produces no artifacts at all — no run directory, no report, no JUnit,
and therefore empty stdout. Worth knowing if you script
REPORT=$(vk ai …).
Reading the cost line
Section titled “Reading the cost line”Every run prints one line in this shape:
compile=$0.0184 · repairs=$0.0000 · replay=$0 · cache_read=12043 tok · est $0.0184| Field | Meaning |
|---|---|
compile |
Spend on compiling the prose into a plan, including any lint retry |
repairs |
Spend on model repairs of drifted steps |
replay |
Always $0 — running the plan calls no model |
cache_read |
Cached input tokens read across the run — the only token count printed |
est |
compile + repairs, the figure --max-cost-usd meters |
Where the line surfaces:
| Surface | Where |
|---|---|
| stderr | Appended to the [ai] PASS / [ai] FAIL … / [ai] ABORTED … verdict, then repeated on a completed run as [ai] estimated total cost: $… |
vk ai --json |
cost (the line) and costUsd (the number, 4 dp) |
| HTML report | The vk ai box at the top of the run report |
| JUnit XML | <testsuite><system-out>, prefixed vk ai:. There is no per-<testcase> cost. |
| Suite overview | A Cost column per test, plus the summary chip |
Suite index.json |
totals.costUsd, per-test costUsd, per-attempt costUsd |
What is free
Section titled “What is free”- A cache hit. The plan replays with no model call, so the line reads
est $0.0000. - The CLI backends.
--model codex-cli/--model cursor-clishell out to an already-logged-incodexorcursor-agent, billing your ChatGPT or Cursor subscription rather than per token. They report$0, which also makes--max-cost-usdand--cost-overrideinert no-ops — the run is bounded by the repair cap and--timeoutinstead.
Keeping it cheap
Section titled “Keeping it cheap”| Do this | Because |
|---|---|
Persist ./.verikun/plans/ between CI runs |
It is the difference between $0 and a full recompile of every test, every run. How |
Name identifiers in the prose — Tap @get_started, not “tap the big green button” |
A selector that resolves never triggers a repair, the only recurring cost on a warm cache. |
| Assert, don’t just act | An assert failure is terminal and never healed, so it fails fast instead of paying for three repair attempts. |
| Pick a cheaper model, or a CLI backend | Compile is output-heavy (the plan is JSON), so the output rate dominates. |
Leave --effort alone unless a test needs it |
It bills through the output rate. |
Tighten --max-cost-usd per test |
It is the cap that always applies; --max-suite-cost-usd is optional. |
| Expect a full re-spend after upgrading verikun | The compiler fingerprint rotates. Budget for it. |
What is not metered here
Section titled “What is not metered here”This page is about verikun’s own model spend — the compile and repair calls it makes on your
behalf. It is not about the tokens your agent spends while driving the device: reading a
vk ui dump back into its context, or looking at a screenshot. That is a separate budget, usually
the larger of the two, covered in
Using it from an AI agent and
Screenshots.