verikun 0.12.0 → 0.13.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` /
@@ -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
@@ -1657,7 +1657,7 @@ AI (run a natural-language test — compile once, replay model-free, self-heal)
1657
1657
  path. The model is woken only to repair a step
1658
1658
  that fails to resolve; a green run persists the
1659
1659
  (repaired) plan so the next run is free. Needs
1660
- ANTHROPIC_API_KEY (Claude), OPENAI_API_KEY (gpt-5.x),
1660
+ ANTHROPIC_API_KEY (Claude), OPENAI_API_KEY (gpt-*),
1661
1661
  or a logged-in agent CLI — no API key: --model
1662
1662
  codex-cli uses your 'codex login' ChatGPT
1663
1663
  subscription, cursor-cli your 'cursor-agent login'
@@ -1668,8 +1668,8 @@ AI (run a natural-language test — compile once, replay model-free, self-heal)
1668
1668
  running; --recompile ignores the cache.
1669
1669
  Models: claude-haiku-4-5 | claude-sonnet-4-6
1670
1670
  (default) | claude-opus-4-8 | claude-fable-5 |
1671
- gpt-5.4-mini | gpt-5.4 | gpt-5.5 | codex-cli |
1672
- cursor-cli.
1671
+ gpt-5.4-mini | gpt-5.4 | gpt-5.5 | gpt-4.1 |
1672
+ codex-cli | cursor-cli.
1673
1673
 
1674
1674
  SUITE (run a directory of natural-language tests as one gated suite)
1675
1675
  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.13.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "verikun",
3
- "version": "0.12.0",
3
+ "version": "0.13.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",