v1.4.0
@veyyon/agent-core
Added
- Compaction can truncate the middle of an oversized text, keeping both edges, in any message role — including the roles that store their model-visible text outside
content, such as a shell cell'soutput, a summary'ssummaryand a file mention'sfiles[i].content. pruneSupersededToolResultsacceptscacheWarmSuffixTokens, a hard ceiling on the sent context a rewrite may sit behind; a candidate over the ceiling is never rewritten, including as part of a batch the cache math would otherwise pay for.
Fixed
- A compaction summary is written from a transcript with the model's own reasoning removed, on every dialect and with none, which was quoting thinking back at the endpoint and drawing a
reasoning_extractionrefusal. - Codex remote compaction requests declare the
responses_compaction_v2implementation, matching the{base}/codex/responsesroute they are sent to. - A ChatGPT Codex server-side compaction now reduces the context it was paid to reduce: its stored window was not on the list of apis whose window can be replayed, so the entry counted as unusable, the whole pre-compaction span was re-expanded on the next rebuild, and the session crossed the threshold and compacted again on every turn.
- Compaction shake keeps the image blocks in a tool result instead of discarding them with the text it replaces.
- Remote compaction forwards the session's prompt cache key to the provider, so a session whose cache key differs from its session id compacts on the same cache lineage its turns use.
@veyyon/ai
Added
- Every OpenCode request carries a
Veyyon/<version>user agent and anx-opencode-sessionheader derived from the conversation id, on theanthropic-messages,openai-completionsandopenai-responsestransports and on server-side compaction, under every cache-retention setting, which the gateway requires to identify the client and route a conversation to its warm prompt cache.
Changed
- Every provider row with a
logindeclarescredential: "api-key" | "oauth", andOAuthProviderInfoandgetLoginCredential(providerId)expose it, so a login surface can tell a dashboard URL from an authorization it must open;OpenAIis namedOpenAI PlatformandOpenAI Codexis namedChatGPT (Codex subscription). - The compaction transport and codex request comments state the route each host family serves. No behavior change.
Fixed
- The Alibaba Coding Plan and Qwen Portal logins validate the pasted key through the controller's
fetchwhen one is supplied, instead of always using the globalfetchand bypassing a configured proxy. - Reasoning recorded behind a rewritten history prefix is dropped before the request on a model that binds thinking blocks to their prefix, instead of being replayed and answered with a 400.
- ChatGPT Codex server-side compaction streams a trailing
compaction_triggeritem to{base}/codex/responses, instead of posting to{base}/codex/responses/compact, which answers 404 and turned every compaction into a paid local pass for the rest of the session. - A Codex compaction stream carrying a
compactionitem with noencrypted_contentis refused instead of being stored as a window every later turn discards. - A compaction route that answers 404 is retried after 30 minutes instead of standing the model down for the whole process, so one transient 404 no longer forces a paid local compaction on every later pass.
- Codex remote compaction keeps at least one user turn when the retained-token budget it is handed is not a finite number, instead of replaying a window holding nothing but the compaction item.
- A Codex server-side compaction sends the session's
prompt_cache_key, so it lands on the session's cached prefix instead of missing it and making the next turn re-pay full uncached input. - A Codex server-side compaction with no session id is refused instead of minting a random conversation identity, which opened a second cache lineage and left the post-compaction history reset with nothing to find.
- The Claude Code user-agent reports
agent-sdk/0.3.257, the Agent SDK release paired with the Claude Code version the same request sends. - With
accounts.loadBalancingoff, one account per provider and credential type serves every session: the explicit choice, else the session's sticky account, else the first stored row. The resolver no longer hashes a new session across accounts, ranks accounts by headroom, or skips a rate-limited or out-of-quota account for a sibling; the session waits on its own account, and only a refused grant moves it, announced throughonCredentialFailover. Turning the setting on restores the previous rotation and ranking. - A signing Anthropic endpoint that answers
stop_reason: "refusal"with categoryreasoning_extractionhas prior-turn reasoning dropped from an immediate retry and from the rest of the session, instead of failing the turn withRefusal (reasoning_extraction)every time reasoning demoted to prose is replayed. - A tool result whose originating call is no longer in the request reaches the model as a
user-role<stale-tool-result>note instead of as assistant text, so a model no longer reads a tool result in its own voice and reproduces one as a visible message. - A resumed session drops an
[Orphan …]or[Previous …]tool-result note already persisted as assistant text, so history recorded before that repair was fixed stops prompting the same visible tool-result message on every later turn. - The persisted note is also removed from the turn's transport-native items, which an
openai-responsesor Codex session replays in preference to the text block once warm, so a live session stops re-priming the imitation on every request after the first one instead of accumulating dozens. - Stripping a persisted note keeps the reply the model wrote after its byte-exact copy of the tool output, instead of dropping the whole block with the reply in it.
- Every fold of a tool result whose call is missing from the request is logged with the call id and the ids the request does carry, so the loss of a pairing can be traced from the log instead of only from its symptom.
- A
<stale-tool-result>note escapes the tool name and call id it reports, so a call id carrying a quote cannot close the attribute and hand the model forged markup.
@veyyon/catalog
Added
- A model's thinking config carries
prefixBinding, set for Claude 5.1 and later, stating that its thinking blocks are bound to the conversation prefix they were produced against. - An
anthropic-messagesmodel's compat carriesreplayDemotedPriorReasoning, which drops prior-turn reasoning on a signing endpoint instead of replaying it as demoted prose. - The bundled ChatGPT Codex catalog carries
gpt-6-astraandgpt-reserve, each with the low-through-max effort ladder and the freeformapply_patchtool the endpoint declares.
Changed
- The server-side compaction capability comment states the route the ChatGPT Codex backend actually serves. No behavior change.
Fixed
- The Claude Code fingerprint version is 2.1.257, so Anthropic OAuth requests for current models are no longer rejected with
claude_code_version_too_old. - ChatGPT Codex discovery identifies as Codex CLI 0.153.2, the floor
gpt-6-astrarequires, so a subscription that has the model lists it instead of receiving the pre-0.153 model set. - A ChatGPT Codex model's effort ladder and
apply_patchtool type come from thesupported_reasoning_levelsandapply_patch_tool_typethe endpoint declares for that row, so a newly listed SKU offers effort control at once instead of arriving with none until models.dev catalogs it. - Antigravity discovery gives
gemini-3.8-flash-tieredthe same effort surface as 3.7: the endpoint serves 3.8 Flash only under that wire id, with no bare id and no per-tier siblings, so the row arrived raw with no effort levels. It now collapses to a logicalgemini-3.8-flashrow carrying the low/medium/high ladder models.dev declares forgoogle/gemini-3.8-flashon thegoogle-leveltransport, and its wire profile pins the 65536 output cap the endpoint reports. - OpenCode Zen and Go discovery resolves the wire API of an id the bundle predates from live models.dev, so
muse-spark-1.3-contributor-freeandmuse-spark-1.3-contributorroute to/responsesinstead of failing with HTTP 500 on/chat/completions; the bundle also carries both rows. - OpenCode Zen and Go model discovery sends the
Veyyon/<version>user agent the gateway requires, so a discovery request is no longer unlabeled traffic that can be filtered into an empty model picker. - An OpenCode Zen or Go discovery whose models.dev lookup fails falls back to the bundle without reporting a discovery failure, so a working gateway listing is no longer warned about as a provider that could not be discovered.
@veyyon/coding-agent
Added
- The models page on veyyon.dev lists every provider and model veyyon supports, read live from the bundled catalog and auto-refreshed from the repository;
bun run site:buildregenerateswebsite/models-data.jsonfrompackages/catalog/src/models.json. - A bare interactive launch replays the previous launch's card from a cache before the CLI's import graph is evaluated, then adopts those rows and corrects only what changed.
bun scripts/bench-startup.ts --runs 5 --bin packages/coding-agent/dist/veyreports the card's first byte at a 34-35ms median composed and a 15ms median replayed. The recording is discarded unless the terminal size, the environment this process received and the binary's path, size and modification time all still match, and it ages out after 24 hours; a launch whose composed card disagrees with the replayed rows drops the recording so the next launch records a fresh one. bun scripts/bench-startup.tsgains areplayarm and keeps the first-frame recording inside its scratch directory. The recording resolves its path fromos.homedir(), which Bun fixes at process start, so the bench's seededHOMEdid not reach it and a run read and overwrote the operator's own cache.VEYYON_REPLAY_DEBUGnames a file the launch appends its replay decision to. A rejected recording is otherwise indistinguishable from a slow launch, and the logger does not exist yet at that point./rephraseasks for the reply on screen again in plainer prose, and refuses unless the conversation is resting on a finished reply./autoswarmopens a setup console for the goal, breadth, attempts and certification, then runs autoresearch with breadth: each iteration builds several candidate arms, rejects the ones that are empty, out of scope, unreadable or duplicates, has the survivors cross-review each other, and keeps at most one;/autoresearchis unchanged and still serial.- The autoswarm setup console assigns a model per arm, comma separated in arm order, so one iteration compares models as well as ideas;
start_armswitches the session to that arm's model before its first edit, the status row names the arm and the model building it, and the session returns to its own model when the arm is logged or the mode is left. - An autoswarm run records the model in force when
run_experimentmeasured it and the run screen shows it under the arm, so a round that stayed on one model is distinguishable from a model comparison; measuring an arm thatstart_armnever announced, or one that is not the arm in flight, states which model actually built it. - The autoswarm setup console accepts a pasted model list that carries a newline, which was rejected whole and inserted nothing.
- Autoresearch and autoswarm have handbook pages.
- Machine-wide resource limits cap CPU, memory, disk writes and process count across every veyyon process at once, beside the existing per-session limits, in
/settingsunder Resources; both scopes default to no limit. - The two resource-limit scopes share one definition of each cgroup control-file format, with no user-visible change: the duplicate the machine scope carried while unreleased could write a freeze quota for a very small CPU budget.
bun run test:cgroup-proofdrives both resource-limit scopes against a real kernel outside the test sandbox and reports each cap as held or not, refusing with a named reason on a host that cannot delegate cgroups rather than passing having proved nothing.- The run screen's session pane shows the number of runs since the segment's best one as
Since 8 runs later, none better, and aTrendrow of one block per run, oldest on the left, scaled between the segment's lowest and highest measurements; an unmeasured run draws as·, and a segment wider than the pane draws its most recent runs behind a leading…. - The autoresearch status row shows the same gap as
2 since best, and drops that segment before the best metric when the terminal is too narrow for both. - The autoswarm setup console states under its title what Enter does on this branch: resumes the named session with its run count, or starts one, with whether
autoresearch.shexists or the first turn writes it; over a session the legend readsenter resume. /autoresearch resumeand/autoswarm resumecontinue an interrupted or paused session without a console or a transcript line, are offered in argument completion, and print what would start one when the branch has no session.- An interrupted autoresearch loop puts
paused · send a message to resumeon the status row and(paused)in the run screen title until a turn resumes it, and the interrupt notice names/autoresearch resumeand/autoresearch offin full rather than a bareoff. - The run screen title is fitted to its border before the border truncates it, so a long session name gives way and
(paused)or(mode off)stays readable instead of ending in(paus…. /autoresearch <text>on a live session where<text>is the stored goal is a resume with nothing to add: it neither prints a context notice nor sends the goal to the model a second time.- A file search accepts
pathas the directory itsinputglobs are searched under, the waypathscopes a text search:{ type: "files", input: "*.ts", path: "src" }issrc/**/*.ts, eachinputentry keeps its own depth, and a scope that is a glob or an input that is absolute is rejected naming the spelling that works. - Browser tool calls batched in one turn run in the order written;
runandcloseon the same tab used to start together and the run found its tab closed. ExtensionAPI.setModel(model, { ephemeral: true })switches the session's model for the rest of the turn without recording it as the session's own model, so a resumed session opens on the model the operator chose.
Changed
- The hidden messages a session injects and the gates that fire them live in
session/nudges.tsrather than as file-private literals inagent-session.ts, and the mid-run todo and prewalk gates key their tool tables through theTOOLregistry so renaming a tool now fails to compile instead of silently disabling the nudge. - The subagent model resolver reads the shared-model switch once and builds the layers of whichever scope that selects, instead of gating each layer against a second reading of it, and the unread
SUBAGENT_MODEL_SCOPE_VERSIONconstant is gone from the rejected-setting log record. No change to what any subagent runs. - The compaction policy vocabulary (check outcomes, the bar a pass is measured against, the truncation edge budget, the prune cache window and idle flush, and the recovery band) lives in
session/compaction-policy.tsrather than interleaved with shutdown timeouts and credential backoffs inagent-session.ts, with no behavior change. - The thinking display's hidden-code fence marker is named
hiddenCodeMarker, no longer colliding with the unrelatedelisionMarkerin@veyyon/utils/byte-truncate; the marker text is unchanged. - The
readtool's URL routing, internal-URL routing, notebook reading, document conversion and structural summarizing each live in their own named method instead of inline branches of one 758-lineexecute, with no change to what any read returns. - The doc comments on the agent registry's conversation scope and address check, and on the subagent model and thinking-level resolvers, state the precedence and the boundary rule without the surrounding narrative; no behavior changes.
- The queued-message predicates and the
retry.fallbackChainsselector vocabulary live insession/queued-message.tsandsession/retry-fallback.tsinstead of as file-private helpers inagent-session.ts;RestoredQueuedMessageis imported from the first of those. - The ACP permission gate's argument reading — which tools need consent, the prompt each call earns and the file locations it points at — lives in
session/permission-intent.tsinstead of as file-private helpers inagent-session.ts, which also drops that file's private copy ofgetStringPropertyin favour of the@veyyon/utilsowner. No change to what is gated or prompted. - The launch card paints about 8ms sooner: the bundled themes are embedded as text and parsed on the ask instead of building all 98 before the first frame, and the card path no longer evaluates
node:assert/strict,node:crypto,node:inspector,node:child_processornode:zlibfor calls it does not make. - The status row reads its truncation limits from
tools/render-limits, a leaf that imports nothing, rather thantools/render-utils, which drops the tool renderers, path helpers and image resizing from the launch card's import graph; first-frame time is unchanged, because those modules only declare functions. - The home screen hero drops the recent-session row;
/welcomestill lists recent sessions. - The launch card paints the whole status row from config instead of a hand-written path and branch, so the profile, model, approval rung, branch and context gauge are on screen with the first frame; measured on a pty against the built binary, the row lands at 47-48ms rather than 1067-1083ms and the frame is editable at 49-50ms.
- The status row's segment gathering and row fitting live in one module,
modes/components/status-line/quiet-row.ts, which the live footline and the launch card both render through, so the two rows cannot carry different segments or order them differently. - A status-line segment reads a flat
SessionFactsvalue block rather than theAgentSessionitself, which is what lets the same segment table render before a session exists. No change to the mounted row. bun scripts/bench-startup.tsrenames itsstatuslinearm tostatusrowand times the status row being on screen; the old arm timed a segment only a session supplied and now reports nothing, because the card supplies the row.- The autoswarm setup console, the autoresearch experiment tool and the run screen's stacked split clamp their breadth, attempt and row counts through the shared clamp rather than local copies. No behavior change.
bun scripts/bench-startup.tstimes itsstatusrowandreplay:statusrowarms against the approval rung on the row rather than the context gauge, which is the row's last segment and is shed on the bench's eighty-column pty: both arms reported no samples at all. They now read a 39ms median composed and 15ms replayed against the built binary.- The autoswarm setup console and the autoresearch experiment tool clamp their breadth and attempt counts through the shared clamp rather than local copies. No behavior change.
VEYYON_TIMINGreports the window between process start and the launch card instead of hiding it: the tree now starts at the CLI entry and carries spans for the command load, the launch-card import, the prologue, settings, the theme and the paint, leaving only Bun's own start and the entry's static imports under(before instrumentation).- The launch card arrives in about half the time. The binary is now code-split, so the standalone loader links the CLI entry and the launch card instead of the bytecode of every subcommand, tool and agent-runtime module before the first statement runs, and whitespace and syntax minification are on. Measured warm on a pty, the card's first byte goes from 138-151ms to 57-72ms, the first keystroke echoes at 111ms instead of 188-207ms, and the binary is 231.7MB instead of 296.9MB. Function names are still kept, so a stack trace is unchanged.
- The six modules on the launch path that reached for the
@veyyon/utilsbarrel now import the subpath that owns what they use, so painting the card no longer evaluates the YAML parser, Handlebars and the prompt-variable layer that the barrel re-exports. The launch card's import graph drops from 311 modules to 268, its import span from 32.5ms to 20.2ms, and the card's first byte to 50-58ms. - The launch shell no longer evaluates a workspace barrel. The launch card, the editor and the status row reached
@veyyon/tui,@veyyon/utilsand@veyyon/agent-corefor a handful of symbols, and each barrel isexport * fromover its whole package; every such edge now names the leaf that owns the symbol. Three modules were split along the same line:internal-urls/resolve-sync.tstakes the URL resolver out of the OSC 8 writer,modes/image-reference-markers.tstakes marker rendering out of the blob store, andmodes/magic-keyword-notices.tstakes the three hidden notices out of keyword detection. Module evaluation for the three shell entries drops from 147ms to 63ms, the editor from 36ms to 3.6ms and the status row from 78ms to 6.5ms. - The status row no longer evaluates the agent runtime, the compaction engine, the provider error registry or the bundled model catalog before the composer takes a keystroke.
session/context-usage.tsreached the@veyyon/agent-corebarrel forcountTokens, itscompactionbarrel forestimateTokensand@veyyon/ai/utils/schemafor two wire helpers, andconfig/inline-tool-descriptors-mode.tsreached@veyyon/catalog/identityfor one family token; each now names the leaf that declares the symbol. The three shell entries evaluate 333 modules rather than 583. - The status row reads its premium-request formatter from
@veyyon/utils/formatinstead of@veyyon/stats/format, so painting the card no longer evaluates the stats package. No visible change to the row. - The status row's non-message token accounting lives in
session/non-message-tokens.ts, so painting the launch card no longer evaluates the compaction layer or the tokenizer throughsession/context-usage.ts, andconfig/inline-tool-descriptors-mode.tsreadsmodelFamilyTokenfrom@veyyon/catalog/identity/familyinstead of the identity barrel. No change to the counts the row reports. - The host capability probe and the environment it measures against moved out of the session budget module into
session/cgroup-host.ts, and the capabilities a probe reports no longer carry the field it used to pick a cgroup parent. No behavior change. - The first-frame recording is written by
startup/first-frame-recorder.tsthrough the sharedatomicWriteFileSyncinstead of a hand-rolled temp-and-rename, so the cache file is created owner-readable only; the replay reader on the boot path still reaches node builtins only. - A cold launch states the context gauge as soon as the session knows its usage instead of when the live status row mounts: the at-rest context record fires on session construction and the launch card repaints on it. A warm launch repaints nothing.
- The launch card paints the working directory on the status row instead of leaving it blank until the session mounts. Measured on a pty, the row named the directory at 59-62ms with the card rather than at 1067-1083ms; the model, mode and context gauge still arrive with the session, to the right of it.
- The status row's location is rendered by one owner,
modes/components/status-line/location.ts, which the live row and the launch card both call, so the two cannot drift.shortenPathandsanitizeStatusTextmoved to their own modules, re-exported fromtools/render-utils.tsandmodes/shared.ts;defaultDisplayRootsandresolveDisplayRootsare now imported from the location module rather than fromsegments.ts. - The launch card paints the git branch on the status row beside the working directory. Measured on a pty, the branch appeared at 59-66ms with the card rather than at 1067-1083ms.
- The status row's branch is rendered by one owner,
modes/components/status-line/branch.ts, which the live row and the launch card both call, so the two cannot drift. - Which segments a status-line preset shows is resolved by one function,
resolvePresetSegments, so the launch card honors the same preset andgit.enabledrules the mounted row does. No behavior change to the mounted row. - The branch a repository is on can be read from
.gitalone throughutils/git-head.ts, without running git;utils/git.tscomposes that reader with thegit symbolic-reffallback a reftable repository needs, rather than keeping a second copy of the file parsing. StatusLineComponent.watchBranchiswatchGitState: one repaint request for every git read the row is painted from, rather than a name that described only the HEAD watcher. No user-visible change.- The model and effort a subagent runs are chosen on one of two exclusive scopes. Subagents → Same Model for All Subagents is off by default, and each agent's page under Roster decides; on, Shared Model and Shared Effort decide for every agent while the per-agent rows are hidden and keep what they hold for when the switch goes off. An agent naming neither runs the profile's default model role at medium effort, and
subagent.modelByDepthdecides nothing in either scope. - A subagent definition that names a tool nobody recognizes is reported with the file and the unknown names instead of loading an agent with no tools and a prompt pruned of everything.
- User-authored subagents are discovered from
~/.veyyon/subagents/, shared across profiles, and enabled per profile. - The compaction loader names the engine on every pass, not only a remote one: "Compacting context... (local compaction)", "(openai remote compaction)", "(azure remote compaction)" or "(codex remote compaction)". A codex or azure server-side pass used to be announced as openai, and a local pass was not announced at all.
- The model the advisor runs is asked in one place: Model → Advisor → Advisor Model, directly under Enable Advisor, hidden while the advisor is off.
advisoris gone from the Roles table, which was a second surface for the same slot under a different name; the slot itself is unchanged, so@advisorand any existingmodelRoles.advisorkeep working. - The Advisor rows are contiguous in the Model tab: the Prewalk rows were declared between them.
- The resolved-model snapshot a warm launch restores is verified against the bytes it was written from instead of being parsed and serialized back to check itself. The file is now a header line then the stage payload, and reading it costs 14.6ms instead of 27.6ms measured on a 9.2MB snapshot; a snapshot written by an earlier version misses and is rebuilt.
- The launch card paints the real composer instead of a drawing of one. Typing at the card edits the editor the session goes on to use, so nothing has to be replayed into it at handover, and a submit typed before the session exists no longer discards the draft. Measured warm on a pty, a keystroke at the card echoes 3.2ms after the card's first byte.
- The settings store holds no database handle.
AgentStorage.forAgentDiris the one owner of the run's agent.db and opens it on first use,config/legacy-agent-db-settings.tsowns the first-run read of the pre-config.ymlsettingstable, and the launch card no longer evaluatesbun:sqliteor the SQLite credential store to read a setting. Measured warm on a pty, the card's first byte goes from 50.5ms to 42.4ms and a keystroke is echoed at 45.6ms instead of 53.6ms; a database that will not open now costs usage statistics with a logged reason rather than failing the launch. - The three hidden magic-keyword notices are in
session/magic-keyword-notices.tsrather than undermodes/, which was the one edge from the session into the UI directory with no drawing behind it. No behavior change. - A running autoresearch or autoswarm loop occupies one status row instead of a widget above the composer, and
ctrl+xopens a run screen listing the session, the playbook, the run in flight and every logged run, with the highlighted entry in full beside it. - A bare
/autoresearchopens the run screen instead of leaving the mode;offstill leaves it. /autoresearch clearand/autoswarm clearask before resetting the worktree, naming the baseline commit and how many files hold uncommitted changes, and reject an unrecognized flag instead of resetting./autoresearchand/autoswarmlist theiroffandclearsubcommands before a letter is typed, andclearoffers--keep-treeand--reset-tree.- A run records the arm that produced it and the arm that certified it, both shown on the run screen.
- Each row of the run screen's list states the run number, the arm that produced it, its metric, its change against the baseline of its own segment, and whether it is the baseline, the leader, kept, dropped, crashed, failed or flagged.
- The run screen's session pane opens with the best measurement of the segment, its change against the measurement the segment started from, and the run and arm that produced it.
- The autoresearch status row prints the best metric with its change against the segment baseline, and sheds the pair together.
- The autoswarm setup console legend lists only the keys that act on the focused field and the range its arrow keys move within.
- The autoswarm setup console states the harness runs one iteration costs, as
3 arms × 3 attempts: up to 9 harness runs per iteration. - The autoswarm setup console legend reads
enter needs a goalwhile the goal is empty, and Enter starts nothing. - The run screen's session pane states the count of runs logged since the best one.
- The autoresearch status row states the count of runs logged since the best one, and sheds it before the best it qualifies.
- The run screen's session pane draws the segment's measurements as a row of blocks, one per run, with a gap for a run the harness never measured.
- The run screen's list states the run in flight and the run awaiting a log by number, with the harness result on the unlogged row.
- The subagent model resolver states the layer numbering its per-agent chain actually resolves. No behavior change.
- The status row no longer carries the secrets segment. The
secretsid is gone from every preset and fromstatusLine.segments, and a configuration naming it is rejected;/secret liststates what a session has masked. - The roster states that an operator may write an agent, and names
docs/features/subagents-authoringas the instructions. - The subagent authoring page states which frontmatter key spellings are read:
thinkingLevelandthinking-levelreach the same field, an underscore does not, and the bundled definitions use the dashed form. - The rewind and checkpoint entry readers and the side-channel reply bound live in
session/rewind-checkpoint.tsandsession/ephemeral-reply.tsinstead of as file-private helpers inagent-session.ts; the doc comment describing the Anthropic request metadata payload is attached to the function it describes rather than to the tool-order check below it. No behavior change. session/content-text.tsis gone; the session modules that flattened content blocks call the@veyyon/utilsowner, which now carries the options that copy held. Two implementations of the same flattening each documented themselves as the only one.- The MCP command controller races its OAuth login and its connection wait through
withTimeoutandraceWithTimeoutin@veyyon/utilsrather than a file-private copy, and the protocol probe's truecolor bar converts hue throughhsvToRgbin@veyyon/utils/colorrather than a second implementation of the same conversion. Both emit what they emitted before, byte for byte.
Fixed
/loginfor a provider that takes a pasted API key (OpenAI Platform, Groq, xAI and every other key provider) shows the dashboard where a key is obtained and prompts for the paste, instead of opening a platform login page in the browser as if it were an OAuth sign-in; the setup wizard likewise neither opens nor copies that URL.- The
/loginpicker printsapi keyorbrowser loginbeside every provider, and the two OpenAI rows readOpenAI PlatformandChatGPT (Codex subscription), so a key paste and a subscription sign-in are told apart before one is chosen. vey auth-broker login <provider>printsGet an API key at:for a key provider instead ofOpen this URL in your browser:.- The RPC
open_urllogin request carriescredential("api-key"or"oauth") so a host opens only the URL a flow waits on;RpcClient.login'sonOpenUrlreceives it as a fourth argument. - Cancelling or failing a sign-in in the setup wizard clears the provider search that started it, so the next keystroke begins a new search rather than appending to the old one and matching nothing.
- Tool renderers sanitize raw tabs and shorten embedded home directory paths across error, output, and fallback views.
- A
jobpoll whose job failed lists each job with its own error again, instead of collapsing to the result text. - Print mode exits with code 130 rather than 1 when a turn is cancelled or aborted.
- The web search command exits with code 2 on missing or invalid query arguments.
- The TTSR command exits with code 2 on missing required subcommand parameters or unrecognized actions.
- The shell command exits with code 2 when invoked without an interactive terminal.
- CLI file argument processing reads attachments via portable node:fs promises rather than runtime-specific file methods.
- The subagent HUD retains the model badge for running tasks without a description when space is available, rather than dropping it against an unused description floor.
- Deleting a session in the session selector removes it from the folder and all-projects lists so toggling scope does not restore the deleted entry.
- ModelSelectorPanel displays 'No matching models' when an active query yields zero results instead of the unprompted 'No models available' login hint.
- The model browser replaces a Bun ANSI-stripping call with the portable node:util helper on disabled model rows.
- The model picker avoids re-fetching and re-sorting the catalog on every query keystroke.
- The thinking effort selector preselects the default row when initialized without a level override.
veyyon auth-gateway servehonoursaccounts.loadBalancing: with it off, one account per provider serves every request the gateway forwards, where before the gateway rotated on each of the several credential reads a single request makes.- An autoswarm arm's model switch is ephemeral: quitting mid-arm and resuming the session no longer opens on the arm's model with nothing to restore the session's own.
- An overlay box narrower than six columns no longer draws a title wider than its own border.
- A warning repeated verbatim with nothing between the two, as the compaction dead-end notice was at the top of a resumed turn, renders once.
- Settings selector displays a dimmed '(unset)' placeholder for optional text settings with empty values.
- The subagent roster list bounds item rows and wraps custom agent hints so the list and navigation controls fit within the modal viewport.
- The settings selector passes the target pane width to the status line preview.
- The autoswarm setup console handles Unicode emojis, CJK characters and combining marks on backspace and windowing without splitting surrogate pairs, and supports Shift-Tab field navigation.
- Autoswarm triage rejects candidate arms whose edits fall inside off-limits directories, where an exact string comparison only matched exact path literals.
- Autoswarm review assigns the director as the sole reviewer when certification is disabled on the session, instead of creating peer review rings for three or more surviving arms.
- Autoresearch storage rejects completing or logging an abandoned run, and rejects duplicate log calls on an already logged run.
/autoresearch statusand/autoswarm statusopen the run screen, where the word was swallowed as the goal and overwrote what a live session was optimizing.- A live session's goal changes only through
goal <text>or the setup console, so any other text after the command reaches the model as context for the resume and leaves the stored goal as it was. /autoresearchand/autoswarmofferstatusandgoalin their argument completions, where onlyoffandclearwere listed and every other word reached the goal write.- Checking out a branch other than the one an autoresearch or autoswarm session records now shows
paused · session on <branch>on the status row and keeps the session's runs readable throughctrl+x, where the session was looked up by its branch alone and so read asbaseline pendingwith no runs whenever it was resumed off that branch. - An autoresearch or autoswarm loop re-reads the branch at the start of every turn, so checking the session's branch back out lifts the pause and restores the experiment tools, where a checkout mid-conversation went unnoticed and the loop kept its tools and system prompt on a branch it was not on.
- Turning an autoresearch or autoswarm loop off while it is paused clears the pause, so the status row stops naming a branch to return to and reports the mode as off.
- Leaving the autoswarm setup console with Escape reports that nothing was started, where it returned to the prompt without a word.
- Pressing Escape during an autoresearch or autoswarm turn pauses the loop until the next message, where a measurement waiting to be logged made the loop resume itself on the very next turn and the interrupt did nothing.
- A hidden continuation that was waiting for the agent to go idle no longer starts a turn after Escape ends the one it was waiting on, where an autoresearch stall nudge queued a turn earlier restarted the stopped loop three milliseconds after the interrupt.
- An autoresearch or autoswarm loop no longer diagnoses a stall when the turn that ended was a session continuation and the loop's own resume has just been accepted behind it, where the false stall queued a second hidden turn and counted against the budget that turns the loop off.
/autoswarm <text>over a live session opens the console on the session's stored goal, where it prefilled the typed text and Enter overwrote the goal with a message meant as context for the resume.- A slash command that opens a console or a prompt while the agent is idle no longer sits under the
Working…loader and its elapsed clock; a prompt raised mid-turn keeps the turn's loader. - The autoswarm setup console's breadth, attempts and certification answers outrank the
breadth,attemptsandcertifyarguments the model passes toinit_experiment, where a guessedbreadth: 1turned a configured swarm into a serial loop; the tool's reply states the configuration in force and that the console set it. - A metric unit that is a word is spaced off the number (
1,596,000 comparisons) while a symbol or abbreviation stays attached (192.78ms), and ametric_unitthat repeats the metric's own name is dropped, where every surface printed1,596,000comparisons. - The autoresearch status row pluralizes its run and arm counts (
1 run,2 arms), where it read1 runs. log_experimentrejectsstatus: "keep"for a run that did not improve on the segment's best unless ajustificationis given, where a loop kept eight flat runs in a row as eight commits that changed nothing the harness could see.skill://<name>andskill://<name>/SKILL.mdread a skill whoseSKILL.mdis a symlink into a directory outside the skill's own, where the read was refused as an escape of the skill root; every other file under the skill still has to resolve inside it./account status,/account refresh,/lspand/trustprint their report as a titled transcript block at the rail, where the lines landed as a bare paragraph that read like an assistant message.- Logging an arm whose model restore is refused, because the session model lost its key mid-run, reports that the session is still on the arm's model instead of recording the restore as done, and the next arm's restore target stays the session's own model rather than the arm it was stranded on.
- An autoresearch or autoswarm turn that ends with no experiment tool run, no measurement waiting and no resume armed is steered back to the loop on the next turn; after three such turns in a row the loop turns itself off and states how to reach the session's runs, where the mode stayed on and the status row read as a live experiment until the user typed something.
/autoswarm goal <text>opens the setup console with<text>in the goal field, where the field readgoal <text>and that literal was written as the goal on start./autoresearch clear --keep-tree, and aclearoff anautoresearch/*branch, leave every worktree file in place, where both deletedautoresearch.shand the other loop files from the worktree; a confirmed reset restores the committed harness and removes only the legacy files the prompt forbids, and aclearon a project that never ran a loop no longer creates the run store.- The interactive console (
bashwith a PTY), the autoresearch dashboard and every otherui.custom(..., { overlay: true })surface sit above the composer zone instead of covering the whole screen: the prompt, status line and footline stay painted while the overlay is up, and the console box is sized to the rows above them. - A session on a model whose thinking blocks are bound to their conversation prefix, such as Claude 5.1 and later, leaves an already-sent tool result in place instead of superseding it mid-history, which invalidated every thinking block recorded after it and drew a 400 on the next turn.
- The installer refuses to replace a binary whose only ownership record is a pre-identity v1 receipt, instead of moving it aside. That receipt vouches for the path alone, so a user who deleted the installed binary and put their own file at the name left exactly one behind, and it was being read as permission to displace their file.
veyyon agents unpackwrites to~/.veyyon/subagents, the directory subagent discovery reads, instead of the profile'sagent/agentsdir, where an unpacked definition was reported as written and then never loaded.veyyon config setandveyyon config resetno longer exit 1 with an ENOENT on a profile that has never been launched: the save now creates the profile's agent directory before it takes the config file's lock, which refused a parent directory that did not exist yet.- A
/btwor/omfgreply long enough to be truncated no longer ends in a replacement character when the cut lands inside an emoji: the trim counts UTF-8 bytes but removes UTF-16 code units, so it could stop on half a surrogate pair. - A malformed content block no longer opens a blank line in a session listing or in rendered history. A text block whose
textis absent or is not a string contributed an empty part, so the join placed a separator beside it, while a thinking or tool block carrying just as little text was skipped outright. - The composer hairline and the transcript rules no longer change shade about half a second after the launch card appears: the card mixes them out of the background this terminal reported on the previous launch instead of a static token, and
tui.paintGroundonautodecides the paint from that same recorded background rather than repainting the whole window when the terminal answers. The background is recorded per terminal incache/launch-facts.json, whose shape version is now 4. - The launch card's context gauge no longer jumps when the session mounts in a project it has never measured: the reading filed under the model is now that reading with the measuring project's context files subtracted, so a card seeded in a heavy repository no longer states 77% left where the session settles at 88%. A project's own reading is unchanged and still wins where it exists.
- The session mount no longer forces a full-viewport repaint over the launch card, so the screen no longer flashes and darkens at handover; the mount now writes only the rows whose content changed.
veyyon agents unpackwrites bundled definitions to~/.veyyon/subagents, the directory agent discovery reads, instead of the active profile'sagent/agents, where nothing loaded them; the--userand--projectflags are gone, and--dirstill writes to a path you name.- The launch card states the model name, git state and context percentage recorded at the end of the last launch instead of placeholders, so the status row and hero do not change when the session mounts; each fact falls back to its placeholder when the release, the model or the project changed.
- The launch card's status row resolves a linked git worktree the same way the live row does, so the path the card paints, its
◫marker and the truncation it forces survive the session mount without being rewritten. - The launch composer's footline reads the draft's token estimate from the same editor the session mounts, so the right zone is on screen with the first keystroke instead of appearing when the session does.
certify_armsis attached only to a session whose breadth is above 1, so a serial autoresearch loop is no longer offered a tool that triages arms it does not have.- An autoswarm winner has to beat the segment's baseline, not merely the worst of its sibling arms: an iteration where every arm regressed reported a winner, which was then logged as an improvement and re-applied.
/autoresearch clear --keeptreeand any other misspelling of--keep-treeno longer fall through to resetting the worktree, which is the opposite of what the flag selects.- The autoswarm setup console keeps its hint column still while a value is adjusted, and wraps its explanatory line instead of cutting it on a narrow terminal.
- The autoresearch status row reads the breadth once, so the first turn of a swarm no longer prints
autoswarmwith no arm count: the name came from the configured breadth and the arm count from the stored session, which do not agree untilinit_experimentruns. - The run screen keeps
esc closeon its footer at every terminal width, shedding the navigation hints first, instead of truncating the exit hint off the right edge of a narrow terminal. - The autoresearch status row drops whole segments, least informative first, when the terminal is narrower than the row, and rebuilds on a resize, so the
ctrl+x runschord that opens the run screen is on screen at every width. - A crashed run reports what its harness measured before it died, and states that it has no metric when the harness measured nothing, instead of formatting the zero the log call was required to supply as a measurement, which listed a segfault as the fastest result of a session where lower is better.
- The run screen lists each metric once: the primary metric a log call repeats among its secondary metrics is no longer stored or rendered as a secondary of itself, which printed a second unitless copy of every measurement and a
-100.0%row for a crashed run. - Every surface reporting the best run of a segment names the same run. The run list, the session pane, the status row and the prompt built from the session each ran their own scan, and two of them counted a run logged with the placeholder zero the log call requires of a run that measured nothing; where lower is better that zero is the best value there is, so the list tagged a run
bestthat the pane beside it did not. - The run screen prints the artifacts directory with the home directory collapsed to
~, so a screenshot of a loop no longer states the account name and profile layout it ran under. - The run screen fits a terminal shorter than fourteen rows: it wrote a fixed fourteen rows whatever the height, so a ten-row terminal lost the four rows holding the footer and the border.
- The run screen fits a terminal narrower than 29 columns: the sidebar is sized from the available width rather than its own floor, and every row is cut to the width it was handed, so the border no longer wraps into rows the card did not count.
- The run screen stacks its list above its detail on a terminal too narrow to carry both beside each other, instead of leaving the detail two columns wide and printing every field of the selected run as a single letter and an ellipsis.
- The run screen names an autoswarm the setup console configured before the first
init_experimentcall, instead of titling it "Autoresearch" and reporting "serial, no arms and no review" for a swarm whose breadth was already set. /autoresearch clearresets to the baseline of the session on the branch it is standing on rather than the newest open session, so a second worktree on another branch no longer selects the commit this one is reset to, nor has its session closed instead.- The run screen pages its detail pane with pgup and pgdn, and closes on escape only:
qclosed the surface, so filtering the run list for a label containing a q lost the screen mid-keystroke. - The status row of a run in flight advances its elapsed time once a second instead of freezing at the value it had when the run started, and its clock stops as soon as no run is in flight and the run screen is closed.
- A search filter typed into the run screen survives the loop logging a run and the clock advancing, instead of being cleared once a second while a run was in flight, and the highlighted run stays highlighted when rows are added above it.
/autoresearch clearrejects the reset when it cannot read git status, instead of prompting about zero uncommitted files and then discarding whatever was there.- The GitHub link in the veyyon.dev navigation carries an accessible name, so a screen reader announces the destination instead of an unnamed link holding a hidden icon.
veyyon config setandveyyon config resetcreate the profile directory they write into, so the first command run on a machine that has never launched a session persists the setting instead of exiting 1 with an ENOENT on that directory.- Raising breadth, attempts, parallelism or certification on a live session is written to the session instead of being dropped, so the loop runs the shape the setup console last showed.
- The launch card keeps one set of facts per project instead of one for the whole machine, so working in two projects no longer erases both and leaves the context gauge reading
?on every start. The file is versioned, bounded to the 24 most recently written projects, and a copy written by the previous shape is discarded rather than read. - The launch card records what a project knows and what a model knows separately, so working in two projects no longer erases both and leaves the context gauge reading
?on every start, and a project opened for the first time states the model's display name and effort instead of a raw id. The file is versioned, each map is bounded to its 24 most recently written entries, and a copy written by a previous shape is discarded rather than read. - The launch card states the context gauge in a project it has never measured, using the at-rest reading the configured model last took anywhere, so a project opened for the first time draws a real bar instead of
? leftand an empty one; the project's own reading still wins where it has one, and the gauge falls back to?only until that model has idled once. - The launch card states the effort the last launch ran at, so the status row no longer grows a
@hightail when the session mounts and shifts every segment beside it; the rung is dropped when the model changes and erased when the row stops printing one. - The launch card prints the configured model id's last path segment rather than the whole qualified id, so a namespaced id no longer costs the status row its context gauge on the first launch of a project.
- The launch hero states the configured model instead of
no model yet · /loginwhen no display name has been recorded yet. - The context percentage the next launch states is recorded only while the session is running the configured default model, so a session started with
--model, switched with/modelbefore anything was sent, or fallen back to another model no longer leaves the card a gauge measured against a window its model does not have. - The context gauge renders every percentage at one width, so a reading that arrives or changes no longer shifts the status row beside it.
- The launch card lays its status row out against the same width the live row uses, so a narrow terminal no longer shows the card keeping a segment the running session immediately drops.
- The screen no longer shakes while an answer streams into a viewport it has not filled: each streamed chunk repaints its own block alone, and the anchor fill above it is now resized in that same frame rather than reused at the previous frame's height, which composed one row past the viewport and moved the window per row of the answer.
- The composer no longer lifts off the bottom row for a frame when content collapses (a tool card closing, the working indicator retiring): the anchor is sized from the children about to render rather than from the frame that already composed, so the placement no longer needs a second paint to correct it.
- Context budgeting is unchanged from 1.3.0: the unreleased reserve for the model's output allocation is withdrawn, because subtracting it from the usable window moved every model's compaction threshold, roughly doubling how often compaction fired and invalidating the prompt cache on each pass.
- A turn too large for compaction to summarize is truncated in the middle, keeping the head and the tail, instead of pausing automatic maintenance; the removed text is written to a recovery artifact the notice names. A session whose newest turn was a single oversized message could previously make no progress, and rewinding the tree did not clear it.
- A payload the outbound secret scan refuses for its size is treated as a context overflow, so the session compacts and retries instead of stopping at "the provider request exceeds the confidentiality scan byte limit" on every attempt. The scan runs before the request is sent, so nothing else had reported the turn as too large.
- An image a kitty-protocol terminal cannot be handed reports the format as the reason instead of claiming images are switched off, and a picture whose conversion is still running no longer prints a placeholder that a moment later becomes the picture.
- A key pressed before the launch card appears is drawn into the card's composer about a millisecond later instead of 156ms later, so the composer no longer sits on screen ignoring what is typed into it while the main module loads.
- The status row's dirty marker appears when
git statusanswers instead of waiting for whatever redraws next, which in a resting session is the next keystroke; the row had been showing a clean branch over a tree nothing had looked at. - The status row keeps the dirty marker the launch card painted instead of dropping it for the width of its own
git status, so a handover on a dirty tree no longer shows the branch change colour twice; a scan that finds the tree really did move still repaints it once. - A turn that ends on text after a tool call is recognized as finished even while a session subscriber is still running, so the todo reminder, the rewind pass and the session-stop hooks no longer skip a turn whose final message arrived behind a slow subscriber.
- A memory limit pins the capped subtree's swap to zero, so the cap bounds the whole anonymous footprint; while unreleased a 256 MB machine cap let a single process reach 5,520 MB by swapping.
- The machine limit requires a parent that delegates two cgroup levels, so a host that delegates one — a container whose cgroup root holds processes — reports per-session limits held and the machine tier unheld, instead of reporting a machine cap the kernel never applies.
- The CPU-limit probe and the limiter resolve one environment, so the probe can no longer report support for a cgroup path the limiter does not write to.
- A probe directory left behind by a killed veyyon no longer makes the only usable cgroup parent look unusable, which dropped the machine to no budget at all until it was deleted by hand.
/cpu-limit statusreports whether the kernel is holding the machine-wide limit, naming the resource it refused, instead of printing the configured cores on a host that holds none of them./cpu-limitstates that a session CPU cap above the machine cap is bounded by it, rather than printing the two numbers with nothing relating them.- A background bash job joins its session's CPU and memory budget instead of running outside every cap.
- A model whose compaction route answers 404 reports the downgrade to local compaction once, instead of once for the 404 and again in different wording on the next compaction.
- A session that never enabled goal mode no longer reports "Goal mode stopped driving" after three consecutive provider-killed turns; the failed-turn counter and its stand-down warning now require a running goal.
- A goal now starts with its full failed-turn tolerance instead of inheriting the failures an earlier goal in the same session left behind, which stood it down on its first error rather than its third.
- A turn-ending provider error too long to render inline now reports how many lines were dropped instead of ending without a trace.
- The installer asks what is already installed before downloading, so a machine already on the released version finishes in seconds instead of fetching the whole binary to discard it.
- The installer repairs an install whose binary was replaced since it was written — a local build copied over it, or a write interrupted mid-swap — by moving that file aside and installing, instead of refusing and leaving the machine on the old version; a file at a path the installer has never installed to is still refused untouched.
- A completed goal now reports the tokens the turn that completed it spent after the
goaltool ran, including a subagent that returned in the same batch, instead of stopping its count at the tool call. /cpu-limitno longer sets a budget: it reports both scopes and lifts this session's CPU cap, and points at/settingsunder Resources for configuration.- An ACP client following a tool-call location now opens the file, not a name ending in the read tool's line range.
- The settings screen states that
leftreturns to the category list, and no longer expands a row that has no description, which consumed the nextleftwith nothing on screen to show for it.
Removed
subagent.sharedModel,subagent.model,subagent.thinkingLevelandsubagent.modelByDepthdecided the model and effort for every subagent at once and are rejected; a config still holding one is reported once, naming the agent page that replaces it.- The
--subagent-modellaunch flag, which set the model for every subagent in the session. - The
--projectflag onveyyon agents unpack, which wrote definitions to./.veyyon/agents; there is no project scope for subagent discovery, so those files were never loaded. - The tagline under the wordmark on the session welcome hero, which the launch card and the mounted hero each printed.
@veyyon/natives
Added
VEYYON_DEBUG_STARTUPemits anative:firstCall:<export>marker naming the native call that first loads the addon, so a launch that pays extraction before its first frame states which call pulled it in.
Fixed
- The addon loader resolves
node:child_processandnode:zlibwhen it loads an addon rather than when it is imported, so importing@veyyon/nativesfor its types or enum values costs 3ms less. - A compiled binary's first launch of a version extracts only the native addon variant the host loads, instead of every variant the binary carries, so a cold start writes about 135MB rather than 270MB before the first frame; the skipped variants are written on demand if the selected one fails to load.
- A compiled binary carries one embedded archive per native addon variant instead of one archive holding all of them, so a cold launch inflates only the variant it loads; cold first paint on linux-x64 drops from 361ms to 229ms.
@veyyon/stats
Changed
- The session parser passes
contentTextan options object rather than a bare separator, following that helper's consolidation in@veyyon/utils. No change to the text it extracts.
Removed
- The
@veyyon/stats/formatentry point.formatCostTieredandnormalizePremiumRequestsare now exported by@veyyon/utils/format.
@veyyon/tui
Added
TUI.markLayoutSized(component)marks a root child whose height theonBeforeComposepass owns, so a component-scoped frame renders it instead of reusing the rows the previous frame's content called for.SelectItem.disabledgreys a row and refuses Enter and click on it, while the cursor still lands on it, so a list can show a choice that does not apply without hiding it.SelectList.setItemsreplaces the rows in place, keeping the live search query, the cancel ladder and the selected row followed by value, so a host whose data changes does not have to construct a second list and take the reader's filter with it.TUI.adoptPaintedWindow(screen)tells the renderer the terminal already holds a frame someone else painted, andTUI.paintedScreen()returns that frame, so the first render after adoption diffs against the rows on screen and rewrites only what changed instead of repainting the viewport.OverlayOptions.aboveFooterkeeps the pinned footer on screen: the rows from the footer's top down are added to the overlay's bottom margin every frame, so a bottom-anchored overlay ends directly above the composer zone and follows a footer that grows.TUI.pinnedFooterRowsreports the footer's row span in the last composed frame so a host can size the component it shows.
Fixed
- An
Editorwith noonSubmitconsumer leaves the draft alone when Enter arrives, instead of clearing it, so a submit typed before anything is listening cannot destroy what was typed. SettingsListreserves two columns for the scrollbar gutter when items overflow the viewport, matchingScrollView.contentWidth.
@veyyon/utils
Added
getLaunchFactsCachePath()resolves the cache file the launch card reads the previous launch's model, git state and context percentage from.formatCostTiered()andnormalizePremiumRequests(), moved here from@veyyon/stats/formatso the status row reaches a terminal formatter without the stats package.getGlobalSubagentsDir()resolves~/.veyyon/subagents, and the legacy-layout migration leaves that directory at the config root instead of moving it underprofiles/default/.
Changed
AbortError, the file lock and the postmortem handler no longer loadnode:assert/strict,node:cryptoornode:inspectorat import, which the launch path waited on for one assertion, one identifier and one signal handler.- The doc comments on
resolveHomeDirOrThrow()andgetConfigRootOverride()state the refusal rules and the sandbox exception without the surrounding narrative; no behavior changes. contentText()is the single owner of content-block flattening across every package: it takes theseparator,imageplaceholder,trimBlocksandtrimStringoptions the coding agent's own copy carried, and its second argument is now that options object rather than a bare separator string. A block that carries no text — a thinking block, a tool call, a loose non-object, or a text block whosetextis absent or not a string — contributes nothing rather than an empty part between two separators.
What changed
3807 commits since v1.3.0.
Breaking Changes
- feat(agents)!: drop the process-wide scope from the Agent Control Center
- feat(ai)!: the account the operator chose is the account that spends
- feat(slash-commands)!: no slash command takes an option
- feat(secrets)!: a /secret line leads with a command
- refactor(installer)!: never clone or build from source
- feat(agent)!: split tool argument transform by audience
Features
- feat(ai): declare whether each provider login asks for a key or a browser authorization
- feat(autoresearch): record the model each run was measured on
- feat(autoswarm): give each arm its own model
- feat(subagents): a shared model and effort scope beside the per-agent one
- feat(bench): measure one arm, on a machine the bench is not loading
- feat(subagents): choose a model and an effort for one agent at a time
- feat(website): add live models page, rework features, remove blog and unused sections
- feat(settings): one owner for the subagent model and one for the advisor's
- feat(subagents): user-authored subagents live at ~/.veyyon/subagents
- feat(settings): the inline image size knobs are on the settings screen
- feat(autoswarm): configure a run in a setup console
- feat(autoswarm): give the swarm its own command
- feat(autoresearch): show breadth on the dashboard
- feat(autoresearch): explore and certify several arms per iteration
- feat(resources): machine-wide limits beside the per-session ones
- feat(commands): /rephrase asks for the last reply in plainer prose
- feat(edit): one after-edit pass per turn, selected by edit.afterEdit
- feat(tui): esc twice discards the composer draft
- feat(evals): omp harness stages OAuth auth DB instead of requiring API key
- feat(search): return matching paths instead of match lines
- feat(evals): a settled run states what it measured
- feat(evals): forward the endpoint bridge to another host
- feat(evals): bound the agent phase, and deliver every parsed option
- feat(evals): reach a locally served model from a trial container
- feat(advisor): make /advisor a slash command and wire its roster editor
- feat(evals): one declarative container program per harness
- feat(coding-agent): filter the providers card, and stop it writing the balancing setting
- feat(evals): the search bench extends along corpus, case suite and arm
- feat(coding-agent): add search/filter to /providers account manager
- feat(coding-agent): reach a conversation the screen is not showing
- feat(coding-agent): count the conversations running off screen, and let /new stop them
- feat(evals): make a search bench case declare the answer the corpus has
- feat(evals): bind harnesses to backends and make every axis reach the agent
- feat(renderer): add composer zone defect oracle, sweep, and replay corpus (Refs #920)
- feat(ai): let a Nous key in without the browser round trip
- feat(deepswe-bench): stream omp agent output via --mode json
- feat(status-line): name the roots the footline shortens a path against
- feat(bench): unify --arms to accept config arms and system adapters in any combination
- feat(shell): classified runner output opens with a [clean] or [errors] verdict
- feat(tools): make unified search the default
- feat(tools): unify workspace search
- feat(omfg): save forged rules to the profile and badge them User created
- feat(statusline): clip the footline path from one end and widen it on a click
- feat(omfg): forge rules carrying the extended TTSR frontmatter
- feat(launch): add configurable cleanup wait setting for exited daemons
- feat(coding-agent): every daemon death names its killer and stays queryable
- feat(ai): detect consecutive redundant reads of unchanged files in tool-call loop guard
- feat(coding-agent): bound read directory listings with depth/limit and a concise root default
- feat(coding-agent): a session store that outlives the eval kernel, and defs()
- feat(proof): composite window chrome after capture and assert pipeline contracts
- feat(utils): state which contract discarded a promise's failure
- feat(utils): read a one-owner claim from the declarations, not the bytes
- feat(ai): name the classification rule that decided a failure
- feat(coding-agent): read a semicolon-delimited list of internal resources
- feat(proof): 60fps hero camera that follows /secret then pans
- feat(proof): direct the hero take — zoom the secret, short prompt, 6× middle
- feat(settings): Files → LSP is one nested panel
- feat(proof): a capture stage holds one measured region of a take
- feat(session): prompt code review reminder on multi-file mutations
- feat(catalog): Command Code replaces the misread Cohere half of #884
- feat(catalog): Cohere and Nous Research join the built-in providers
- feat(tools): frame search by purpose
- feat(prompt): route structural search explicitly
- feat(tools): teach unified search routing
- feat(tools): add unified search ablation
- feat(utils): vary any registered prompt in a bench arm
- feat(eval): add Python workspace ablation
- feat(tools): add unified runtime ablation
- feat(conformance): make a mutation chunk survivable and affordable
- feat(conformance): derive every expected-error contract from the fault it injects
- feat(conformance): fill the 250k corpus allocation from per-subsystem plans
- feat(conformance): add the model-check, mutation and fuzz engines
- feat(conformance): add the generator, oracle, render, report and shrink modules
- feat(conformance): serve h2c with a fault engine that keeps its faults distinct
- feat(conformance): the virtual terminal answers a status report
- feat(conformance): the four virtual subsystems a deterministic case runs against
- feat(conformance): the corpus record, its identity, the allocation manifest and the shard router
- feat(demos): let the rasterizer draw strikethrough, and add the two HUD renderers
- feat(tui): rebuild the anchored subagent and todo blocks in the house vocabulary
- feat(proof): prepare autonomous hero recording
- feat(goal): let models create persistent goals
- feat(coding-agent): the todo board reads as a panel, and writes itself in
- feat(coding-agent): a running tool block moves its rail, and cools once it lands
- feat(slash-commands): wire /stats, which was a parser nobody called
- feat(secrets): the escape for a reserved word is a verb, not a shell dash
- feat(secrets): an expiry warning nobody asked for can be declined
- feat(secrets): emptying the vault is a command, not a credential
- feat(overlays): a card is a ladder of materials, measured on the terminal
- feat: every bar moves in eighths of a cell, from one owner
- feat(theme): the band under the cursor has a direction
- feat(overlays): a card is a lit surface, and the light crosses it as it opens
- feat(tui): paint a background per column, and a surface that can be lit
- feat(settings): a subagent's page owns what it runs, and recurses
- feat(tui): a scrolled viewport travels through the rows between
- feat(tui): walk the context gauge to its new reading
- feat(tui): the copy picker fades its band and every picker gives the clock back
- feat(tui): the ask dialog cross-fades its option band
- feat(tui): fade the settings category band under the pointer
- feat(tui): the hand-painted pickers cross-fade the pointer band
- feat(tui): the suggestion popup grows instead of cutting in
- feat(tui): the pointer band fades in and out instead of switching
- feat(tui): a dismissed card folds away before it leaves
- feat(tui): one animation clock for the whole terminal
- feat(tui): accept a composer suggestion on click
- feat(coding-agent): move the transcript drill-in onto ModalShell
- feat(coding-agent): move the login screen onto ModalShell
- feat(coding-agent): make the composer chips answer clicks
- feat(coding-agent): answer the pointer on ask-dialog option rows
- feat(coding-agent): forward pointer events into the model picker's browser
- feat(coding-agent): answer the pointer at the row level in the six selector overlays
- feat(coding-agent): route pointer events into settings submenus and modal bodies
- feat(coding-agent): pin subagent models per spawn depth with subagent.modelByDepth
- feat(coding-agent): remove the built-in commit-drift rule
- feat(rules): drop the bash-tool-nudge rule
- feat(prompt): price the tool definitions beside the system prompt
- feat(providers): turn account load balancing on by default
- feat(accounts): make the account card the login and logout surface
- feat(accounts): refresh the account under the cursor, not the provider
- feat(accounts): say what state each credential is in
- feat(secrets): make every /secret capability a verb and delete the manager card
- feat(account): say which account serves next, and how many secrets are live
- feat(secrets): the card asks in one line, not one paragraph
- feat(secrets): a stored credential's value can be corrected
- feat(secrets): storing a credential costs one field, not three
- feat(secrets): the manager can store a credential from an environment variable
- feat(secrets): /secret's subcommands work in the terminal
- feat(resources): every limit a session can hit lives on one tab, and covers the whole tree
- feat(login): the login screen is one frame, and names the account it just stored
- feat(login): every provider whose credential is one pasted key can be logged into
- feat(accounts): /login answers the name you typed and lands on the account it added
- feat(composer): the footline is opt-in, and the exit sign is not
- feat(accounts): the footline names the account that is spending, and adding one comes back to the card
- feat(accounts): one login command, a durable global account choice, and a gate on spending a second subscription
- feat(coding-agent): refuse low-information IRC messages between subagents
- feat(simulations): add @veyyon/simulations, a home for real-use simulations
- feat(ai,catalog): decode the context breakdown Cursor was already sending
- feat(release): cut a release with one command
- feat(settings): show what each agent is for, and widen scout to reconnaissance
- feat(cpu-limit): let one session depart from the profile's CPU budget
- feat(cursor): cursor provider compatibility, discovery and project rules
- feat(compaction): server-side compaction transport and its capability gate
- feat(session): per-session CPU limit, and a revoked yolo reaches a running child
- feat(rules): ask for a narrower test selection when a command runs everything
- feat(agent): cache-aligned compaction request context
- feat(rules): ship bundled rules in section directories, experimental off by default
- feat(settings): give rules their own sidebar section
- feat(release): publish from a tag push instead of a CI-side controller
- feat(release): prepare a release locally instead of inside CI
- feat(coding-agent): reload the model catalog from the picker with ctrl+r
- feat(catalog): add Baseten's per-route reasoning table
- feat(rules): add per-compact repeatMode and bash-tool-nudge TTSR rule
- feat(accounts): surface a login that a failed refresh signed out
- feat(ai): make a login torn down by a failed refresh enumerable
- feat(deepswe-bench): add the replay-manifest and system-comparison harnesses
- feat(onboarding): land the shared filter-escape claim and the approvals step
- feat(dashboard): tell a blocked or waiting agent from a working one
- feat(retry): say why a retry happened and what it cost
- feat(retry): resolve retry limits per provider
- feat(cache): name the gap that expired the cache window
- feat(tui): reach the alt-arrows surface by env, and say which mode you got
- feat(tui): replay a resident transcript onto the normal screen at exit
- feat(tui): read the wheel from alternate-scroll cursor keys
- feat(tui): let the transcript reside on the alternate screen
- feat(tui): scroll transport that does not grab the mouse
- feat(cache): make blocking on a rejected cache an opt-in setting
- feat(ai): check the prompt cache instead of only billing it
- feat(ai): anchor the stable Anthropic system prefix separately
- feat(tui): name the cause on the cache-miss marker
- feat(tui): size a select list to a total row budget
- feat(catalog): prompt cache breakpoint generation floor
- feat(ai): explicit prompt cache breakpoints, official OpenAI Responses only
- feat(veybot): generate the agent's model routing from .env
- feat(veybot): make the toolchain configuration, not code
- feat(tools): browser run output, secret name prompt, tool loading differential
- feat(release): add release.sh, one owner for a release and its proof
- feat(veybot): add rehearse, a dry run that cannot reach GitHub
- feat(veybot): enforce the three-beat triage cadence
- feat(veybot): make triage opt-in per issue
- feat(veybot): port-backlog drain CLI
- feat(veybot): port_upstream and ci_repair task kinds
- feat(veybot): CI check reads and paginated label-filtered issue listing
- feat(upstream): gate clean feature ports
- feat(upstream): triage clean feature candidates
- feat(agent): harden sessions, secrets, and observability
- feat(secrets): tell the model which secrets it can spend
- feat(secrets): withhold stored credentials from display expansion
- feat(completions): complete the @file launch positional
- feat(completions): complete setting keys and their values
- feat(tui): clickable status footline — goal readout opens goal detail (GMI-2b)
- feat(update): confirm 'Checksum verified' on a successful self-update
- feat(tui): accelerate repeated wheel ticks in scroll isolation
- feat(release): derive commit-history notes + gate the generator on CI
- feat(coding-agent): present only alabaster while the light-theme slab class is unfixed
- feat(coding-agent): unify the run clock, merge model effort, clickable scroll-to-bottom
- feat(tui): release the mouse when the frame fits the viewport
- feat(coding-agent): ease the footline badge slot open and closed
- feat(tui): scroll isolation — wheel scrolls the transcript, footer stays pinned
- feat(dist): GitHub-only update path and changelog-gated auto-release
- feat(stats): attach turn context to request details instead of a lone reply
- feat(session): relativize wire paths under session roots (TW-10)
- feat(session): canonicalize outbound tool call ids per provider compat
- feat(session): per-profile workdir, agent setCwd, task cwd input
- feat(instructions): seed a new profile's AGENTS.md on creation
- feat(instructions): load exactly three instruction layers by default
- feat(approval): /yolo command for a full session permission bypass
- feat(approval): add full-bypass rung for the /yolo command
- feat(settings): per-model thinking effort on compaction.model + subagent.model
Fixes
- fix(tui): keep a failed poll's job rows instead of collapsing to the error text
- fix(tui): model picker empty state, session deletion across scopes, HUD model badge
- fix(tui): sanitize tabs and home paths in every tool renderer path
- fix(cli): exit 130 on a cancelled print turn and 2 on a usage error
- fix(login): launch the browser only for a login that waits on it
- fix(catalog): list gpt-6-astra on ChatGPT Codex with its declared effort ladder
- fix(tui): render a warning repeated with nothing between it once
- fix(tui): clamp an overlay box title to its own border at tiny widths
- fix(autoswarm): switch to an arm's model ephemerally so a resumed session keeps its own
- fix(auth-gateway): honour accounts.loadBalancing when serving credentials
- fix(startup): the card repaints on the session's at-rest record
- fix(startup): the launch card imports the repository walk from git-head
- fix(search): strip the scope's trailing slash through trimTrailingSlashes
- fix(autoresearch): a session continuation ending is not the loop stalling
- fix(session): drop a continuation waiting on idle when the user interrupts
- fix(settings): restore authoring guide hint, wrap footer, and align scrollbar height
- fix(startup): the launch card resolves the worktree and drafts with the row it hands over
- fix(settings): clarify unset text values and prevent subagent roster overflow
- fix(tools): path scopes a file search; browser calls in a batch run in order
- fix(autoresearch): the console opens on the session goal; an idle hook UI rests the loader
- fix(autoresearch): escape pauses the loop; console answers outrank init arguments
- fix(autoresearch): close the run-flow holes triage, review and storage left open
- fix(autoresearch): clear keeps the harness when the tree is kept
- fix(ai): with load balancing off, one account serves a provider
- fix(autoresearch): a turn that advances nothing is steered, then stopped
- fix(autoresearch): report an arm whose model restore was refused
- fix(read): serve a skill whose SKILL.md is a symlink out of its directory
- fix(autoresearch): a word typed after the command stops overwriting the goal
- fix(catalog): a models.dev miss is not an OpenCode discovery failure
- fix(ai): log every orphan fold, and give the OpenCode conversation id one owner
- fix(ai): strip the persisted stale-tool note from the native replay items, keeping the reply
- fix(ai): escape the stale-tool envelope attributes and stop copying clean history
- fix(catalog): identify the client on OpenCode model discovery
- fix(ai): a stale tool result stops speaking as the assistant
- fix(ai): key the OpenCode session header on the conversation, not the routing id
- fix(ai): drop an unsigned thinking block beside a signed one after a refusal
- fix(ai): OpenCode request identity and reasoning-refusal recovery
- fix(catalog): resolve unbundled OpenCode ids' wire API from live models.dev
- fix(tui): keep the composer on screen under the console overlay
- fix(catalog): give antigravity's gemini-3.8-flash-tiered deployment its effort surface
- fix(autoresearch): an explicit off clears the pause it was taken under
- fix(autoresearch): let a paused loop resume when the branch comes back
- fix(autoresearch): pause the loop on a branch switch instead of blanking it
- fix(compaction): drop reasoning from every summary transcript, not just anthropic's
- fix(anthropic): stop replaying reasoning bound to a rewritten prefix
- fix(agents): unpack writes where subagent discovery reads
- fix(config): create the profile directory before locking the config file
- fix(bench): time the statusrow arm against the rung the row always carries
- fix(ai): bump the Claude Code fingerprint to 2.1.257
- fix(utils): a content block that carries no text contributes none
- fix(session): a truncated side-channel reply ends on a whole glyph
- fix(website): the GitHub nav link had no accessible name
- fix(installer): a v1 receipt is not permission to displace a user's file
- fix(test): assert the effort suffix against the ThinkingLevel enum
- fix(bench): seed the startup bench the way an install leaves a home
- fix(bench): let only the program answer the editable arm
- fix(proof): hold the before arm at origin/main, and take away the files the branch added
- fix(startup): read the terminal size and the environment the way the card does
- fix(launch): file the model's context reading as a floor, not one project's total
- fix(launch): paint the card on the ground the session settles on
- fix(agents): unpack into the directory discovery reads
- fix(autoresearch): bound the sidebar with clampLow instead of an inline floor-first clamp
- fix(autoresearch): keep the run screen inside a terminal narrower than its sidebar
- fix(config): create the profile directory a one-shot config write needs
- fix(website): name the GitHub link in the site navigation
- fix(autoresearch): collapse the home directory in the run screen's artifacts path
- fix(tui): mount the session over the launch card without repainting it
- fix(status-line): keep the marker the launch card painted, and drop the secrets segment
- fix(site): regenerate the models catalog the build claims to regenerate
- fix(autoresearch): keep a measured crash, a typed filter and an unreadable git status honest
- fix(autoresearch): fit the run screen to short terminals, page it, and scope clear to its branch
- fix(launch): state the gauge in a project it has never measured
- fix(launch): file a model's facts by model, so a new project states its name and effort
- fix(autoresearch): keep the primary metric out of the secondary map
- fix(autoresearch): list a metric once in the run detail
- fix(launch): state the effort the last launch ran at on the first frame
- fix(autoresearch): a run that measured nothing shows no number
- fix(launch): key launch facts by project so two projects do not erase each other
- fix(website): monochrome V favicon
- fix(website): remove scroll reveal animation and motion.js entirely
- fix(website): restore --maxw, --pad, --ease, --dur CSS variables lost in font edit
- fix(launch): record a context reading only for the model the default role names
- fix(status-line): state a context percentage on the launch card
- fix(tui): size the home anchor from the frame it is about to compose
- fix(launch): compose the card's row against the live row's budget
- fix(compaction): keep a codex compaction on the session's cache lineage
- fix(compaction): post codex compaction to the route the host serves
- fix(compaction): a codex server-side compaction was a paid no-op that compacted again every turn
- fix(compaction): drop the dead codex v2 import and re-lock the compaction file
- fix(compaction): post codex compaction to the route oh-my-pi serves, and revert the context-window budget change
- fix(compaction): route every session budgeting read through the usable window
- fix(compaction): budget context against usable input, not the whole window
- fix(agent,coding-agent): a turn too large to send is reduced, not parked
- fix(compaction): name the engine on every compaction pass
- fix(tui): the dirty marker arrives when git status answers
- fix(startup): the launch card shows what was typed before it appeared
- fix(tui): the placeholder row names the setting that undoes it
- fix(session): record the turn's last assistant message before the subscriber fan-out
- fix(tui): prepare an inline image before it is first drawn
- fix: remove personal name from attribution-detection test
- fix(tui): draw an inline image at its own origin, at the size it is shown
- fix(commands): await the machine description on the lift path
- fix(commands): say when a session cap is bounded by the machine cap
- fix(test): type the settings tab argument in the pointer suite
- fix(ai): keep a user turn when the codex compaction budget is not a number
- fix(session): hold both resource-limit tiers against a real kernel
- fix(compaction): port Codex remote compaction to the wire the backend serves
- fix(tui): ask the terminal about sixel instead of assuming it cannot
- fix(acp): peel the read selector from a tool-call location
- fix(agent): keep image blocks when compaction shakes a tool result
- fix(compaction): post codex compaction to the live route, not the retired one
- fix(goals): count the turn that completed the goal
- fix(install): ask before downloading, and repair a drifted install
- fix(transcript): an abridged provider error says how much it dropped
- fix(goals): the stand-down warning belongs to a goal that is running
- fix(read): refuse a directory that cannot be scanned instead of calling it empty
- fix(catalog): restore the opencode-go rows the merge rolled back
- fix: reconcile the ratchets and paths the merge left pointing at nothing
- fix(workspace-tree): give both new directory builders the addon-failure contract
- fix: resolve merge conflicts with origin/main
- fix(edit): fall back to the default after-edit pass on an out-of-schema value
- fix: update baselines and tests for runtime removal and evals restructure
- fix(session): resolve a resume id under any profile
- fix(tui): a long command no longer pushes the approval options off the card
- fix(web-search): drop the ecosia engine
- fix(tui): attribute a blocked loop to the phase that spent the time
- fix(session): an exit record is not a live second writer
- fix(evals): route prompt-render test through registry, fix anti-vacuity count
- fix(evals): update stale harbor tests for --agent flag and remove unified-runtime arm
- fix(utils): an empty legacy agent dir is not a second profile
- fix(settings): a search box holding only spaces is a cleared box
- fix(ai): a long name whose tail cycles is not a runaway sampler
- fix(evals): read DOCKER_GATEWAY_TOKEN from env as fallback
- fix(evals): strip broker env leak, pass gateway token to omp
- fix(search): file and structure search own their byte budget
- fix(evals): omp harness gateway routing, harbor --agent flag, bun staging
- fix(tui): stream a new row into the empty space instead of sliding the screen
- fix(startup): load collab and argot runtime on first use, not at startup
- fix(tui): keep the cursor still on a frame that paints nothing
- fix(stats): load the dashboard graph on first /stats, not at startup
- fix(render-oracle): judge a rebuilt tape against the committed record
- fix(coding-agent): an eval backend reaches its kernel module on every call
- fix(render-oracle): judge the composer against the placement the renderer produced
- fix: compose overlay targets the 'main' service harbor builds, not 'task'
- fix(cursor): route tool-call updates by call_id and never rerun an answered call
- fix: auth-broker snapshot validates OAuth source field; harbor skips source mount for pinned binary
- fix(scripts): point the focused-view demo at the render-oracle terminal
- fix(tui): pull a keystroke's frame out from behind stream backpressure
- fix(cursor): upload the operator's instructions once, not three times
- fix(tui): write nothing for a frame the screen already shows
- fix(evals): remove last stale src/ references in config and scripts
- fix(cursor): deliver operator instructions on the active user turn
- fix(evals): replace stale src/ paths after directory restructure
- fix(evals): repair Handlebars compile calls and entry-point discovery after restructure
- fix(tui): release a bracketed paste whose end marker never arrives
- fix(render-oracle): decide blank space against the frame, not the screen
- fix(tui): one owner for the live-tail window anchor
- fix(prompt): deliver project instruction files to cursor-agent models
- fix(evals): verify repo root and remote before trusting dataset SHA
- fix(coding-agent): biome formatting after path update
- fix(coding-agent): update stale evals import paths after restructure
- fix(tools): a search cap notice is true of the result it prints
- fix(tools): discovery ranks a tool on all of its own text
- fix(personality): an inherited property name resolves like any other unknown name, and a spec cannot spell prompt structure
- fix(tools): a rejected search field names the set that works
- fix(tools): speak every search cap and own the notice prefix
- fix(test): type the search type enum options as strings
- fix(tools): restore the search notice and bound a structure binding
- fix(discovery): activate the tools a search matched well, not its tail
- fix(read): list a directory at its top level when the caller named no depth
- fix(read): stop a structural summary at the line default, not only at the byte budget
- fix(read): price a PDF member list and an agent extraction like every other read window
- fix(read): price a structural summary like every other read window
- fix(ai): a bare gateway status is a twenty-second retry, not a five-minute park
- fix(ai): a deterministic parse failure stops the retry ladder on both classifier routes
- fix(tools): make the spill threshold bound what a result costs, not only when an artifact is written
- fix(argot): the transcript viewer expands handles instead of showing them raw
- fix(launch): price a log read like every other tool result
- fix(plugins): a marketplace dry run installs nothing, and link replaces what is there
- fix(read): price an archive listing and a glob path list like every other tool result
- fix(modes): input the terminal delivered is not dropped
- fix(read): price a directory listing like every other tool result
- fix(tools): load debug only where an adapter command resolves
- fix(test): three gates read state a bucket-mate left behind
- fix(mentions): price a @path mention like a tool result
- fix(read): bound every read window by the configured budget
- fix(read): price a default window like every other tool result
- fix(search): stop reporting documentation prose as a code match
- fix(search): stop billing three trailing lines per text match
- fix: never report an unloadable native addon as an empty workspace
- fix(evals): read the build flag the veyyon adapter declares
- fix(evals): record the build a program-delivered arm staged
- fix(evals): keep the grade a bounded agent phase earned
- fix(evals): put a pier run's output where the run named
- fix(startup): clear the relaunch marker when no launch card paints
- fix(startup): keep what was typed before the launch card painted
- fix(goals): keep driving a goal whose post-turn work outlasts the continuation delay
- fix(catalog): read the context window LM Studio actually accepts
- fix(tui): show text typed at the launch card while it is being typed
- fix(session): stop the old conversation by default on /new
- fix(evals): probe the auth gateway only for arms that route through it
- fix(coding-agent): carry a prompt typed at the launch card into the composer
- fix(ai): read a named auth refusal as a credential verdict, not a bare 503
- fix: a sweep that reads what git tracks survives a deleted path
- fix(evals): a refused fixtures archive states its kind instead of being re-read
- fix(coding-agent): say a search found nothing in the reader's words
- fix(coding-agent): tell a search deadline apart from an empty web
- fix(evals): a re-aggregation returns its verdict instead of setting the exit code
- fix(coding-agent): stop treating search metadata as a search result
- fix(evals): migrate an older run record, refuse a newer one, and carry the recorded arm
- fix(evals): an attachment set is a matrix axis, not a cell index
- fix(evals): a prompt attempt ends at its deadline even when the client cannot be aborted
- fix(evals): a harbor run that stops making progress ends instead of rendering forever
- fix(evals): the deep-swe executor no longer reads a trial's pipes with its deadline cancelled
- fix(evals): one bounded request, and the last two callers use it
- fix(evals): a dashboard request the manager never answers ends instead of hanging
- fix(evals): bound every external command a run spawns
- fix(coding-agent): decide project containment with the owner, not a local copy
- fix(coding-agent): measure a collapsed ssh preview in rows, not newlines
- fix(evals): a trial leaves no listener on the run it was cancelled with
- fix(ai): explain a llama.cpp parse failure on every route to a local server
- fix(evals): a cancelled trial stops waiting on the pipes its tree held open
- fix(ai): read a rate-limit status code as a number, not a substring
- fix(evals): bound the output read that follows a killed trial
- fix(ai): decide a marker-less Google EOF with the owner every dialect uses
- fix(evals): one bounded terminator ends a trial's process tree
- fix(coding-agent): keep a running subagent's card live across a transcript rebuild
- fix(utils): read x-ratelimit-reset through the header's owner
- fix(ai): word a provider finish-reason error in one place
- fix(evals): prove a pid before escalating a cancel to SIGKILL
- fix(evals): bound a trial's teardown so a hung dispose cannot wedge a worker
- fix(evals): retry a trial that threw before its row is lost
- fix(evals): the progress bar and every polled pane show what was read
- fix(evals): one reader parses a harbor trial result
- fix(evals): one owner classifies a trial status
- fix(utils): a status line the message opens with reports its code
- fix(shell): the renice test states the headroom the host gives it
- fix(error): a provider failure the field produced reaches a decision
- fix(cursor): a tool the exec channel already ran is not run again
- fix(oracle): a transcript row is not a second composer prompt
- fix(minimizer): a cargo failure before rustc keeps its evidence
- fix(minimizer): a deferred nextest panic body is not dropped
- fix(export): a value in an HTML attribute cannot close it
- fix(write): an indented numeric mapping key is not a pasted search prefix
- fix(tools): a selector suffix no longer hides a symlink escape
- fix(compaction): a compact route that answers 404 is not asked again
- fix(evals): a dashboard action states what the manager did
- fix(evals): a broken Terminal-Bench checkout refuses instead of shrinking the dataset
- fix(evals): an orphaned run gets its status from the timestamp its directory carries
- fix(evals): the disclosure bench refuses a measurement nothing backs
- fix(evals): a harbor trial nobody priced reports absent spend, not $0
- fix(evals): the dashboard says when a table it is showing went stale
- fix(evals): a terminal-bench corpus hash and task id state what the run read
- fix(evals): an entry point that cannot act on a flag refuses the invocation
- fix(evals): a count the deep-swe runner cannot act on refuses the run
- fix(evals): the edit adapter refuses a wrong invocation before it unpacks anything
- fix(evals): a harbor flag the runner cannot act on refuses the launch
- fix(evals): a search bench run that measured nothing refuses instead of passing
- fix(evals): a bench results block is keyed safely and filed in its own section
- fix(evals): a benchmark snapshot reports what it read, not an empty run
- fix(evals): one rule names a trial's directory, and
..never climbs out of the run - fix(evals): a resumed run keeps the log of the attempt it is resuming
- fix(evals): a job name names one run, and a cancel reports what it killed
- fix(evals): a dashboard subscriber that stops reading is dropped, not buffered
- fix(evals): a trial that cannot be recorded stops the run
- fix(evals): one owner decides a trial's deadline and its output bound
- fix(evals): a run id names one plan, and a run refusal is one line
- fix(evals): refuse a variant axis nobody applies
- fix(natives): annotate the Darwin child map so it still type-checks
- fix(evals): a run id and a job name are one directory name
- fix(evals): refuse a directory or task-list file the run could not use
- fix(evals): refuse a flag no entry point declares, and register the harnesses the deep-swe runner resolves
- fix: hang a tab indent when wrapping, size the Darwin pid list from the kernel
- fix: pin an HTTP reason phrase to its own code, bound the launch renderer
- fix(evals): check every mutating request body against its declared fields
- fix: shorten and bound every path a tool status line or the /omfg panel prints
- fix(evals): check every launch field before a run starts
- fix: seven defects found auditing the range since v1.2.0
- fix(evals): the dashboard bundles for a browser, and an empty store says so
- fix(evals): a resume of a run with no journal refuses instead of starting it
- fix(evals): --list states every registry an invocation selects from
- fix(evals): a preflight refusal states each credential once
- fix(evals): a dictionary row states the revision it was measured against, and two harbor suites register their own harnesses
- fix(evals): a generation attempt carries no invented provenance
- fix(evals): a trial journal states the record shape it holds
- fix(evals): one deep-swe source root, and an ungraded trial leaves the denominator
- fix(evals): the deep-swe runner throws instead of ending the process
- fix(evals): the typescript-edit corpus regenerates to the same cases
- fix(evals): suite arithmetic reports the denominator it actually measured
- fix(evals): a broken run refuses instead of reporting a result
- fix(evals): unmeasured spend and unrecorded identity leave the store as absent
- fix(evals): an unscored trial is never counted as a zero
- fix(coding-agent): remove any casts in search test fixtures, use typed builders
- fix(coding-agent): seed provider inventory in search tests and sync root changelog
- fix(coding-agent): address review feedback for providers search
- fix(coding-agent): give every live conversation its own registry row
- fix(evals): read the withheld-cost note's rates from the card that owns them
- fix(coding-agent): float the resume card instead of pinning it to the top edge
- fix(evals): run the model the run named, on every backend, or refuse
- fix(evals): stage the assets a pier trial's agent requires, and stop scoring a setup failure zero
- fix(evals): report what a trial spent instead of reporting nothing
- fix(evals): give a harbor trial the mounts, gateway and load order it needs
- fix(ai): serve a broker snapshot that holds a logged-in API key
- fix(coding-agent): clear memory on the shared deletion retry window
- fix(evals): make a long parallel evaluation run survivable and its scores honest
- fix(coding-agent): keep a configured Ollama base URL's path
- fix(hashline): one definition of what a numeric-keyed mapping row looks like
- fix(ai): call the errorMessage owner in the pi-native payload hook error
- fix(coding-agent): answer an LSP abort the same way on every server
- fix(ai): let a failure name what actually failed
- fix(coding-agent): report what failed instead of substituting an empty result
- fix(utils): honor the abort signal on every onProcessExit branch
- fix(ai): redact every credential header in Codex diagnostics
- fix(tools): redirect a retired primitive in the search tool's own vocabulary
- fix(cli): describe the grep dev command the way the command does
- fix(renderer): read a composer pad background from SGR parameters
- fix(tools): build the runtime tool from a lazy import, like its neighbours
- fix(goals): keep a guided goal turn alive and visible
- fix(coding-agent): wait for a GUI editor to close before reading the buffer back
- fix(ai): retry a turn that never reached its first event
- fix(utils): a host that cannot load the native addon still ends its children
- fix(tui): size a sibling-dependent layout before the frame composes, not after
- fix(tui): never route anchor slack the live children have already taken
- fix(hashline): import truncate from its owner, not the utils barrel
- fix(settings): name the VEYYON_ timeout env vars in the stream timeout settings
- fix(tools): cut truncated previews at a code point, not a UTF-16 code unit
- fix(irc): end a send-await when its recipient can no longer reply
- fix(tui): measure display truncation in columns, not code units
- fix(web-search): classify a Kagi refusal from its response body
- fix(tui): stop leaking the home directory into an error card
- fix(web-search): honor a stored key for Brave and Jina
- fix(ai): redact every credential header in a persisted 400 dump
- fix: close defects found in the pre-release audit pass
- fix(coding-agent): restore the revive identity hatch and unpin stale error bytes
- fix(ai): declare per-provider tool-call argument delta wire shapes
- fix(agent): address lifecycle revival racing, session alias, and error formatting
- fix(tui): route footer clicks through the shared clampLow owner
- fix(tui): sanitize paths, truncate custom tool args safely, and fix windows inspector paths
- fix(persistence): version and migrate memories storage and mnemopi cost log
- fix(shell): resolve minimizer bypass documentation and epoch clamping
- fix(ai): register HTTP/2 session listeners after stream termination helper
- fix(status-line): sanitize focusedAgentId in focusExitBadge
- fix(write): refuse patch and read display markers instead of stripping prefixes
- fix(tui): derive scroll-isolation footer hit bounds from window top
- fix(ai): handle HTTP/2 session error and close events in Cursor provider
- fix(ai): append streamed tool-call argument deltas without lossy prefix heuristic
- fix(ai): decide read subsumption from arguments instead of result text
- fix(shell): disable Windows rate control on non-positive or non-finite cores. Refs #910
- fix(agent): preserve selector identity and apply scheme exemption per target in read supersede pruning
- fix(bash): judge approval against effective directory from leading cd and relative cwd
- fix(agent): support multi-target set-based supersede pruning for read tool results
- fix(gates): harden test scanner, class privacy scan, and scene verification
- fix(edit): sanitize error paths and bound large unmatched hunks
- fix(launch): drop a non-existent lifetime field from the broker recovery seed
- fix(read): guard optional args in readToolRenderer result path formatting
- fix(tui): slice LaTeX argument text instead of accumulating it per character
- fix(coding-agent): resolve target kind via stat and deduplicate multi-target ast_grep
- fix(boundary): expand delimited path targets in cwd boundary checks
- fix(launch): prevent duplicate completion records and clean up broker recovery restart branch
- fix(tui): accept readonly lines in PinnedComposer test fixture
- fix(ai): preserve URI schemes, Windows drives, and compound selectors in read loop guard
- fix(tui): parse optional LaTeX arguments in linear time without character concatenation
- fix(coding-agent): deduplicate ast_grep matches across overlapping targets
- fix(tui): exclude pinned footer rows from scroll snapshot and scroll space
- fix(job-manager): keep delivery suppression armed until job record is purged
- fix(agent): clear contentBox on generic tool renderer rebuild
- fix(task): fail a subagent whose result cannot be serialized
- fix(release): default the check wait to one hour and make it configurable
- fix(session): disambiguate successive handoffs with monotonic counter
- fix(session): restore wirePathRoots on switchSession rollback and isolate test globals
- fix(task): fail a subagent that yields unusable data
- fix(session): use withResolvers and errorMessage in BackgroundSessions
- fix(session): bound background session drain on quit
- fix(omfg): fix extended TTSR frontmatter test expectation and trim foreign changelog entry
- fix: format imports and fix biome suppression
- fix(prewalk): normalize --prewalk-into to its first entry
- fix(deepswe-bench): use errorMessage helper from @veyyon/utils
- fix(coding-agent): keep the agent roster on the live model and the current conversation
- fix(tui): seed the home anchor from a direct measurement at mount
- fix(agent): resolve tool interruptibility per call, not per tool
- fix(natives): suppress warning for GLIBC version mismatches
- fix(debug): resolve the Python interpreter spelling the host installs
- fix(ssh): keep the control socket inside the Unix path limit
- fix(session): keep the cached prompt prefix across a working-directory change
- fix(deepswe-bench): write actual API key into omp models.yml
- fix(deepswe-bench): add baseUrl and api to omp models.yml
- fix(deepswe-bench): restore CONTAINER_ASSETS_DIR constant in omp_agent.py
- fix(deepswe-bench): generate models.yml for omp to resolve dynamic models
- fix(coding-agent): await discovery for explicit --model not in static catalog
- fix(deepswe-bench): add models.dev to egress allowlist
- fix(modes): a failed login ends on the reason, not on a fresh card
- fix(ai): say why a provider rejected the key, in the provider's words
- fix(natives,utils): fall back to JS kill when native addon cannot load (#917)
- fix(session): resume the session that is still running, not its transcript
- fix(session): start a new session without interrupting the running one
- fix(session): relativize paths against active working directory only
- fix(web): refuse a bot wall instead of reporting an empty web
- fix(status-line): print no control character a name carries
- fix(bench): use clampLow/errorMessage/isRecord from @veyyon/utils
- fix(status-line): keep the count on a narrowing footline, and fit the zone to the settled group
- fix(models): a role alias resolves wherever the CLI resolves a model
- fix(export): the streamed serializer applies toJSON the way the builtin does
- fix(bench): extract omp node_modules to correct container path
- fix(bench): tar full node_modules for omp, not just @oh-my-pi
- fix(launch): a saturated budget refuses a spawn, not a stop
- fix(launch): a cleanup wait cannot purge a record another start owns
- fix(bench): stage omp node_modules tar.gz for container resolution
- fix(natives): a trial-load child answers instead of booting the CLI again
- fix(status-line): read the home directory when a path is shortened, not at import
- fix(session): the review reminder does not claim the changes are from this turn
- fix(lsp): the ACP write path honors the language-server master switch
- fix: remove duplicate changelog entries and index deepswe-bench docs
- fix(utils): keep a candidate's own spelling when it is made relative to a root
- fix(bench): stage auth DB as auth-agent.db to match veyyon_agent.py
- fix(status-line): clip a path on a boundary on either platform, and clamp it in cells
- fix: resolve CI lint and secret scan failures
- fix(test): repair multiline string literal and import order in structure-search test
- fix(status-line): show the clicked name whole, and reflow to it in one motion
- fix(export): make the streaming serializer fail where the builtin fails
- fix(status-line): give the retracting model chip's room to the directory, not back to a shed gauge
- fix(loop-guard): steer once at the threshold, not on every turn past it
- fix(status-line): give a shed part's cells back to the location, and keep the row on the right edge
- fix(session): keep the generation check after the before_agent_start hook
- fix(coding-agent): review fixes for the prewalk settings change
- fix(status-line): paint the clip mark in the colour it cut, and cut on a name boundary
- fix(statusline): clip the directory and the branch together, and travel the trade
- fix(launch): the list fallback no longer throws on absent details
- fix(statusline): shed the branch before clipping into the directory
- fix(ai): a failed bedrock turn reports its duration again
- fix(coding-agent): prewalk takes its models from settings, not role aliases
- fix(bench): report search benchmark failures through errorMessage
- fix(tools): clarify search path ownership
- fix(tools): close unified search review gaps
- fix(tools): harden unified search edge semantics
- fix(tools): bound broad search result context
- fix(tools): complete unified search surface migration
- fix(tools): harden unified search boundaries
- fix(tools): remove stale search boundary assertions
- fix(config): centralize retired search tool migration
- fix(evals): require Pier collect support
- fix(statusline): keep the end of the path a clipped footline is naming
- fix(plugins): report the truth about what is installed
- fix(shell): import GetActiveProcessorCount from Threading
- fix(shell): define Darwin PROC_ALL_PIDS; libc never exported it
- fix(coding-agent): keep caller exec hooks beside the CPU gate
- fix(coding-agent): drop duplicate MCP CPU-gate field and tests
- fix(coding-agent): refuse MCP stdio and CLI-preloaded exec while saturated
- fix(coding-agent): sort HookAPI.exec imports for biome
- fix(coding-agent): gate public HookAPI.exec before the process starts
- fix(shell): keep Windows CpuRate conversion clippy-clean on Linux CI
- fix(coding-agent): gate custom exec before the process starts
- fix(statusline): answer a footline click in a session that has not scrolled
- fix(shell): take Windows CPU rate against the host, not affinity
- fix(coding-agent): restore changelog and adopt eval by session id
- fix(coding-agent): refuse eval cells and tiny systemd quotas
- fix(shell): floor a tiny cpu.max quota instead of freezing
- fix(shell): surface failed cgroup adopts and refresh the handbook book
- fix(coding-agent): fail closed and actually lift CPU limits
- fix(statusline): a missing git binary cannot raise out of the default-branch lookup
- fix(site): the hero img declares the published film's 1920x1080 box
- fix(coding-agent): wire the branch summary reserve
- fix(proof): keep duplicate-frame guard fatal and fix wait_for_screen
- fix(coding-agent): reuse shared type guards
- fix(ci): resolve discovered spawn targets
- fix(statusline): preserve model segment against wide location paths
- fix(proof): verify scoped ImageMagick cleanup
- fix(coding-agent): bind focused revival to agent identity
- fix(coding-agent): retain acknowledged job delivery
- fix(agent): isolate and recover side requests
- fix(ai): settle Cursor transport failures
- fix(coding-agent): harden eval persistence
- fix(compaction): restore Codex server-side compaction and fix OpenAI authorization headers
- fix(compaction): exclude Codex from server-side compaction and restore OpenAI request auth
- fix(coding-agent): retain restarted daemon completions
- fix(coding-agent): preserve completed subagent state
- fix(ai): terminate failed Cursor exec handlers
- fix(agent): abort paused runs without dispatching
- fix(launch): prevent empty render on undefined op and fallback to text content
- fix(coding-agent): attach late background results
- fix(coding-agent): settle interrupted agent status
- fix(proof): scope ImageMagick temp files
- fix(coding-agent): park failed compaction runs
- fix(agent): isolate side-request conversations
- fix(proof): standardize terminal capture at 15pt font and 128px margin
- fix(proof): wire auxiliary recorders to centralized scene configuration
- fix(proof): forward SCENE_FPS into hero-cut CUT_ARGS
- fix(proof): add build needle fallbacks to demo-hd verification guard
- fix(proof): centralize scene configuration across recorders and sessions
- fix(proof): drive the hero scene the way the take needs it
- fix(proof): record the hero at the rate the pipeline actually delivers
- fix(coding-agent): route export temp write through the atomic-write owner
- fix(coding-agent): the evidence ledger trims a working directory through the shared helper
- fix(coding-agent): reach the discard contracts through their owner, not the barrel
- fix(proof): stop the recorder capturing three frames a second
- fix(coding-agent): an escape sequence split across two chunks keeps its text
- fix(utils): sort the module census so a declarer list is not the filesystem's order
- fix(ai): type the structural probe list so the classification inventory suite compiles
- fix(coding-agent): record the read tool prompt budget the list form spends
- fix(coding-agent): tell the model when the block gave up on drawing a picture
- fix(natives): a wrapped row continues under its own indent
- fix(coding-agent): drop the image-visibility statement when the images are scrubbed
- fix(session): harden multi-file code review reminders
- fix(coding-agent): say whether an image reached the screen
- fix(coding-agent): one left edge per tool block
- fix(ai): honor the Portal poll interval and accept a device response without the completion URI
- fix(natives): validate persisted AVX2 verdicts
- fix(providers): finalize Command Code and Nous integration
- fix(providers): correct Command Code and Nous integration
- fix(settings): Files → LSP row keeps the Language Servers name
- fix(settings): nested LSP copy and regenerate the settings reference
- fix(settings): format nested LSP settings and refresh the handbook book
- fix(ai): a codenamed lite model keeps the transport and sends all_turns
- fix(coding-agent): the theme ground goes on with the launch card
- fix(ai): require cross-turn reasoning context support for Responses Lite transport
- fix(natives): stock-Windows AVX2 probe and persisted host verdicts
- fix(utils): the retry loop asks the caller about every failed response
- fix(ai): the provider ladder asks the registry what the transport stage does
- fix(ai,utils,agent): a cancellation is refused by every reader of the retry decision
- fix(models): the static-stage snapshot now hits on a warm launch
- fix(ci): the root changelog matches its sources and no comment attributes a change
- fix(tui): the render floor answers a question about a window
- fix(demo): the cadence gate reads the moving portion, not the mode
- fix(coding-agent): the board pulse stops below a full cell
- fix(hashline): an insertion may anchor a line too wide to print
- fix(proof): prevent duplicate frames in scene recorder marks
- fix(ai): an ollama stream keeps the bytes its healer was holding
- fix(ai): one home per error class, so classification keys off identity
- fix: regenerate the legal bundle and assert provenance in the file that owns it
- fix(ai): one owner-only helper creates every request-debug dump file
- fix(mcp): refuse a server whose command or url is still a placeholder
- fix(coding-agent): keep a test credential that resolves, and the deadline capture to the deadline
- fix(coding-agent): stop building a prompt date no section renders
- fix(ai): drop the in-flight resolution when a credential cache is reset
- fix(extensions): load an operator-named path without the project-trust gate
- fix(proof): a capture reaches the model and runs the bun the product requires
- fix(coding-agent): a missing variable is never sent as the credential
- fix(utils): a peer that never ends a frame cannot exhaust the heap
- fix(ai): a debug capture cannot fill the disk it is debugging
- fix(tools): complete unified search cutover
- fix(ai): a failed response body cannot outgrow the error it explains
- fix(tools): preserve unified search semantics
- fix(mcp): a stdio server takes everything it spawned with it
- fix(security): an MCP credential that cannot be presented is not replaced by none
- fix(security): a rotated credential is re-read instead of re-sent
- fix(security): an MCP server sees only the environment it was given
- fix(security): a repository's code needs a decision before it runs
- fix(approval): a misspelled per-tool policy blocks the tool it names
- fix(bench): the boot guard refuses a baseline it cannot compare against
- fix(context): the gauge counts the provider figure the running turn produced
- fix(proof): a take resolves its publishing tools before it spends minutes recording
- fix(mcp): the loader forwards every discover option it is given
- fix(goals): a suppressed goal record is logged with its goal id
- fix(goals): only an operator interrupt pauses an active goal
- fix(goals): a recovered provider hiccup does not stop a goal
- fix(goals): a goal is restored or reported, never unset in silence
- fix(ttsr): defer bundled coding guidance
- fix(secrets): make a masked value findable, and empty every vault in one command
- fix(ai): grade a terminal provider record by the outcome, not by the catch running
- fix(coding-agent): grade a recorded session exit, and stop the anchored HUD when its mode stops
- fix(utils): reject a non-object eval prompt override through isRecord
- fix(deepswe-bench): drop an unused type import from the runner
Release notes were shortened from 327,052 characters to fit GitHub's 125,000-character body limit. Read the complete package changelogs and full commit range.