verikun 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -251,7 +251,8 @@ iteration by construction, which is what makes it a loop.
251
251
  the bundled per-1M price table if it drifts.
252
252
  - **`--model`** picks the model and its provider — Anthropic (`claude-haiku-4-5` ·
253
253
  `claude-sonnet-4-6` (default) · `claude-opus-4-8` · `claude-fable-5`), OpenAI
254
- (`gpt-5.4-mini` · `gpt-5.4` · `gpt-5.5`), each read from its own key
254
+ (`gpt-5.4-mini` · `gpt-5.4` · `gpt-5.5` · `gpt-4.1`, the last a non-reasoning model
255
+ that undercuts the default sonnet at $2/$8 per 1M), each read from its own key
255
256
  (`ANTHROPIC_API_KEY` / `OPENAI_API_KEY`), or a CLI backend — **`codex-cli`** (the
256
257
  logged-in `codex` binary) or **`cursor-cli`** (`cursor-agent`) — which need no key at
257
258
  all: spend is on your subscription, so their cost line is `$0` and `--max-cost-usd` /
@@ -576,6 +577,20 @@ vk tap @tap_to_continue_label_id
576
577
  **Cost:** $0.45 · **Wall time:** ~4 min · **Model:** Claude Sonnet 4.6 with
577
578
  prompt-cache hits (1 M cache-read tokens kept cost low on a long conversation).
578
579
 
