Unreleased
next release
Added
- session.backgroundLimit (default 3, range 1-20) caps how many conversations run in the background at once, whether /new, RPC or ACP moved them there; a handoff past it stops the oldest background conversation and names it in the handoff message.
- RPC new_session with background: true keeps a streaming session's turn running in the background and attaches the client to a new session, switch_session to a background conversation's transcript re-attaches the live session, get_background_sessions lists background conversations, cancel_background_session stops one, and the data.background handoff reports streaming: false for a session that moved with no turn running.
- ACP _veyyon/sessions/background moves a streaming session to the background, where its pending session/prompt resolves when the turn ends and no session/update is sent; session/load or session/resume re-attaches it live, _veyyon/sessions/background/list lists background conversations, _veyyon/sessions/background/cancel stops one, and closing the connection stops every conversation it moved there.
- /resume marks conversations running off-screen as running, and ctrl+x stops the selected one without resuming it.
- A bash approval card offers Approve "<pattern>" for session, which allows later calls reporting the same pattern (git status * for git status -s) without prompting; a call with quoting, expansion, a pipe, a redirect, an environment variable, a working directory, or a guard flag offers no pattern row, and a program that runs another command (sudo, xargs, env) is granted only its exact command.
- The lsp tool's incoming_calls and outgoing_calls actions list the functions that call the symbol at the cursor, or that it calls, with each call site, as the language server resolved them.
Changed
- The assistant text reveal and the tool argument reveal run on one RevealFrameClock; both still tick at 30 frames per second only while text is held back.
- The stdio, streamable-HTTP and SSE MCP transports answer a server-to-client request through one answerServerRequest in mcp/types; each answer is unchanged.
- Tool result text joining and the web tool display projection use template literals and optional chains; their output is unchanged.
- The custom tool file-extension list in discovery/helpers.ts carries a not-a-tool-name: marker, because its "bash" names an extension rather than the bash tool; no user-visible change.
- createAgentSession reads the caller's telemetry config in two fewer statements; its behavior is unchanged.
- /dump and /advisor dump raw write each message role through its own function in session/session-dump-format.ts; the transcript is unchanged.
- A launch builds no ICU time zone cache, number formatter or collator before the first frame: the terminal guard reads the local date from the C library, the tts tool description prints its character limit literally, and prompt templates sort variable names by code unit, so the source launch paints its first frame in 58.5 ms instead of 60.5 ms and its ready run peaks at 217.1 MiB RSS instead of 220.4 MiB (median of 30 and 11).
- A launch builds no ICU time zone cache or grapheme segmenter: the logger, the session's tool signature and the date the session states to the model read local time from the C library, and the grapheme segmenter is built on first use, so the compiled binary reports ready in 180.3 ms instead of 188.0 ms and paints its first frame in 37.0 ms instead of 38.4 ms (median of 30), and 8 s after launch holds 249.5 MiB RSS instead of 254.3 MiB, 118.5 MiB anonymous memory instead of 119.7 MiB and a 260.4 MiB peak instead of 264.8 MiB (median of 10).
- A launch evaluates yaml on the first settings file edit or secret declaration parse, @opentelemetry/api on the first traced span, and diff on the first diff it generates or renders, instead of with the module graph, so an RPC launch of the compiled binary evaluates 1,876 modules instead of 1,991 and holds 190.6 MiB RSS instead of 194.6 MiB, 93.4 MiB anonymous memory instead of 97.2 MiB and a 199.1 MiB peak instead of 203.7 MiB (median of 31).
- A launch with secrets.enabled parses the bundled environment-keyword list with Bun's YAML parser and evaluates yaml only for a keyword file a user wrote, so an RPC launch of the compiled binary with secret obfuscation on holds 158.5 MiB RSS instead of 161.7 MiB, 74.9 MiB anonymous memory instead of 77.1 MiB and a 169.2 MiB peak instead of 172.8 MiB, on 210 ms of CPU instead of 220 ms (median of 31).
- A session created without a telemetry config starts no OpenTelemetry span and evaluates no @opentelemetry/api module, instead of tracing every turn through the global tracer so that span text passed through the secret obfuscator, so an interactive session of the compiled binary spends 225 ms of CPU on turns two to six instead of 240 ms (median of 12) and holds 306,933 heap objects instead of 309,753 after four turns (median of seven); a session given a config still obfuscates secrets in its spans.
- A launch evaluates arktype when the first schema is built instead of in the runtime warmup ahead of the session: shipped source imports arktype values from @veyyon/ai/utils/schema/arktype, the completion bridge and the custom command API's arktype member evaluate it on first use, and the warmup has no arktype stage, so the compiled binary names the model 183.6 ms after launch instead of 191.2 ms with createAllTools taking 7.4 ms instead of 13.7 ms (median of 15), and a session idle before its first turn holds 159.0 MiB RSS instead of 166.7 MiB (median of five); 40 s after a first turn it holds 178 MiB instead of 176 MiB with the same JS heap.
- Loading a models config unchanged since its last accepted load evaluates no ArkType module, because the validator fingerprint reads the ArkType release from package metadata instead of the $ark registry that the eager arktype import installed.
- A print, RPC or ACP session holds its at-rest context reading, which builds every active tool's schema, until its first prompt, as an interactive session already did, and the CLI's jitless ArkType setting is held until the first schema is built, so an RPC launch of the compiled binary with a provider key evaluates no ArkType module before its first turn: it evaluates 1,741 modules instead of 1,856, reports ready in 187.1 ms instead of 203.3 ms on 250 ms of CPU instead of 340 ms, and holds 170.9 MiB RSS instead of 190.2 MiB, 76.9 MiB anonymous memory instead of 93.6 MiB and a 184.2 MiB peak instead of 197.9 MiB (median of 31).
- The presentation summary resolves the server-side compaction transport through @veyyon/agent-core/compaction/remote-compaction instead of @veyyon/ai/providers/openai-compaction, so loading it no longer evaluates the Codex client; no user-visible change.
- A print, RPC or ACP launch evaluates no terminal renderer: main.ts loads the session picker only when --resume opens it, the launch command loads the launch card only when it paints, and the ask tool reads its card width from chrome/modal-geometry.ts instead of the modal shell, so main.ts reaches 1,467 modules instead of 1,582 and an RPC launch of the compiled binary reports ready in 221 ms instead of 228 ms with 190.6 MiB RSS instead of 196.7 MiB and a 205.3 MiB peak instead of 212.2 MiB (median of 20).
- A streaming assistant message finds a Mermaid fence by jumping between runs of three backticks or tildes instead of splitting its whole text into lines on every delta, so a 108 KiB reply streamed in 24-character deltas spends 26 ms in message updates instead of 152 ms, and 735 ms instead of 892 ms with a 120-column render after each delta (median of three).
- With smooth streaming on, an assistant message counts, slices and renders a provider delta on the next reveal frame instead of on every delta, and a delta reads only the blocks not yet visible to decide a layout repaint, so a 200,000-character reply streamed in 24-character deltas costs 780 ms of process CPU per turn instead of 940 ms (median of 15 turns).
- With smooth streaming on, assistant text and streamed tool-call arguments reveal at the rate they arrive, held behind the arrivals by a lead sized to the gaps between them, instead of by an eighth of the waiting units every frame, so a reply arriving in 120-character batches every 400 ms reveals 8 to 11 characters a frame instead of 4 to 19, one arriving 4 characters every 200 ms reveals one character at a time instead of up to three, and one arriving 200 characters every 100 ms is shown 200 ms after its last delta instead of 1,000 ms (bench/reveal-pacing.bench.ts).
- The transcript reports every leading row a re-rendered block left unchanged as stable instead of none of the block's rows, so the terminal engine re-reads a streamed reply from its first changed row instead of from the reply's top on every frame, and a 1,200,000-character reply streamed in 48-character deltas costs 2.69 s of process CPU per turn instead of 2.87 s (median of three), a 400,000-character reply 1.00 s instead of 1.05 s (median of five).
- A streamed delta or tool event rebuilds the anchored todo board only when the agent's motion state, the terminal size or the theme changed since the board was last drawn, instead of on every event, so a 400,000-character reply streamed under a live three-phase board costs 1.18 s of process CPU per turn instead of 1.59 s (median of four), and the same reply with no board is unchanged.
- A spinner step on a streaming write, edit or apply_patch card draws the card again only when a row it drew reads the frame, because a framed block reads the frame for its header and arrival row when it composes its rows and the card keeps its serialized arguments between builds, so a step of a 1,500-line streaming write costs 89 µs instead of 357 µs and of a streaming edit 15 µs instead of 230 µs (median of three), and a turn that streams a 1,500-line write in three deltas over 20 seconds costs 1.09 s of process CPU instead of 1.40 s (median of six).
- A streaming tool call's argument reveal parses the call's JSON only when bytes arrive outside the string argument its decoder reads, and a tool card serializes a call's arguments when a block's input is read instead of on every revealed step, so revealing a 128 KB write parses 128 KB of JSON instead of 27 MB in 6 ms instead of 55 ms (median of seven), and a turn that streams a 1,500-line write costs 2.82 s of process CPU instead of 2.96 s (median of nine).
- A rule with an ast-grep astCondition matches a streaming write or edit call again only after its source has grown by a quarter and by at least 1,024 characters, runs one match per call at a time with only the newest snapshot queued behind it, and always matches the completed call before its reminder is delivered, so a turn that streams a 1,500-line write under one such rule runs 14 matches over 756 KB of source instead of 3,814 over 233 MB, and costs 0.34 s of process CPU instead of 123.8 s with a 131 MiB RSS peak instead of 336 MiB.
- The status line runs git status for its dirty marker when a turn ends, after a ! or % command and when HEAD changes, and otherwise once every 10 seconds, instead of on any render a second after its last lookup, so a 13-second streaming turn in a checkout of this repository spends 110 ms of CPU in child processes instead of 555 ms and 1.48 s of process CPU instead of 1.55 s (median of four).
- A running agent's live preview appends each streamed delta to its output tail and derives its last eight lines when it reports progress, at most every 150 ms, instead of splitting the 8 KB tail on every delta once the agent has streamed 8 KB, so a turn that spawns one agent streaming a 400,000-character answer costs 760 ms of process CPU instead of 830 ms (median of nine turns).
- On a terminal with the Kitty image protocol, a tool card finds the images of each streamed partial result in the result instead of building the card's views for it, so a bash card streaming a 50 KB build log costs 259 µs of CPU per drawn frame of four chunks instead of 779 µs, a test log 415 µs instead of 965 µs and a log of wrapped lines 208 µs instead of 373 µs (median of seven).
- A tool card keeps the wrapped rows of each windowed section instead of only the last one drawn, and finds the lines a tail buffer moved up when it cut its front, so a streaming bash card wraps only the lines that arrived since the frame before: a frame of a 50 KB log of wrapped lines renders in 193 µs of CPU instead of 588 µs and of a test log in 388 µs instead of 441 µs (median of seven).
- A command's 50 KB output tail drops the characters a chunk pushed out by walking them off its front instead of encoding the whole tail into a buffer and decoding it back, so keeping the tail of a streaming test log costs 19 µs of CPU per four chunks instead of 83 µs, of a build log 10 µs instead of 24 µs and of a log of wrapped lines 12 µs instead of 26 µs (median of seven).
- A streaming bash card reads the progress-run shape of each output line by character code instead of with five regular expressions, reads the home directory once per os.homedir function instead of once per shortened span, no longer searches its already shortened output rows for the home directory, and computes the render context's output when a renderer reads it, so a frame of four chunks of a 50 KB build log costs 197 µs of CPU instead of 259 µs, of a test log 320 µs instead of 423 µs and of a log of wrapped lines 167 µs instead of 199 µs (median of nine).
- A command's 50 KB output tail holds the chunks appended since its last cut concatenated after the kept front instead of joining both into a new string on every read, so a streaming bash card that receives 64 chunks per drawn frame costs 391 µs of CPU per frame of a build log instead of 766 µs, of a test log 407 µs instead of 942 µs and of a log of wrapped lines 387 µs instead of 582 µs (median of seven), and at four chunks per frame a build log costs 182 µs instead of 197 µs (median of nine).
- An interactive session loads the /mcp, /ssh, /tan and /todo command controllers on the command's first run, the speech-to-text recorder and recogniser on the first push-to-talk, the DAP client on the first debug call, the emoji table on the first :name completion and the GitLab Duo Workflow client on its first turn, so an idle session evaluates 220 KiB less source and holds 303,171 heap objects instead of 314,768 and 55.10 MiB of heap and extra memory instead of 55.76 MiB, with banner time unchanged (median of seven).
- An interactive session loads the /secret, /mcp and /ssh handlers, the /plugins manager, the /memory mm Hindsight helpers, the autoresearch run screen and launcher and the /review <pr> GitHub client on first use, so an idle session evaluates 34 fewer modules and 542 KiB less source and holds 295,277 heap objects instead of 299,223, 54.51 MiB of heap and extra memory instead of 54.98 MiB and 242 MiB RSS after collection instead of 245 MiB, with banner time unchanged (median of seven).
- The compiled binary's legacy-extension table imports each retained host module's exports by name and builds a module's export record when an extension first imports it, instead of importing all 1,057 modules as namespaces, so the binary's chunks declare 27 module namespace objects instead of 1,031 and 1,395 export getters instead of 8,914, and an idle session holds 284,103 live objects instead of 325,240, 38.2 MiB of heap instead of 41.8 MiB and 246 MiB RSS after collection instead of 255 MiB (median of five).
- An interactive session at rest arms no working-clock heartbeat, stops its loop watchdog after 10 quiet seconds and its idle trim after the trim, and samples its stack once a second, each until the next keystroke or frame, so the runtime's idle collector drops to its slow rate: the linux-x64 binary idle 90 seconds after launch records 131 thread wakeups in 60 seconds instead of 4,062 and spends 20 ms of CPU instead of 160 ms (median of three).
- Capability discovery finds the repository root by checking each ancestor of the working directory for .git instead of listing and keeping every ancestor directory, and concurrent loads share one lookup; twenty concurrent lookups from a directory under a 1,700-entry directory take 0.31 ms instead of 2.50 ms and leave 32 KiB on the heap instead of 259 KiB, and from a home directory outside any repository take 0.30 ms instead of 1.85 ms and leave 33 KiB instead of 185 KiB (median of 15).
- A session compares a SHA-256 digest of its tool names, descriptions and MCP server instructions to decide whether a tool change rebuilds the system prompt, instead of keeping the joined text, so each main session and live subagent holds 87,568 fewer bytes of heap once its tool set has been applied twice with the default tools.
- On Linux the binary's embedded module pages are released on the first 5-second window under 5% CPU after work instead of after 30 quiet seconds, so an idle session 15 seconds after launch holds 190 MiB RSS instead of 270-286 MiB, and a session with turns 12 seconds apart holds 209-216 MiB after its second and third turns instead of 290-295 MiB, at unchanged turn latency.
- The compiled binary holds one copy of Zod instead of two, because the schema converter no longer requires a CommonJS copy beside the one the package barrel hands to extensions; the binary is 2.3 MiB smaller and an idle session holds 3 MiB less RSS after collection (270 to 267 MiB, median of five).
- The resolved model stage (resolved-models.json) stores each distinct compat record once and each model an index into that table, so on a profile with 5,646 models the file is 0.81 MB instead of 2.10 MB and a launch restores the model registry in 10.2 ms instead of 22.2 ms, with 4.5 MiB less RSS growth (median of 11); a stage written by an earlier release is rebuilt once.
- 261 class members that read no instance state are module functions and constants instead of #private members; JSC copies every private name of a class into each function its body creates, so with the 73 moved in the other packages the binary shrinks by 815 KiB and an idle session holds 580 KiB less RSS after the idle trim (median of five).
- The isolation runner loads the commit message generator, its model helper and the commit prompts on the first AI-written isolation commit instead of when the task tool loads, so an idle session holds 390 fewer live objects and 0.10 MiB less heap (median of five) and a source session evaluates 16 fewer modules.
- The veyyon:// handler imports the embedded documentation index on its first resolve or completion instead of at launch, so a compiled binary no longer links the 1.4 MB documentation literal into the startup graph, and an idle session holds 0.8 MiB less RSS five seconds after launch (median of seven); the JS heap and the RSS after the idle trim are unchanged.
- The Mermaid render cache imports lru-cache/raw like every other module, so a session evaluates one lru-cache build instead of two: the binary shrinks by 296 KiB and an idle session holds 537 fewer live objects (median of seven).
- AgentSession runs its retry ladder through session/runtime/retry-runtime.ts and its secret redaction and display expansion through session/runtime/session-secrets.ts, and holds 404 private names instead of 487, so its compiled bytecode drops from 9,474 KiB to 7,646 KiB across the three modules and loading it from a --bytecode binary costs 18,660 KiB RSS instead of 21,424 KiB (median of nine).
- The streaming reveal counts and slices each run of ASCII text one code unit per cluster and passes only the clusters that contain a non-ASCII character to Intl.Segmenter, with identical cluster boundaries, so revealing a 400,000-character reply costs 1.5 ms of grapheme work instead of 21.0 ms for ASCII prose and 11.1 ms instead of 21.3 ms for prose with CJK or emoji clusters (median of three).
- The status line keeps the scratch-directory classification of the working directory until the working directory, the home directory or the temporary directory changes, instead of resolving each root with realpath on every render, so a status row renders in 9.2 µs instead of 21.9 µs warm (median of three).
- AgentSession runs compaction through session/runtime/compaction-runtime.ts, compaction-summarizer.ts and compaction-recovery.ts, and the retry ladder's model switches through session/runtime/retry-fallback.ts, and holds 372 private names instead of 410, so the compiled bytecode of the session and its retry and compaction modules drops from 7,568 KiB to 6,926 KiB and loading them from a --bytecode binary costs 33,144 KiB RSS instead of 39,144 KiB (median of 15).
- AgentSession runs the prewalk and plan-yolo model handoffs through session/runtime/model-handoff.ts and holds 362 private names instead of 372, so the session's compiled bytecode in a --bytecode binary drops from 6,712 KiB to 6,484 KiB and loading it costs 32,240 KiB RSS instead of 32,512 KiB (median of 15).
- AgentSession runs plan mode, its approved-plan reference and its decision reminders through session/runtime/plan-mode-runtime.ts and holds 355 private names instead of 363, so the session's compiled bytecode drops from 6,153 KiB to 5,994 KiB and loading it from a --bytecode binary costs 32,068 KiB RSS instead of 32,180 KiB (median of 15).
- AgentSession writes finished messages to the session log through session/runtime/message-persistence.ts and holds 340 private names instead of 354, so the compiled bytecode of the session and its message persistence drops from 6,012 KiB to 5,736 KiB and loading them from a --bytecode binary costs 31,652 KiB RSS instead of 31,852 KiB (median of 15).
- AgentSession computes the context figure, the prompt snapshot of a run in flight and the history-rewrite usage anchor through session/runtime/context-accounting.ts and holds 335 private names instead of 340, so the compiled bytecode of the session and its context accounting drops from 5,676 KiB to 5,580 KiB and loading them from a --bytecode binary costs 31,388 KiB RSS instead of 31,512 KiB (median of 15).
- AgentSession holds the memory backend's session state and delivers recalled memory through session/runtime/memory-context.ts and holds 325 private names instead of 335, so the compiled bytecode of the session and its memory context drops from 5,540 KiB to 5,381 KiB and loading them from a --bytecode binary costs 31,088 KiB RSS instead of 31,336 KiB (median of 15).
- AgentSession routes requests under its provider session id and inherited prompt cache key, and closes provider transport sessions, through session/runtime/provider-sessions.ts and holds 313 private names instead of 325, so the compiled bytecode of the session and its provider sessions drops from 5,347 KiB to 5,175 KiB and loading them from a --bytecode binary costs 30,864 KiB RSS instead of 31,024 KiB (median of 15).
- AgentSession runs the tool-call loop guard and the Gemini reasoning-header interrupt through session/runtime/loop-guards.ts and holds 306 private names instead of 313, so the compiled bytecode of the session and its loop guards drops from 5,044 KiB to 4,927 KiB and loading them from a --bytecode binary costs 30,592 KiB RSS instead of 30,744 KiB (median of 15).
- AgentSession runs the empty-stop and unexpected-stop retry cycles through session/runtime/stop-retries.ts and holds 298 private names instead of 307, so the compiled bytecode of the session and its stop retries drops from 4,994 KiB to 4,838 KiB and loading them from a --bytecode binary costs 30,328 KiB RSS instead of 30,516 KiB (median of 15).
- AgentSession records usage headers and turn cost, reads usage reports and saved resets, and runs Codex auto-redeem through session/runtime/provider-usage.ts and holds 295 private names instead of 298, so the compiled bytecode of the session and its provider usage drops from 4,795 KiB to 4,736 KiB and loading them from a --bytecode binary costs 30,160 KiB RSS instead of 30,256 KiB (median of 15).
- AgentSession runs the stale-result and overflow prunes, image drops, shake and duplicate-result elision through session/runtime/history-rewrites.ts and holds 285 private names instead of 289, so the compiled bytecode of the session and its history rewrites drops from 4,686 KiB to 4,572 KiB and loading them from a --bytecode binary costs 29,976 KiB RSS instead of 30,188 KiB (median of 21).
- AgentSession tracks the terminal yield through session/runtime/yield-tracker.ts and sends the rewind, verification and review reminders a settling turn owes through session/runtime/finalize-reminders.ts, and holds 276 private names instead of 285, so the compiled bytecode of the session and its yield and finalize modules drops from 4,460 KiB to 4,334 KiB and loading them from a --bytecode binary costs 29,720 KiB RSS instead of 29,888 KiB (median of 15).
- AgentSession holds the --auto-approve flag, the /yolo bypass, the kept per-tool approval decisions and the ACP permission prompt with its standing answers in session/runtime/session-approvals.ts and holds 270 private names instead of 276, so the compiled bytecode of the session and its approvals drops from 4,303 KiB to 4,209 KiB and loading them from a --bytecode binary costs 29,604 KiB RSS instead of 29,676 KiB (median of 15).
- AgentSession shapes every provider request, with its tool-call id map, the directory outbound paths render relative to and the bytes each request left out, through session/runtime/provider-wire.ts and holds 265 private names instead of 270, so the compiled bytecode of the session and its provider wire drops from 4,179 KiB to 4,139 KiB and loading them from a --bytecode binary costs 29,484 KiB RSS instead of 29,584 KiB (median of 15).
- A compaction moves the payloads of the history it summarized out of memory once it is recorded, reading each entry back from the session file on first use.
- An interactive session no longer loads the HTML export template, stylesheet, viewer script and tool renderers until /export runs: /share builds its snapshot from export/session-data.ts, cutting the idle heap and extra memory of the compiled binary from 104.8 MiB to 102.9 MiB.
- The compiled binary keeps the embedded veyyon:// docs index off the heap until the first veyyon:// read, and with the bundled model catalog read from its embedded file on demand the idle heap and extra memory drop from 102.9 MiB to 95.9 MiB; dist/cli.js ships the catalog as dist/models.json.
- The advisor renders its project-context prompt from the context files when it first starts instead of at session creation, and with the shared prompt copies and record-only entries read from the session file, a session holding 40 finished spawned agents after ten turns uses 150.6 MiB of heap and extra memory instead of 175.5 MiB and 479-484 MiB RSS instead of 488-497 MiB.
- An interactive session loads the settings, model, account, session, tree and dashboard cards when the first one opens, so an idle session holds 546,422 heap objects instead of 551,514 and 304 MiB RSS after a full collection instead of 309 MiB, with peak RSS 332 MiB instead of 338 MiB (median of five).
- The Mermaid ASCII render cache holds at most 512 Ki UTF-16 code units of source and render, least recently used first out, instead of every frame of every streamed diagram for the life of the session, so streaming 20 diagrams retains 4.3 MiB instead of 32.4 MiB.
- A launch reads a models.yml the validator accepted before from accepted-models-config.json beside models.db while the file, its path and the validator are unchanged, instead of building the models-config schema, so an idle session holds 75.9 MiB of heap and extra memory instead of 81.9 MiB, 496,284 heap objects instead of 528,818 and 293 MiB RSS after a full collection instead of 303 MiB, and draws its banner in 0.243 s instead of 0.348 s (median of five warm launches).
- The compiled binary precompiles every Handlebars prompt template at build time and revives it on first render instead of parsing and walking it, so a session builds its system prompt in 6.4 ms instead of 16.1 ms, createAgentSession takes 77.2 ms instead of 88.1 ms and the status line names the model 303 ms after launch instead of 315 ms (median of 11), and an idle session holds 71.5 MiB of heap and extra memory instead of 72.2 MiB (median of five).
- The order a session's disposal runs in moved from createAgentSession to orderSessionDisposal in session/top-level-sessions.ts; no user-visible change.
- A conversation /new left running is disposed once its turn ends and no background job it owns is running, releasing its browser tabs, eval kernels, advisor, spawned agents and roster entry and writing its session_exit record, instead of holding them until the process exits.
- A session builds the advisor's tools on the advisor's first review or /advisor configure instead of at startup, so a session whose advisor never runs constructs no second copy of every built-in tool: the compiled binary's idle heap and extra memory drop from 58.8 MiB to 57.6 MiB across 578 loaded modules instead of 606, startup RSS from 334 MiB to 328.5 MiB, the first frame lands 13 ms sooner (median of six), and three turns spawning four agents each peak at 406-409 MiB RSS instead of 415-418 MiB.
- The launch tool's output renderer loads @xterm/headless on its first screen replay, through the loader the interactive bash tool already used, so a session that replays no PTY screen no longer evaluates it at startup, saving 1.02 MiB of heap and 5.5 ms of module evaluation.
- MCP servers stay connected until the last top-level session using them is disposed, so a conversation /new left running no longer orphans them at exit, and disposing one of several top-level sessions (a background conversation or an ACP session) no longer tears down the agent lifecycle or the title and embedding workers the others use.
- ctrl+x in /resume and a /new past session.backgroundLimit cancel the stopped conversation's background jobs.
- /new, /resume and a handoff abort a spawned agent of the previous conversation that is mid-turn before disposing it, deepest generation first, instead of disposing it with its bash, eval and advisor work still running.
- A config with compaction.strategy: "off" beside compaction.enabled: true migrates to compaction.enabled: false, and compaction.enabled is the only setting that turns auto-compaction off.
- The HTML export's session tree prints each tool call with the same label as the terminal tree, for every registered tool, instead of its own switch whose find and ls cases matched no registered tool and printed most calls as raw JSON; a newline in an op, action or name argument no longer breaks a tree row in either tree.
- tools/fs/read.ts is split into per-concern read-*.ts modules with no change to the read tool's schema, output or exports.
- The session retry loop, the async job manager, the launch broker and the Gemini web search provider take their exponential delay from exponentialBackoffDelay in @veyyon/utils; each loop's base, ceiling and jitter are unchanged.
- The resolve pending row clamps its reason at 80 columns instead of 72, and the web_search and gh cards expand tabs in query and label text.
- Production code strips ANSI through stripAnsi from @veyyon/utils; Bun.stripANSI is called only by that helper.
- A session that renders its personality from a project .veyyon/personalities/<name>.md file raises a personality warning stating the file, and personality warnings reach the session's notice channel once per session instead of writing to stderr on every system prompt rebuild.
- Restoring resolved-models.json at launch verifies a CRC-32 instead of a SHA-256 digest, cutting model registry construction from 22.86 ms to 20.69 ms (median of 21 interleaved launches), and the first launch after upgrading rebuilds the snapshot once.
- The static model stage restored from resolved-models.json shares equal compat records the way a catalog build does, so a warm launch holds one record per distinct compat shape instead of one per model.
- The model settings domain reads the thinking vocabulary from thinking/constants.ts instead of thinking/index.ts, so the launch card, editor and status row evaluate 355 modules instead of 358 before the composer accepts a keystroke, skipping 0.85 ms of module evaluation (median of nine).
- A launch whose --model names its provider (provider/id, or an id with --provider) looks the model up among that provider's models instead of building all 4,499 catalog models first, skipping 48 ms of catalog construction, so a compiled --model provider/id launch draws its first frame in 310 ms instead of 333 ms (median of 13 interleaved launches).
- The /debug CPU profile borrows the process's sampling profiler from the stall sampler and hands it back when the profile ends, instead of starting and stopping the profiler itself and leaving the stall sampler's profile stopped.
- Assistant turns and tool cards create their image, conversion and rail-frame collections on first use and share one rail-frame table instead of each constructing its own, and a tool card's block producer holds its listeners in a shared empty array until one subscribes, so a rebuilt 26,806-entry transcript holds 2,547 Maps, 1 Set and 0 WeakMaps instead of 37,211, 29,120 and 7,858, retaining 64.1 MiB and 1.10M objects instead of 68.3 MiB and 1.17M.
- A tool card builds its block on the frame that draws it instead of on each call, result, spinner start and stop handed to it, and reads its animation, freeze, displacement and repaint policies without building its views, so a rebuilt 81,240-row transcript builds each card once and its rebuild drops from 113.2 ms to 91.7 ms (median of five) with an unchanged first frame.
- Assistant answer and thinking segments, custom and skill messages, summaries, collab prompts and hook dialogs paint their prose through one style object per theme colour instead of a new one per component, so a rebuilt component reuses the rows its predecessor rendered from the shared Markdown render cache, and a resumed 38 MB transcript draws its first frame in 218.3 MiB and 194 ms instead of 223.4 MiB and 219 ms (206.1 MiB and 183 ms with the Box row change).
- A provider request's secret scan walks the payload with one frame per container that copies its keys or values only when the transform changes one, instead of queueing an event per key and value and re-measuring every unchanged string, and the literal-secret matcher steps its root state through a code-unit table and skips the second literal pass when no rule changed the text, cutting the scan of a 1,200-turn request with two configured secrets from 156.2 ms to 22.9 ms and an identity walk of it from 57.2 ms to 3.7 ms, with identical output across 400,000 generated payloads and 480,000 generated texts.
- A rebuilt transcript's highlight prefetch holds each source until every card that draws it has read it, including sources the 256-entry highlight cache held when the prefetch ran, instead of dropping it for that cache on its first read, so a command run again or a rebuild after an earlier one no longer re-highlights evicted sources one at a time; a 13,470-entry transcript draws its first frame with 0 instead of 284 such highlights (512 on later rebuilds), from 214.5 ms to 200.2 ms.
- A session reads its persisted MCP tool selection at construction and on each MCP tool refresh from the one entry that records it instead of rebuilding, deobfuscating and argot-expanding the branch's whole message list, cutting createAgentSession on a 26,806-entry resume from 40.2 ms to 35.4 ms.
- A resumed read result numbers its card rows by scanning the result text in place instead of slicing and regex-matching each row, cutting SessionManager.open on a 76 MB session from 179.9 ms to 143.9 ms with identical cards across 400,000 generated result texts.
- The CLI configures ArkType to validate by interpreted traversal instead of compiling a validator for every schema, cutting a compiled launch that opens a session from 372 ms to 316 ms and its heap by 11 MiB; a tool-argument rejection of a described closed set now reports the field's description followed by (accepted: …) instead of ArkType's list of the members.
- The debug tool runs each action through its own handler in a table keyed by action instead of one 398-line switch, so an action added to the schema without a handler fails the type check; every action's text and details are unchanged across a gdb session.
- The launch parser sets each value-less flag through the table the profile bootstrap reads as its list of value-less flags, instead of an if-chain that had to be kept in step with that list by hand, and splits a token's flag, extension, string, optional and positional handling into single-purpose functions instead of one 183-line loop; every result and refusal is identical across 300,000 generated argument lists.
- The edit tool reads its mode descriptions from the tool prompt rows instead of the whole prompt registry, so a session that builds it no longer loads the commit-agentic, bench and prompt-registry modules it never renders, 9 fewer modules on a session start.
- A session binds its own inline extensions (autoresearch and the custom tool bridge) without the api.pi package namespace, so a session with no author extension no longer loads the whole package barrel, cutting createAgentSession on a 13,470-message resume from 267 ms to 213 ms and its heap by about 5 MiB; an author's extension still receives api.pi.
- A session checks whether its working directory is a project root once when it discovers the project, and again only when its cwd moves, instead of on every system prompt build, cutting a rebuild in the veyyon checkout from 1.16 ms to 0.23 ms and in a repository holding other projects from 75 ms to 0.25 ms with identical blocks.
- A system prompt build looks up its inputs, names its tools, fills its template data, checks its eval and file overrides and assembles its blocks in single-purpose steps instead of one 520-line function, and reuses the paragraph split of a context file, rule or appended prompt it already compared instead of re-formatting the text for every comparison, cutting a rebuild with a 48 KB AGENTS.md from 1.56 ms to 1.23 ms with identical blocks.
- init_experiment resolves its settings, writes the session, arms the runtime and builds its report in single-purpose steps instead of one 258-line execute, and reads the worktree status and path prefix concurrently when checking for harness changes, cutting a clean-branch init from 3.94 ms to 3.39 ms with an identical report, session row and tool set.
- run_experiment and log_experiment read the worktree status and path prefix concurrently through one repository resolve instead of one after the other, cutting each read on a 400-file repository from 2.20 ms to 1.65 ms.
- A spawned agent's session records no raw SSE frames, which only the displayed top-level session's debug viewer and report bundle read, so five turns spawning four agents that each stream 60,000 characters leave 84.9 MiB of heap and 36.6 MiB of extra memory instead of 95.5 MiB and 40.8 MiB, and peak at 576 MiB RSS instead of 597 MiB (mean of three).
- The eval tool builds its parameter schema once per set of enabled languages and report_tool_issue once per tool list, instead of once per session, so building both for 3,000 sessions registers no ArkType node instead of 87,000 and retains 1.8 MiB instead of 9.5 MiB.
- The lsp tool runs a file-bound action through one method that starts the server, opens the file and resolves the cursor, then one function per action (location lookups, references, hover, code actions, document symbols, rename, reload), instead of one 388-line method; no user-visible change.
- The composer's submit runs its extension input handlers, collab guest routing, shell shortcuts, steering prompt and auto-title in single-purpose methods, and the streaming and between-turns paths share one steering prompt with draft restore instead of two copies; no user-visible change.
- The bash tool runs a call's preparation, interception check, internal URL expansion, working directory resolution, spawn admission and its background, managed, client-terminal and local runs in single-purpose methods instead of one 480-line execute; a trivial command and a 20,000-line command run in unchanged time.
- A rebuilt transcript highlights the code and diff context of every tool card in one parallel native batch before its first frame instead of one card at a time during it, cutting a resumed session's first frame from 497 ms to 357 ms, and from 14.2 s to 7.5 s with compacted history expanded, with byte-identical rows.
- A tool card whose presentation changes with nothing subscribed builds its block when it is next read instead of at the change, so disposing a transcript no longer builds a sealed block for every card, cutting a transcript reset from 31.4 ms to 1.6 ms, and from 2,070 ms to 26.7 ms on a 64k-card transcript.
- A collapsed tool card's progress-run folding keys each output line once instead of twice at every run boundary and skips the diagnostic lookup for a line whose first token has no progress shape, and home-path shortening returns a line that does not contain the home directory before running its pattern, cutting the two over 1.52M lines of 44,117 tool results from 207.8 ms to 141.9 ms and from 118.6 ms to 85.9 ms with identical rows.
- veyyon session stats folds each entry through a reducer with one method per entry kind instead of one 340-line loop, cutting the report on a 104,969-entry session from 36.4 ms to 30.0 ms with an identical report.
- The legacy agent settings migration runs as one step per retired area over a shared key reader, cutting a legacy-heavy config's migration from 15.0 µs to 10.9 µs per load; a current-format config is unchanged.
- A compaction pass finds the entry it just wrote by the id the append returned instead of copying the session's entries and scanning them for its summary text, which cuts that step on a 238,086-entry session from 6.53 ms to 0.002 ms.
- Automatic compaction runs its candidate loop, per-candidate retries, progress check and follow-up scheduling in single-purpose methods, and shares the hook offer and the write step with manual compaction; no user-visible change.
- A tool card's display rebuild runs its call phase, result phase, custom and multi-file views, images and not-executed notice in single-purpose methods; resume render time on a 63,915-component transcript is unchanged.
- The first-frame replay's description of its recorded environment states that a file-driven change such as a new defaultProfile is corrected by the row diff rather than by a file stat; no user-visible change.
- A truncated read, search or run_experiment result records only its truncation counts in the session file, not a second copy of the kept text, so new results take less disk and memory and a resume parses less.
- The lsp tool dispatches each workspace-scoped action (status, diagnostics, rename_file, capabilities, request, workspace symbols, workspace reload) to its own handler, and definition, type_definition and implementation share one lookup; no user-visible change.
- A goal session records the token and time a tool call spends as a small goal_progress entry instead of a full copy of the goal, so the session file holds the objective once per goal change rather than once per tool call and a resume parses less.
- A read whose displayed rows count up without a break records no per-row line-number list in the session file, since the card numbers those rows from the first line; a read whose rows jump or skip still records the list.
- Goal records on a session branch parse through the shared isRecord guard instead of a local copy; no user-visible change.
- A read result's session file line omits the card's copy of the file text when the result's own numbered rows or plain text rebuild it, and the session restores it on load, which cut the recorded read results in local sessions from 4.27 GB to 2.89 GB.
- A tool start marker in the session file omits the start time and the argument summary the entry timestamp and the assistant message already hold, so the resume warning for an unanswered call reads the arguments from that message.
- An edit result's session file line omits the post-edit file text when its pre-edit text and numbered diff rebuild it byte for byte, and the session restores it on load, which cuts the post-edit copies recorded across local sessions from 671.18 MB to 3.79 MB.
- A search result's session file line omits the card's copy of the matched rows, the path list fileMatches already holds, and the wrapper's repeat of the sub-search's truncation counts when the rest of the line rebuilds them, and the session restores them on load, which cuts the recorded search details in local sessions from 331.33 MB to 248.91 MB.
- An eval result's session file line omits each cell's output and the top-level status events when the result's text and the first cell hold them, and a job result's line omits each job's result and error text the result's text holds, and the session restores them on load, which cuts the recorded eval details in local sessions from 497.77 MB to 253.78 MB and the job details from 100.84 MB to 37.80 MB.
- Session spend, which /session, get_session_stats and goal accounting read at every turn start, tool completion and agent end, tallies the history behind the compaction boundary once per boundary instead of on every read, which cuts a read on a 201,156-entry session from 29.25 ms to 0.92 ms.
- A prune, shake, image drop, recovered retry marker, compaction tail elision or dead-end warning rewrites the session file only from the earliest entry it changed, instead of the whole file, which cut the persist step after a one-entry prune on a 376 MiB session from 1.2 s to 135 ms.
- The session's advisors, their delivery routing and their interrupt latches run in a session collaborator, and advisor stats and overflow compaction in their own modules; no user-visible change.
- Tool discovery state (the MCP and local selections, built-in names, default MCP selections and the search index), checkpoint state (the open checkpoint, its pending rewind report and the last completed rewind), user shell and Python runs, the work a turn schedules after prompt() returns and the IRC records a streaming turn has not yet taken run in session collaborators; no user-visible change.
- With edit.streamingAbort on, the check on a streaming edit patch scans each diff line once instead of rescanning the whole diff on every delta, which cuts a turn streaming a 91 KiB, 2,000-line patch in 23,293 deltas from 7,852 ms to 212 ms, and from 124 ms to 64 ms with the setting off.
- A streaming write or bash card highlights, numbers and wraps only the lines that arrived since its last frame instead of the whole source on every argument delta, which cuts drawing a 600-line write streamed in 754 frames from 27,125 ms to 376 ms and its last frame from 67.7 ms to 0.38 ms.
- The per-agent run monitor (progress, abort and soft request-budget handling, usage totals and output capture) runs in task/run-monitor.ts, split out of task/executor.ts, with one handler per agent event; no user-visible change.
- The status line's message fingerprint and usage-window reading run in per-role and per-window helpers; no user-visible change.
- An agent's yield finalization, reminder ladder, session setup and teardown run in single-purpose helpers in task/executor.ts, and a child renders its own system prompt section once per run instead of on every system prompt rebuild, which saves 114.5 µs per rebuild for deep; no user-visible change.
- The session's agent event handler routes each event type to its own method, records a finished assistant message as the settle's last message once before its first await instead of again after persistence, and splits post-run maintenance into yield settle, failure recovery and stop-time continuation passes; no user-visible change.
- Text search resolves its scope, runs the native and virtual searches, pages the matches, and renders and fits its output in single-purpose functions; no user-visible change.
- Raw settings migrations run as an ordered list of one function per retired setting family instead of one 600-line function; no user-visible change.
- CLI startup resolves the launch model, prewalk and plan-yolo targets, settings overrides, the --fork/--resume session, the resume picker, extension flags, the initial prompt, the setup wizard, update notices and the mode handoff in single-purpose functions instead of functions of 272, 720, 229 and 102 lines; no user-visible change.
- A session's secret runtime (the loader, the expansion lease, the reload queue, the unreadable-vault refusal and the tool-argument expansion) runs in SessionSecretRuntime in secrets/session-runtime.ts instead of in closures inside createAgentSession; no user-visible change.
- Startup model and thinking-level selection (session restore, the settings default, deferred --model patterns, the fallback role chain, the first authenticated model and the metadata refresh) runs in StartupModelSelection in session/startup-model.ts instead of inline in createAgentSession; no user-visible change.
- The edit tool's patch mode computes a hunk's replacements (the direct match, trimmed-context variants, fuzzy fallback and overlap checks) and re-indents a hunk's new lines (tab-to-space, space-to-tab and matched-line alignment) in single-purpose functions instead of functions of 341 and 215 lines; no user-visible change.
- A session discovers its context files, workspace tree, repository context, skills, rules, watchdog files and advisor configs through one discoverProjectInputs path at startup and after a working-directory change, starts rules discovery with the other project scans instead of after the session context loads, and clears the 5-second workspace tree deadline timer when the scan finishes instead of leaving it armed.
- A session's tool session (the file mutation counters, the active-tool set, the host notifier and the advisor's view) is built in session/tool-session.ts instead of inline in createAgentSession; no user-visible change.
- A session's MCP startup (discovery, the deferred connect and the reactive tool, prompt and notification wiring) runs in session/factory-mcp.ts, and the project its system prompt renders, the re-discovery after a working-directory change and the appended-prompt assembly run in ProjectPromptInputs and helpers in session/prompt-inputs.ts, instead of inline in createAgentSession; no user-visible change.
- The debug tool's load check stops probing PATH at the first debug adapter whose command resolves instead of resolving all 14 configured adapters, which it ran for the session's tools and again for the advisor's, cutting a bare session's construction from 7.30 ms to 5.02 ms on a host with gdb installed.
- The advisor's toolset is built in session/tool-session.ts, and the context gauge a new session records at rest and the one the status row draws come from one measureContextGauge in config/compaction-strategy.ts, instead of a copy inline in createAgentSession; no user-visible change.
- A tool card builds its display when a frame draws it instead of on every change, so a resume stops highlighting each rebuilt call only to replace it with its result, which cuts rebuilding and drawing a 4,712-block transcript from 999 ms to 603 ms.
- The includeWorkspaceTree prompt gate states the project discovery scan that fixes it for the session instead of a read in sdk.ts that no longer exists; no user-visible change.
- The read tool dispatches a path through single-purpose steps (internal URLs, archive, SQLite and PDF image members, directories, images, notebooks, converted documents, binary files, summaries, multi-range and single-range text), and a range read collects, truncates, formats and annotates its window in helpers of their own; no user-visible change.
- A resumed read result builds its card text from its numbered rows when a card first draws it instead of when the session opens, a read group with previews off (the default) keeps no card text, and rewriting an unchanged resumed read writes back its stored tag without building the text, which cut the heap of a 38 MB, 1,280-read session after opening from 92.3 MiB to 67.6 MiB, its string cells from 915,658 to 106,754 and its open from 253 ms to 192 ms, its heap after the first frame from 171.1 MiB to 144.4 MiB, and its first whole-tail rewrite from 377 ms to 221 ms and 150.2 MiB to 120.5 MiB retained, with byte-identical file output.
- A session's custom tools and tool registry are built in session/factory-tools.ts, its extensions and custom commands load in session/startup-extensions.ts, its Codex prewarm and language-server warmup start in session/startup-background.ts, its argot arm and start records run in session/startup-records.ts, its owned background-job manager is built in session/async-jobs.ts, and the secret lease each in-flight request was admitted under is tracked in SecretRequestLeases in secrets/request-leases.ts, instead of inline in createAgentSession; no user-visible change.
- The transcript's frame walk places each block's rows, seals committed snapshots and carries the live-region boundary in single-purpose steps over one reused cursor, and a tool card's display resolves its policies in presentation/tool-execution.ts and its renderer views and generic fallback in presentation/tool-card-views.ts, which cuts a fully reused frame of a 20,000-block transcript from 0.407 ms to 0.306 ms and a committed-replay frame from 0.457 ms to 0.303 ms with identical rows.
- A settings-screen change applies its live effect through a table keyed by setting path in modes/terminal/controllers/setting-effects.ts instead of a 261-line switch, and the effects for names no setting has (autoCompact, theme, thinkingLevel and fourteen statusLine* spellings) and for settings with no screen row are gone; no user-visible change beyond the two fixes below.
- A URL read sends its alternate-markdown, .md suffix, content-negotiation, alternate-feed and llms.txt probes concurrently, keeps the highest-priority rendition that answers and aborts the rest, and downloads a document's bytes once for the converter and the binary notice, cutting an article read from 93.3 ms to 63.1 ms and a script-shell page read from 216.6 ms to 93.7 ms.
- The same-file reload comparison lives in session/provider-replay-projection.ts and compares each message's replayed fields without deep-copying them first, and skips a message both lists share, cutting it on a 433-message branch from 0.47 ms to 0.13 ms.
- A running veyyon process that has used under 5% CPU in each 5-second window for 30 seconds discards its compiled JavaScript code and returns free memory to the OS, cutting an interactive session's resident memory at idle from 361 MiB to 332 MiB after eight turns and from 325 MiB to 303 MiB after startup, at a cost of 37 ms on the next turn.
- An idle veyyon binary on Linux also unmaps the clean resident pages of its embedded modules when it discards its compiled code, cutting an interactive session's resident memory at idle from 317 MiB to 226 MiB after startup and from 342 MiB to 251 MiB after eight turns, with no measurable change to the next turn.
- The settings screen reads a record setting's override layers with isRecord from @veyyon/utils; no user-visible change.
- A process keeps the CPU model its system prompt shows without the /proc/cpuinfo text it was read from, releasing 61 KB of heap for the process life on a 32-thread host.
- A kept-alive spawned agent's session options and a parked agent's reviver hold only the run fields they read, and each progress snapshot copies its tail lines out of the streamed text, so a finished agent no longer keeps its run's assistant text, the spawning tool call's progress callback and abort signal, or the streamed message a tail line was cut from; after five turns spawning four agents each and the 20 agents parked, heap and extra memory is 61.2 MiB instead of 64.2 MiB and live objects 547,990 instead of 549,635 (median of three).
- The CPU model the system prompt shows is copied through detachedString from @veyyon/utils; no user-visible change.
- A session whose tools, commands and extensions are all first-party no longer evaluates Zod: the search tool declares its parameters with ArkType, which lists its type values as files, structure, text, and the command, custom-tool and extension loaders read api.zod from the package namespace only for author code, cutting the idle heap and extra memory of the compiled binary from 83.7 MiB to 81.9 MiB and live objects from 546,500 to 528,700 (median of five).
- Built-in tools build their ArkType parameter schemas on the first read of parameters instead of when their modules load, and session creation reads no inactive tool's parameters for discovery keys or prompt metadata, so a default session registers 834 schema nodes instead of 1,841 and the compiled binary's idle heap and extra memory drop from 75.9 MiB to 72.2 MiB, live objects from 496,323 to 457,494 and RSS after a full collection from 292 MiB to 289 MiB (median of five).
- The compiled binary renders the default system prompt from its precompiled statement templates with prompt.renderSequence instead of parsing and compiling their joined text at every session start, and evaluates no Handlebars compiler module, so buildSystemPrompt takes 3.0 ms instead of 6.0 ms and createAgentSession 44.4 ms instead of 47.6 ms (median of 13), and the idle heap and extra memory drop from 71.7 MiB to 70.1 MiB, live objects from 458,538 to 455,377 and RSS after a full collection from 288 MiB to 285 MiB (median of seven).
- The bundled rules are parsed once per process and every session's TTSR manager shares one compiled set of their conditions, scopes and globs, so loading and registering them takes 0.02 ms instead of 0.5 ms per session and a live spawned session retains 245.6 KiB of heap instead of 277.0 KiB and 1,723 objects instead of 2,606 (median of three runs over 20 sessions).
- ArkType schema nodes create their assert, pipe, rootApply and allows closures on first read instead of at construction, so a node traversed only as part of a larger schema retains half the function cells and the compiled binary's idle heap drops from 48.7 MiB to 47.8 MiB and live objects from 438,905 to 422,703 (median of seven).
- An ArkType schema node without metadata serializes itself once and holds that JSON object and string as both its inner and its full serialization, so the compiled binary's idle heap and extra memory drop by 271 KiB, from 68,550 KiB to 68,279 KiB, and live objects from 422,696 to 421,155 (median of five).
- A rebuilt transcript allocates no closure per assistant turn or tool card beyond the function a tool's view draws through: rail frames, framed blocks and cached views are class instances, cards, producers and tool-result images report through listener objects, inline images share one theme, and an assistant turn repaints through its host, so a resumed 600-turn session holds 120,613 KiB of heap and extra memory instead of 123,380 KiB, 639,759 live objects instead of 702,893 and 385 MiB RSS instead of 389 MiB (median of five).
- A tool card computes no edit preview once its call has a final result, and a transcript rebuilt from history sets each card's result before any preview starts, so resuming a 600-turn session whose 521 edits target files that still exist no longer reads and diffs each of them: the session settles in 625 ms instead of 1,773 ms with 1.50 s of CPU instead of 3.83 s (median of nine), and holds 116.5 MiB of heap and extra memory instead of 129.1 MiB and 383 MiB RSS after a full collection instead of 531 MiB (median of five).
- The at-rest reading a top-level session files for the next launch is measured by measureAtRestLaunch in session/startup-records.ts instead of inline in createAgentSession; no user-visible change.
- createAgentSession runs as the ordered steps of SessionStartup in sdk.ts, with the agent identity and provider prompt-cache key in session/startup-identity.ts, the per-request hooks in session/startup-request-hooks.ts, the credential-disabled relay in session/startup-credential-relay.ts, the tool-discovery mode in session/tool-discovery.ts, and the providers.openaiWebsockets reading in config/openai-websockets-mode.ts; no user-visible change.
- An MCP prompt command parses its key=value arguments and joins its reply's text through two helpers in session/factory-mcp.ts instead of inline; its output is unchanged.
- Codex web search folds its response stream into the answer, sources, model and usage through CodexAnswerCollector in tools/web/search/providers/codex-answer.ts, and maps a failed response through the shared provider HTTP error classifier; its results and errors are unchanged.
- The 98 bundled themes and the two root themes are embedded as files and the one a run asks for is read and parsed on demand, instead of every theme's JSON text held as a module string, so the binary shrinks by 240 KiB and an idle session holds 47.29 MiB of heap and extra memory instead of 47.76 MiB and 134.3 MiB of anonymous resident memory instead of 136.2 MiB (median of eight).
- A context file in which no @ import resolves is kept as the string discovery read instead of a copy rebuilt from its segments, so a session in a project whose AGENTS.md is 48,191 characters holds 94 KiB of whole-text copies of it instead of 282 KiB.
- An ArkType union in a jitless scope, as the CLI configures every scope, builds its discriminant, which only a compiled validator reads, on first read instead of at construction, and a union in any scope serializes it on first read, so a session start discriminates no union instead of 275, the compiled binary's warm:arktype startup phase takes 25.3 ms instead of 27.6 ms (median of 25), and an idle session holds 36.09 MiB of heap and extra memory instead of 36.38 MiB and 395,674 live objects instead of 398,201 (median of five).
- An interactive launch takes the session's at-rest context reading, which builds every active tool's schema, once the frame that draws the first keystroke in the composer is committed or when a prompt is submitted, instead of inside session creation, and the status row draws the resting gauge the last launch recorded until then, so a cold createAgentSession takes 104.5 ms instead of 144.2 ms (median of 16) and an idle session holds 41.37 MiB of heap instead of 44.76 MiB, 335,831 live objects instead of 370,870 and 123.8 MiB of anonymous resident memory instead of 133.3 MiB (median of five).
- ArkType builds each string.* keyword (string.email, string.url, string.date.iso and 50 more) when a schema first names it instead of when ArkType loads, so loading ArkType in the jitless CLI takes 11.5 ms instead of 21.3 ms and 2.0 MiB of heap instead of 3.9 MiB (median of 21), and an idle session holds 39.58 MiB of heap instead of 41.37 MiB, 314,847 live objects instead of 335,846 and 119.3 MiB of anonymous resident memory instead of 123.6 MiB (median of four).
- A resumed session at rest states its context gauge, and the /context and /compact figures of the slash popup, with the non-message size its last response recorded in place of a measurement, instead of building every active tool's schema to measure it, so resuming a 600-turn session draws the model in 221.6 ms instead of 246.2 ms and spends 1.14 s of CPU instead of 1.20 s (median of 11), and the session at rest holds 70.8 MiB of heap instead of 73.8 MiB, 521,322 live objects instead of 553,019 and 348 MiB of resident memory after collection instead of 384 MiB (median of five).
- The launch-facts record is defined in config/launch-facts.ts instead of modes/launch-facts.ts, so the session reads and writes it without importing the terminal modes; no user-visible change.
- The launch-facts record clamps its context percentages with clamp from @veyyon/utils instead of a local helper; no user-visible change.
- The in-flight prompt count and macOS power assertion, extension event forwarding, the title refresh on a re-plan, and working-directory re-scoping run in four session collaborators instead of as AgentSession members, which cuts the class from 265 private names to 186, so an idle session holds 10.67 MiB of private-name tables instead of 13.22 MiB and 96.3 MiB of anonymous resident memory instead of 102.0 MiB (mean of two).
- The edit tool's write path, with the LSP writethrough and the ACP filesystem bridge, is defined in edit/modes/patch-execute.ts, which the streaming edit preview does not import, so the launch module graph holds 21 fewer modules (301 KiB of source) and a one-turn -p run takes 287.2 ms instead of 293.0 ms with 229.2 MiB peak RSS instead of 231.3 MiB (median of 61).
- A tool execution block's input serialization is defined in presentation/tool-input.ts instead of presentation/tool-execution.ts; no user-visible change.
- A session takes the Anthropic fast-mode reset, the Claude device id, the Codex transport details and compaction context, and the Gemini thought-signature accounting from leaf modules of @veyyon/ai, and loads the Codex client inside the background websocket prewarm, so the Anthropic, Codex and Google provider clients load on a session's first request to them instead of at launch: the launch module graph holds 21 fewer modules (691 KiB of source), and an idle session holds 251,352 live objects instead of 254,549, 50.12 MiB of heap and extra memory instead of 50.48 MiB and 234 MiB RSS after collection instead of 237 MiB (median of seven).
- A turn reads the session's spend only while a goal is charged for it, and the spend tally reads each summarized assistant turn and task result back from the session file without keeping it in memory, so the first turn after resuming a 154 MiB, 53,819-entry synthetic session takes 250 ms of CPU instead of 490 ms and peaks at 354 MiB RSS instead of 458 MiB, and /session on it settles at 237 MiB RSS instead of 282 MiB (median of 3 alternating runs).
- A streamed chunk's frame sizes the home anchor's fills from the rows it keeps for the status rows, the composer and its footline instead of rendering them again, so the first streamed answer of a session (4,000 characters at 400 characters a second in 8-character deltas) takes 285 ms of main-thread CPU instead of 353 ms and 555 ms of process CPU instead of 680 ms (median of 6 alternating runs); a turn whose transcript already fills the screen is unchanged.
- A running session holds the text a read card, an eval cell output or a job result repeats from its tool result's content once, as a resumed session does, instead of a second copy in the result's details, so a 120-turn session that reads a distinct 8 KB file each turn holds 590 KiB of file text instead of 871 KiB and 27.45 MiB of JS heap after collection instead of 27.69 MiB (three runs each).
- A session checks whether a message is already on its branch against a sorted typed array of the branch's message timestamps, comparing persistence keys only for entries with the same timestamp, instead of a Set of every message's persistence key, so after two turns on a resumed 600-turn synthetic session the persistence collaborator retains 688 bytes of heap and a 43 KiB typed array instead of 516 KiB of heap, and a 5,000-message branch's index adds 125 KiB to the heap instead of 1,182 KiB.
- The profile, ~/.agent and extension-package providers load commands/, rules/, prompts/ and tools/ through one shared loader per layout, and an unreadable tools/ directory in the profile reports a discovery warning, as the other three layouts already did.
- An extension tool call resolves its approval policy, raises its approval card, sends tool_call and rewrites its result in four steps instead of one 350-line method; approval, vetting and results are unchanged.
- The secret obfuscator validates its registry, installs plain and regex entries, builds regex replacements and restores placeholders in single-purpose methods, and its message, content and provider-context walks share one identity-preserving map; no user-visible change.
- Structure search resolves its scope, merges overlapping targets, groups matches, renders each file, lays out the result and fits it to the inline budget in single-purpose functions instead of one 280-line closure; no user-visible change.
Fixed
- An agent with several approval cards open at once stays marked as waiting until its last card closes, instead of showing as working once its first card is answered, so the agent dashboard draws it as blocked and the runtime budget excludes the operator's reading time across the whole span, counted once.
- A non-recursive discovery scan reads only the files directly inside its directory, so a helper module under tools/<name>/ no longer registers as a custom tool named for its file, and a file below commands/, rules/, prompts/ or instructions/ (rules/archive/old.md) no longer loads as a command, rule, prompt or instruction.
- VEYYON_TIMING=x on an interactive launch stops the launch card's terminal before it prints, so the startup timing tree reaches stderr instead of only the log file, and the startup bench's ready:load and ready:boot arms report samples again.
- A tool card in a rebuilt transcript serializes its call's arguments through one accessor shared by every block instead of a getter allocated per block, so a drawn tool turn keeps one closure and two scopes instead of two and four (bash: 0.9 closures and 1.85 scopes per turn instead of 1.9 and 3.85).
- The terminal components barrel exports HOOK_EDITOR_TEXT_PAD_COLS, computeModalDims, sizingForArea and the other modal sizing names again, which it dropped when they moved to chrome/modal-geometry.ts.
- The compiled binary loads a legacy extension that imports @mariozechner/pi-coding-agent, @earendil-works/pi-tui, @veyyon/ai or another shimmed package root, or a pi- subpath such as @mariozechner/pi-ai/oauth, instead of failing it with no bundled module registered for @veyyon/pi-coding-agent or Cannot find package '@mariozechner/pi-ai'.
- The slash popup a launch's first / opens states /context and /compact without a context figure while the session is at rest, instead of measuring it and building every active tool's schema inside the keystroke, so the first / echoes in 5.5 ms instead of 27.2 ms and its popup settles in 38.1 ms instead of 59.5 ms (median of 11).
- A read card rebuilt on resume drops the file text it was built from once its card text exists, and a prune that replaces the read's result drops it too, so a resumed 600-turn session whose overflow prune superseded 398 reads holds 49.6 MiB of heap instead of 54.7 MiB and 244 MiB RSS after collection instead of 248-249 MiB (two runs each, 40-second settle).
- Dismissing the welcome card on a resumed session reports the card's rows as dropped from native scrollback instead of unmounting them unreported, so the first turn no longer replays the transcript: on a resumed 600-turn session its peak resident memory is 492 MiB instead of 527 MiB, its CPU time 0.48 s instead of 0.68 s, and its largest erasing frame 57 rows instead of 31,635 (median of five).
- Resuming a session or rebuilding the transcript puts its earlier prompts in the up/down history ring without writing them to the prompt history database again, so resuming a 600-turn session adds 0 rows to history.db instead of 600 (median of seven).
- A spawned agent no longer records its context gauge, model name and thinking level into the launch facts, so the next launch's card and status row state the top-level session's reading instead of the last spawned agent's, and a spawned agent no longer builds every tool's wire schema at creation to measure that gauge: the first one created in a fresh process takes 119 ms instead of 181 ms (median of five).
- A models.yml at a path other than the default one, rejected by a check after the schema (a missing apiKey, a retired key), names the file that was read in its error instead of the default models.yml path.
- A language server error reaches the lsp tool's result once as LSP error: <message> instead of LSP error: LSP error: <message>, and rename_file drops a server that answers workspace/willRenameFiles with the JSON-RPC MethodNotFound code whatever its message says, instead of listing it as a server note.
- A settings file with defaultThinkingLevel: off starts sessions with thinking off instead of at high.
- A retry.fallbackChains entry that is not a string is skipped with a config warning and the next entry is tried, instead of leaving the failed turn's prompt waiting forever on a retry that threw.
- A failure inside retry recovery, such as a locked auth database, a credential helper that exits with an error, or a full disk while the model switch is recorded, ends the retry with an auto_retry_end failure naming it, instead of leaving the prompt waiting forever.
- A usage-limit or rate-limit error whose message states a zero or elapsed retry window parks the credential for the computed backoff instead of for no time, and the session retry loop, the auth gateway and the advisor read a retry window from an error message through one parser.
- A same-file reload that changes a bash turn's kill signal or a user turn's synthetic, steering, attribution or demoted-reasoning marks now closes the live provider sessions, instead of continuing a provider chain whose history no longer matches the transcript.
- A models.yml compat block type-checks every key of the compat contracts, so a wrong value such as thinkingKeep: last fails validation naming the key instead of going to the provider verbatim; thinkingKeep, reasoningDisableMode, toolSchemaFlavor, the Anthropic flags and trustExplicitThinkingOnly are documented overrides.
- Disposing a session waits up to 1 second for its aborted agent loop to finish before it releases async jobs, eval kernels, browser tabs and the transcript, so a tool that ignores its abort signal no longer races the teardown of what it is using.
- A record-valued settings row (model roles, default effort, provider limits, agents, fallback chains, per-provider retry, tool approval) opens when a runtime override or --config file sets one of its entries; the overridden entries show their source and are read-only, and edits to the other entries write only the profile's values.
- The Default Model row states which layer supplies the active model at the start of its description, so the one-line footer no longer truncates it away.
- A tool card's header and section labels are clipped at the card's width instead of running past its edge, so an ssh host or a write path with no break in it no longer folds onto the next terminal line, and no card row exceeds a terminal narrower than the rail and its indent.
- An invalid enum value in config.yml falls back to the setting's default for every enum setting, not only edit.afterEdit.
- The pending previews of ssh, browser, read, debug, bash, github, lsp and 30 other tools update a string argument as it streams instead of when the JSON object closes, and the live preview matches the rebuilt transcript.
- The session /new starts while a response is still streaming, and every other top-level session in the process, runs background bash, the job tool and daemon exit watches on a job manager of its own, and a job reports to the conversation that started it or spawned the agent that started it instead of being refused.
- The Claude Code capture helper moved from src/cli/claude-trace-cli.ts to scripts/claude-trace-capture.ts beside bun run claude:trace, its only caller; it was never a veyyon subcommand and no longer ships in the package source.
- statusLine.segmentOptions.model.roomy: false joins the model and its thinking level with the dot separator in the composer footline instead of being overridden to the Model @level form.
- The plan review overlay repaints when a plan is swapped in or an external-editor annotation is committed, instead of waiting for the next keystroke.
- An agent spawned by another agent, and an agent revived from its transcript, reports its start, progress and finish on the top-level session's event bus, so the agent dashboard, the RPC agent stream and collab guests show agents at depth 2 and below instead of nothing.
- --profile <name> --resume <id> for a session another profile wrote forks it into <name> at its recorded working directory and leaves the source unchanged, instead of appending to the other profile's transcript under <name>'s settings and credentials.
- /resume <id> for a session another profile wrote relaunches Veyyon in that profile instead of opening the transcript under the running profile.
- veyyon --resume <id> naming a subagent transcript nested inside a session's directory activates the profile that wrote it instead of forking it into the profile the launch started in.
- veyyon --fork <id> for a session another profile wrote activates that profile and writes the fork under it, instead of copying the session into the profile the launch started in; --profile <name> --fork <id> still forks it into <name>.
- A named profile with no hindsight.bankId set retains into and recalls from the veyyon-<profile> Hindsight bank instead of the veyyon bank every profile shared; set hindsight.bankId: veyyon in that profile to keep reading memories it retained before.
- With sessions stored under $XDG_DATA_HOME/veyyon, veyyon --resume <id> activates the profile that wrote the session, and the session picker's all-projects view and an ACP client's session list show the active profile's sessions from every project, instead of none of them.
- The session picker's all-projects view and an ACP client's session list no longer show each spawned agent's transcript as a session of its own.
- /profile <name>, /resume of another profile's session, and veyyon --profile <name> run from a tool's shell start the other profile on its own .env files instead of the credentials the running profile loaded from its own.
- An Exa API key read out of a profile's mcp.json no longer reaches a process started under another profile, which resolves EXA_API_KEY from its own configuration instead.
- On Windows, launch in a project where another profile's daemon broker is running starts this profile's own broker instead of connecting to the other profile's pipe and failing with Daemon broker authentication failed, and a session under VEYYON_CODING_AGENT_DIR no longer shares the default profile's broker and its environment.
- Switching a running session to a transcript another profile wrote, through an extension or RPC switch_session, fails with an error naming the owning profile instead of continuing that transcript under the running profile.
- An unknown-flag error suggests a short flag in the spelling the parser accepts, such as -r for -rr, instead of --r, which the parser rejects, and offers -h and -v like every other flag.
- A URL read whose image bytes do not decode returns the text page the server sent, or a note that the bytes were not a valid image, instead of failing the read.
- A URL read of a page whose alternate <link> href is not a valid URL skips that link and reads the page instead of failing.
- A compaction that writes the same summary as an earlier one, such as a second server-side compaction with its empty summary, stamps its dead-end warning on the new entry and passes the new entry to session_compact handlers instead of the earlier one.
- With edit.streamingAbort on, a streaming edit patch whose last removed line has no trailing newline stops the turn when that line is absent from the file, instead of leaving it for the edit tool to reject after the stream ends.
- Disposing a session while a Python or eval run is in flight no longer keeps the process alive for up to 3 seconds after the run finishes.
- The CLI imports the terminal output guard when a worker thread starts rather than at startup, keeping it off the static boot graph; no user-visible change.
- A tool card whose call carries an argument of the wrong type, such as input: 404 for search, draws the value as text or omits it instead of failing with Renderer failed: e.toWellFormed is not a function.
- The read card for a structurally summarized file numbers each row with the line the model saw, a merged brace pair with its opening line, instead of counting up from line 1 past every elided body, and draws the … elision row and the summary budget notice without a line number.
- The streaming-reveal throughput bench builds its target as a transcript view instead of a raw assistant message, so bun packages/coding-agent/bench/streaming-throughput.bench.ts runs again; no user-visible change.
- An agent's live preview shows the shorthand the stream was still holding when a streamed message ends without a final content snapshot, instead of dropping it.
- veyyon --resume <id> for a session from another project reopens that session in place and moves the launch into its recorded working directory, instead of prompting to fork it into the launch directory or failing without a terminal; an explicit --cwd moves the session's working directory there instead.
- veyyon --resume <id> runs under the profile whose sessions directory holds the session, instead of the profile the launch started in; an explicit --profile takes precedence.
- The advisor's tools read the primary tool session live, so an advisor ask delivers the host notification, an advisor task label rides the session's side transport, and an advisor spawn after a working-directory change inherits the new context files, workspace tree, skills and rules instead of the launch ones.
- Clearing the transcript for a rebuild (resume switch, /shake, compaction, a display setting) releases the discarded blocks and their rendered rows before the new blocks are built instead of holding both trees until the next frame, cutting the retained heap at a rebuild of a 13,470-entry transcript from 264.7 MiB to 157.0 MiB.
- Turning Settings → Providers → Import Other Tools' Config on or off applies to the next discovery load in the running session and leaves disabledProviders unchanged, instead of adding or removing a provider named importForeignConfig there and taking effect only in the next session.
- Turning Settings → Appearance → Show Inline Images on or off redraws the transcript under the new value, instead of leaving the images on cards already drawn.
- Closing /settings, flipping an appearance toggle there or hovering a status line preset keeps the configured statusLine.segmentOptions on the status line, instead of resetting them to the preset's until the next unrelated resync.
- @veyyon/coding-agent/export/html exports buildSessionData and collectSubSessions again, which moving them to export/session-data had dropped from that entry point.
- A resumed session at rest drops the committed transcript blocks above the retained screen from the terminal frame, including blocks that report a version and blocks a frame carries without drawing, keeps one block more instead of dropping none when the commit lands on a block boundary, and releases the rows each dropped block memoized, so a resumed 600-turn session at rest holds 87.9 MiB of heap and 746,900 objects instead of 105.5 MiB and 962,500 (median of three).
- A session replace or display reset that lands between the paint that committed transcript rows and the next frame replays the whole transcript, instead of dropping the committed rows from the replayed frame and leaving 35 of 600 rows in the terminal's scrollback or 21 of 699 on the alternate-screen scroll tape.
- The raw SSE capture that /debug reads releases each frame record as it evicts it instead of holding it until the window compacts, so the capture stays within its 512,000-character budget and a 60-turn session holds 883 content frames (130.0 KiB) instead of 1,407 (207.2 KiB).
- An action on an element named by a tab.observe() id or a tab.ariaSnapshot() ref lands once on the element that replaced it after the page redrew, when that element is the only one with the node's role and accessible name, instead of failing with "Node is detached from document"; a click, hover or tap reads the element's point and dispatches in one round trip, so it also lands on a page that redraws every animation frame, and an element the role and name cannot single out fails naming the id or ref as stale.
- tab.screenshot() sends a second capture on a CDP session of its own when the first has not answered within 1 second, up to three, so a capture sent while the page commits a navigation no longer holds until the protocol timeout or holds later screenshots and the tab's close behind it.
- A tab.goto() aborted with net::ERR_ABORTED by a navigation the page started, such as a click handler's redirect, is sent once more and resolves on its own URL; a goto aborted twice fails naming the navigation that aborted it last.
Removed
- The unread sessionAccent field is gone from the status line's settings object; statusLine.sessionAccent still colors the editor border.
- The unused built-in designer model role is gone from Settings → Model → Roles; a modelRoles.designer value you already set still resolves through @designer as a custom role.
- The retired statusLine.separator and statusLine.transparent settings are gone; neither changed anything on screen, and a config file that still sets them loads unchanged.
- 36 exported functions and constants that nothing called, and the unused Smithery Connect API client mcp/smithery-connect.ts, are gone; no user-visible change.