Skip to content

v1.0.49

Choose a tag to compare

@github-actions github-actions released this 14 Aug 20:06
· 4209 commits to main since this release

@veyyon/catalog

Added

  • Refreshed the bundled catalog from models.dev: 127 rows across 22 providers that upstream already served, including gemini-3.7-flash on Google, Vertex, GitHub Copilot, OpenRouter, Kilo, NanoGPT, OpenCode Zen and Vercel AI Gateway, each carrying the declared low/medium/high effort ladder. The bundle had drifted far enough that a model released the day before was absent from every provider that serves it. A row missing from the bundle still recovers its ladder at runtime through the models.dev fallback, so what this closes is the cold-start and offline window before that fetch lands, not a broken lookup.

Removed

  • Dropped novita/inclusionai/ling-3.0-tiny and umans/umans-deepseek-v4-flash-0731-lab, which upstream no longer lists.

@veyyon/coding-agent

Fixed

  • A recursive delete through a variable nobody set no longer stops a yolo session. rm -rf "$BUILD_DIR", rm -rf "$CARGO_TARGET_DIR", rm -rf "$WORKTREE" and rm -rf "$checkout" were all critical, the one verdict /yolo cannot lift and no standing grant can cover, so the most ordinary cleanup an agent writes ended an unattended run — and the reason given was that the variable MIGHT hold /. It does not: an unset name expands to nothing, so the guard was refusing a command on a guess about a value that does not exist, at the only severity that cannot be overridden. A floor that fires there is a floor an operator switches off, and then nothing is left for the real rm -rf /. An unsettled expansion now earns destroys only where no assumption is needed — the EMPTY reading, which is what an unset variable already is, so rm -rf "$OUT"/* (the July 2026 incident, dir expanded to nothing), rm -rf "$D"/ and rm -rf "$D/lib" all still stop even yolo — while a bare rm -rf "$D", catastrophic only if the name turns out to be / or the home directory, drops to dangerous: still a prompt at every rung below yolo, no longer a claim to be as certain as rm -rf /. A word that spells a protected component itself keeps the floor whatever the prefix expands to, so rm -rf "$D/.ssh" is refused exactly as rm -rf ~/.ssh is.
  • Two words the guard reported as "unknown" carry opposite evidence, and reading them as one shape is a fail-open hole. A variable NOBODY SET expands to nothing. A variable whose value the scan READ and declined to paste is going to become a real path: rm -rf $V with V="/*" globs to every top-level entry, ${NOPE:-/} carries / in its own text, cd / && rm -rf $PWD names a variable the shell maintains and which is / by the time it runs, and ~user names another account's home directory. Each of those was measured deleting the root with no prompt at any rung before the guard read the environment at all, so ExpandedWord now records WHY a word is unknown and every one of them keeps the destroys floor. Only the genuinely-unset case is treated as speculative.
  • A recursive delete through a variable is judged by the path it can become, instead of being refused for holding a variable at all. rm -rf "$DST/facet" was critical, which is the one verdict /yolo cannot lift and no standing grant can cover, so on a long unattended run every variable-shaped cleanup stopped the agent dead — and the reason given, "an expansion whose value is not knowable from the command text", was true of an ordinary staging command whose worst possible reading is /facet, a top-level path the literal spelling rm -rf /facet has always been allowed to delete. An unsettled expansion is now instantiated with the three values that make a path dangerous (empty, the root, the home directory) and each concrete result goes through the same classifier a literal path does, so the refusal names the reading it is about: "rm would recursively remove a protected system directory (/) when the expansion this command line does not settle is empty". Every incident shape is still refused, including the July 2026 one: the bare word reads as /, "$dir"/* reads as /, "$dir"/lib reads as /lib, and "$D/.ssh" reads as the credentials directory. A value that CLIMBS is the residual this accepts, and it is named where the rule is written.
  • A glob is judged as the directory it reads, which closes the literal half of the same hole. rm -rf /*, rm -rf ~/*, rm -rf /var/* and rm -rf ~/.config/* were all allowed, because the text /var/* equals no protected root, is not an ancestor of the home directory, and sits under none of the protected directories — so each of them destroys a directory whose glob-free spelling has always been refused. A component holding a glob is now dropped together with everything below it and the directory the glob reads is judged instead. A glob inside an ordinary directory (rm -rf ./dist/*, rm -rf /var/log/nginx/*) is untouched.
  • rm -rf "" no longer reports the working directory as its target. A word an expansion collapsed to nothing was resolved against the working directory like any relative path, so a cleanup run from the home directory was refused as a delete of the home directory itself.
  • The re-root nudge waits for a habit and then says it once. cwd-reroot fired on the FIRST call naming a path outside the working directory — one glance at one file in another project — and its own body then told the reader to ignore it if the read was a one-off, which is a paragraph of advice arriving before the behavior it is about exists. It repeated after a gap of eight messages on top of that, so a session that reads across two projects heard it several times about work that had already moved on. It now declares warmupMatches: 3 and repeatMode: per-compact: silent until three separate calls have reached outside the working directory, and then silent until the transcript is replaced.
  • A rule may declare warmupMatches in its frontmatter: how many distinct streams it has to match in before it fires at all, default 1 (the old behavior, unchanged for every rule that says nothing). The unit is the stream rather than the match, which is the whole difficulty — one tool call is re-matched on every delta it streams, so a warm-up counted in matches clears inside the first call and the rule fires exactly as early as it did before, with nothing reporting it. A count is set aside when the reminder is claimed and restored if that claim is released undelivered, so an aborted turn costs neither the reminder nor the evidence for it; delivery starts the count over.
  • veyyon update replaces the binary that is running, instead of whichever copy of the name PATH resolves first. Measured end to end: a 1.0.47 binary installed under a sandbox home reported New version available: 1.0.48, Checksum verified and ✓ Updated to 1.0.48, yet stayed at 1.0.47 — while a different install nobody had named was overwritten with the 1.0.48 release asset. Both halves are silent, and the machine that produces them is ordinary: the installer prints a warning by name whenever it installs somewhere that is not first on PATH. The target now comes from the running executable whenever this process is the shipped binary, and an install with nothing on PATH at all no longer fails with "Could not resolve veyyon binary path in PATH". A source checkout still resolves its launcher through PATH, because bun sits at the executable path there and a checkout updates by advancing itself.

Removed

  • buildRuleFromMarkdown in capability/rule.ts, a second copy of the rule-frontmatter parser that nothing imported. Every provider, the TTSR CLI and the generated-rule path all go through the one in discovery/helpers.ts, so the copy's only remaining effect was to look like the place a new frontmatter field belongs: a field added there parses in nothing and the rule silently ships without it. parseRuleConditionAndScope, which helpers.ts does import, stays.

@veyyon/simulations

Added

  • New family src/cache-sim/, which prices a prompt-cache change before anyone makes one. It drives the shipped Anthropic request builder to capture the real wire body and the real breakpoints, then bills the result against a modelled provider cache (longest prefix of the arriving request wins, entries expire on the retention they were written with, published read/write multipliers). Scenarios: every counterfactual arm sends byte-identical content so a delta is never a comparison of two different prompts; a system block that changes each turn is measured against the shipped anchor and against a deeper one; a retention switch is priced across gap lengths and shown to have exactly one crossover; and a rewritten earlier message is shown to forfeit the whole history behind it while rewriting the newest message does not.
  • src/cache-sim/ also runs a fleet: several sessions billed against one shared cache, interleaved by simulated time, which is the only shape in which the shipped anchor's justification can be measured. It prices the trade between anchoring the first system block (so a subagent can read the harness its parent cached) and anchoring one block deeper (so a parent with a changing system tail stops re-reading everything in between), and reports the fan-out at which the shallow anchor breaks even. It also shows that an entry is invisible to another session unless its marker carries scope: "global", which no code path sets, so today that break-even is never reached at any fan-out.
  • The fleet scenario also prices its own recommendation under an adverse assumption: no published number says what a shared cache write costs, so the modelled cache takes a globalWritePremium and the scenario re-runs the switch as if a shared write cost the dearest write in the table. Sharing still wins from one subagent, and scoping every system marker rather than the anchor loses outright, because the deepest system marker sits on a block that changes every turn.
  • src/cache-sim/ now covers the implicit-cache surface too, which a scan of the local corpus says is where the tokens are actually lost: of 152,120 judgeable turn pairs, the misses arriving too fast to be expiry whose shape is consistent with a rewritten history forfeit at most 18.5M tokens on implicit-cache providers, against 1.5M on the Anthropic path where every placement effect lives. It drives buildTransformedCodexRequestBody — the same builder the shipped Codex provider calls — and bills it against a modelled implicit cache (1024-token floor, 128-token matching increments, no write price, keyed by prompt_cache_key). It prices what a caller on that surface actually controls: an append-only run reads most of its prompt, a re-serialized early item forfeits every item behind it on a prompt of identical size, the same edit to the newest item costs nothing, and a fresh key per turn forfeits everything however identical the bytes are. It also asserts the surface carries no breakpoint of any kind, since that premise is what makes prefix hygiene the only lever. The scenario header states what those corpus figures bound and what they cannot prove.
  • src/cache-sim/ now sweeps the shipped Codex builder for prefix stability, which is the question the corpus measurement raises and the priced scenarios cannot answer: whether this repository is the author of the rewrite it attributes tokens to. It drives buildTransformedCodexRequestBody over a conversation that grows one step at a time, on both replay paths (a payload the model minted, and the block re-encode a mid-session model switch leaves behind), and asserts every block of turn N comes back byte-identical in turn N+1. It does, so the loss is provider-side or above the builder rather than in it. The content-part variant space is derived from the message unions in @veyyon/ai/types rather than listed, so a new kind fails the type check until someone records a decision for it, and the kinds that cannot reach a Codex request are pinned by exact equality. The scenario also names the same class living unpriced on the Google path, where the signature-retention settings are a distance-from-the-end window whose boundary moves as history grows.

What changed

16 commits since v1.0.48.

Fixes

  • fix(bash-guard): keep yolo's floor for what is certain, not what is guessed
  • fix(simulations): take the zeroed usage from its one owner
  • fix(update): replace the binary that is running, not the first one on PATH
  • fix(rules): make a nudge wait for a habit and speak once per compaction
  • fix(bash-guard): judge an unsettled expansion by the path it can become

Documentation

  • docs(simulations): correct the corpus figures the implicit scenario cites
  • docs(simulations): say which two surfaces cache-sim covers
  • docs(internal): re-stamp the two rulebook docs the warm-up change edited

Tests

  • test(bash-guard): sweep the yolo floor under a recursive delete
  • test(simulations): ask whether this repo is the author of the rewrite it measured
  • test(simulations): price the surface where the tokens are actually being lost
  • test(simulations): price the shared-cache switch under the assumption it is wrong
  • test(simulations): price the shipped cache anchor against the subagents it was chosen for
  • test(simulations): price a prompt-cache change before anyone makes one

Chores

  • chore: bump version to v1.0.49
  • chore(catalog): refresh bundled models from models.dev