580
+ ## Feedback — help improve verikun
581
+
582
+ verikun improves from the rough edges people hit while driving it. When verikun *itself* is
583
+ the friction — a step that heals on every cached replay (an unstable compiled selector,
584
+ often a label-only control with no resource-id), a repair "give-up", or a gotcha in its own
585
+ operation — that's worth an issue at
586
+ [github.com/ddikman/verikun/issues](https://github.com/ddikman/verikun/issues).
587
+
588
+ Driving verikun with an AI agent + the [skill](.claude/skills/verikun/SKILL.md)? It hands
589
+ off to the **`suggest-verikun-improvement`** skill, which drafts a short, TL;DR-first
590
+ suggestion, **reviews it with you before anything is submitted**, and **redacts every
591
+ app-under-test specific** (package, on-screen text, selector values, test prose, logs) so no
592
+ client code or logic can leak.
593
+
579
594
  ## Build from source
580
595
 
581
596
  For local development, or to run an unreleased version, build from a clone:
@@ -11,8 +11,9 @@ const errors_1 = require("../errors");
11
11
  // ALLOWED_MODELS and providerFor all derive from this, so the --model allowlist, its
12
12
  // price and its backend can never disagree. Prices WILL drift between releases
13
13
  // (Anthropic cached 2026-05-26; OpenAI 2026-07-02) — `--cost-override <input/output>`
14
- // is the escape hatch and is authoritative when supplied. Every model here bills cached
15
- // input at ~0.1x (Anthropic + OpenAI gpt-5.x alike), matching CACHE_READ_MULT below.
14
+ // is the escape hatch and is authoritative when supplied. Nearly every model here bills
15
+ // cached input at the ~0.1x CACHE_READ_MULT below (Anthropic + OpenAI gpt-5.x alike); the
16
+ // exception carries an explicit `cacheReadMult`.
16
17
  const MODELS = {
17
18
  'claude-haiku-4-5': { input: 1, output: 5, provider: 'anthropic' },
18
19
  'claude-sonnet-4-6': { input: 3, output: 15, provider: 'anthropic' },
@@ -21,6 +22,11 @@ const MODELS = {
21
22
  'gpt-5.4-mini': { input: 0.75, output: 4.5, provider: 'openai' },
22
23
  'gpt-5.4': { input: 2.5, output: 15, provider: 'openai' },
23
24
  'gpt-5.5': { input: 5, output: 30, provider: 'openai' },
25
+ // The one NON-REASONING model in the registry, and cheaper than the default sonnet
26
+ // ($2/$8 vs $3/$15). Two consequences, both handled rather than papered over: it takes
27
+ // no reasoning_effort (openai.ts's REASONING_MODELS gate skips the param for it), and it
28
+ // bills cache reads at 0.25x rather than the 0.1x every other model here uses.
29
+ 'gpt-4.1': { input: 2, output: 8, provider: 'openai', cacheReadMult: 0.25 },
24
30
  // CLI-agent backends: billed to the user's ChatGPT/Cursor subscription via an already-logged-in
25
31
  // CLI, not per token — so price is $0 and --max-cost-usd/--cost-override are inert no-ops (the
26
32
  // run is bounded by maxRepairs + --timeout instead). The `-cli` suffix reads clearly as "the
@@ -29,7 +35,9 @@ const MODELS = {
29
35
  'codex-cli': { input: 0, output: 0, provider: 'codex' },
30
36
  'cursor-cli': { input: 0, output: 0, provider: 'cursor' },
31
37
  };
32
- exports.MODEL_PRICES = Object.fromEntries(Object.entries(MODELS).map(([m, s]) => [m, { input: s.input, output: s.output }]));
38
+ // Strip `provider` off each spec; what remains IS the Price (including any cacheReadMult,
39
+ // so a per-model deviation reaches the tracker without being re-listed here).
40
+ exports.MODEL_PRICES = Object.fromEntries(Object.entries(MODELS).map(([m, { provider: _provider, ...price }]) => [m, price]));
33
41
  exports.ALLOWED_MODELS = Object.keys(MODELS);
34
42
  exports.DEFAULT_MODEL = 'claude-sonnet-4-6';
35
43
  /** Resolve which provider backend serves a model (unknown → anthropic, the default). */
@@ -59,7 +67,8 @@ function parseCostOverride(raw) {
59
67
  function priceFor(model, override) {
60
68
  return override ?? exports.MODEL_PRICES[model] ?? exports.MODEL_PRICES[exports.DEFAULT_MODEL];
61
69
  }
62
- // Cache reads bill at ~0.1x input; cache writes (5-min TTL) at ~1.25x input.
70
+ // Cache reads bill at ~0.1x input (unless the model overrides it via Price.cacheReadMult);
71
+ // cache writes (5-min TTL) at ~1.25x input.
63
72
  const CACHE_READ_MULT = 0.1;
64
73
  const CACHE_WRITE_MULT = 1.25;
65
74
  const PER_M = 1_000_000;
@@ -72,7 +81,7 @@ function estimateCostUsd(usage, price) {
72
81
  return ((input * price.input +
73
82
  output * price.output +
74
83
  cacheWrite * price.input * CACHE_WRITE_MULT +
75
- cacheRead * price.input * CACHE_READ_MULT) /
84
+ cacheRead * price.input * (price.cacheReadMult ?? CACHE_READ_MULT)) /
76
85
  PER_M);
77
86
  }
78
87
  /**
@@ -27,9 +27,6 @@ const DEFAULT_REQUEST_TIMEOUT_MS = 120_000;
27
27
  // gpt-5.x are reasoning models: they take reasoning_effort, require max_completion_tokens
28
28
  // (not max_tokens), and reject a custom temperature. Map verikun's effort scale onto
29
29
  // OpenAI's (whose ceiling is 'high'), and only send it when the caller asked for one.
30
- // INVARIANT: every OpenAI model in cost.ts's registry is a reasoning model that accepts
31
- // reasoning_effort. If a non-reasoning model is ever added there, gate this send behind an
32
- // allowlist like claude.ts's EFFORT_MODELS (else it 400s → exit 2 with no retry).
33
30
  const EFFORT_MAP = {
34
31
  low: 'low',
35
32
  medium: 'medium',
@@ -37,6 +34,12 @@ const EFFORT_MAP = {
37
34
  xhigh: 'high',
38
35
  max: 'high',
39
36
  };
37
+ // reasoning_effort is rejected outright by non-reasoning models (gpt-4.1), and a 400 is NOT
38
+ // retried — it surfaces as exit 2 — so send the param only for models known to accept it,
39
+ // mirroring claude.ts's EFFORT_MODELS. An ALLOWLIST on purpose: a model missing from here
40
+ // merely forgoes effort, whereas a denylist would let the next non-reasoning model 400.
41
+ // Keep in step with cost.ts's registry when adding an OpenAI model.
42
+ const REASONING_MODELS = new Set(['gpt-5.4-mini', 'gpt-5.4', 'gpt-5.5']);
40
43
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
41
44
  const backoffMs = (attempt) => Math.min(1000 * 2 ** (attempt - 1), 15000);
42
45
  /** Map OpenAI's usage onto the normalized (Anthropic-shaped) Usage the CostTracker
@@ -162,7 +165,7 @@ class OpenAiProvider {
162
165
  { role: 'user', content: user },
163
166
  ],
164
167
  };
165
- if (this.opts.effort) {
168
+ if (this.opts.effort && REASONING_MODELS.has(this.opts.model)) {
166
169
  const mapped = EFFORT_MAP[this.opts.effort];
167
170
  if (mapped)
168
171
  body.reasoning_effort = mapped;
package/dist/cli.js CHANGED
@@ -1176,6 +1176,7 @@ async function runAiTest(file, opts, backend, platform, device) {
1176
1176
  (0, output_1.err)(`[ai] cost ceiling $${opts.maxCostUsd} reached during compile (${cost.summaryLine()}) — not running`);
1177
1177
  return {
1178
1178
  ok: false,
1179
+ cached,
1179
1180
  costUsd: Number(cost.usd().toFixed(4)),
1180
1181
  costLine: cost.summaryLine(),
1181
1182
  modelRepairs: 0,
@@ -1267,6 +1268,7 @@ async function runAiTest(file, opts, backend, platform, device) {
1267
1268
  (0, output_1.err)(`[ai] estimated total cost: $${cost.usd().toFixed(4)}`);
1268
1269
  return {
1269
1270
  ok: result.ok,
1271
+ cached,
1270
1272
  costUsd: Number(cost.usd().toFixed(4)),
1271
1273
  costLine,
1272
1274
  modelRepairs: result.modelRepairs,
@@ -1308,6 +1310,7 @@ async function cmdAi(positionals, flags) {
1308
1310
  if ((0, args_1.flagBool)(flags, 'json')) {
1309
1311
  (0, output_1.json)({
1310
1312
  ok: result.ok,
1313
+ cached: result.cached,
1311
1314
  model: opts.model,
1312
1315
  cost: result.costLine,
1313
1316
  costUsd: result.costUsd,
@@ -1657,7 +1660,7 @@ AI (run a natural-language test — compile once, replay model-free, self-heal)
1657
1660
  path. The model is woken only to repair a step
1658
1661
  that fails to resolve; a green run persists the
1659
1662
  (repaired) plan so the next run is free. Needs
1660
- ANTHROPIC_API_KEY (Claude), OPENAI_API_KEY (gpt-5.x),
1663
+ ANTHROPIC_API_KEY (Claude), OPENAI_API_KEY (gpt-*),
1661
1664
  or a logged-in agent CLI — no API key: --model
1662
1665
  codex-cli uses your 'codex login' ChatGPT
1663
1666
  subscription, cursor-cli your 'cursor-agent login'
@@ -1668,8 +1671,8 @@ AI (run a natural-language test — compile once, replay model-free, self-heal)
1668
1671
  running; --recompile ignores the cache.
1669
1672
  Models: claude-haiku-4-5 | claude-sonnet-4-6
1670
1673
  (default) | claude-opus-4-8 | claude-fable-5 |
1671
- gpt-5.4-mini | gpt-5.4 | gpt-5.5 | codex-cli |
1672
- cursor-cli.
1674
+ gpt-5.4-mini | gpt-5.4 | gpt-5.5 | gpt-4.1 |
1675
+ codex-cli | cursor-cli.
1673
1676
 
1674
1677
  SUITE (run a directory of natural-language tests as one gated suite)
1675
1678
  suite <dir> [--app <id>] [--name n] [--json] (+ all \`ai\` flags, incl. --server)
package/dist/version.js CHANGED
@@ -3,4 +3,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VERSION = void 0;
4
4
  // GENERATED by scripts/gen-version.mjs from package.json's "version" at build time
5
5
  // (the `prebuild` script). Do NOT edit by hand; bump package.json instead.
6
- exports.VERSION = '0.12.0';
6
+ exports.VERSION = '0.14.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "verikun",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "description": "Drive Android emulators/devices and iOS simulators for AI agents: tap, type, swipe, screenshot, and inspect the UI hierarchy by semantic identifiers — like Puppeteer for native apps.",
5
5
  "keywords": [
6
6
  "android",