Unreleased
next release
Changed
- Spawned agents have three states, running, idle and asleep: an idle agent falls asleep after 5 minutes (agent.idleTtlMs, "Sleep Idle Agents After") and frees its session, and any agent with a transcript, including a stopped, failed or killed one, wakes when messaged for the rest of the session instead of being pruned or left unrevivable; an agent that ran in an isolated worktree stays readable at history://<id> but cannot be woken, since the worktree is removed when its run ends.
- job cancel on an agent and x in the agent dashboard stop the agent and everything it spawned and put them to sleep, so a stopped agent wakes when messaged instead of being terminated.
- The irc roster lists the 10 most recently active asleep agents and a count of older ones, which still wake by id, and a blocking irc send to a peer that falls asleep before replying ends at once instead of waiting for its timeout.
- agent.softRequestBudget defaults to Unlimited instead of 200 requests, offers 200, 500, 1000 and 2000, and the bundled scout and sonic agents no longer carry a lower built-in limit.
- Collab hosts and guests speak protocol 5, which reports closed agents as asleep; a peer on protocol 4 is rejected with the protocol-mismatch error.
- A text search result keeps only the card rows its card can draw and a count of the matches past them, so a 30-turn session with broad searches writes 4.1 MB to its session file instead of 7.2 MB and 21 MB to disk instead of 31 MB, with the card drawn as before.
- A session file records a text search's kept card rows as a length of the rows its result redraws, so a resumed card draws the rows it was written with whatever the card's row limits become, and the search codec no longer loads the card limits at launch.
- A live tool or assistant block's rows enter terminal scrollback once, when final, instead of as copies taken while they scroll off mid-run, so a 20-turn session with parallel tool calls writes 1.0 MB to the terminal instead of 4.1 MB with no whole-transcript replays, and a running tool's rows above the screen reach scrollback when it finishes.
- The working line and live tool-card spinners advance every 200 ms instead of every 80 ms and repainted rows no longer send their blank padding, so a session waiting 30 s on a model and a tool writes 94 KB in 327 frames to the terminal instead of 197 KB in 548.
- No user-visible change: /btw and /omfg show, replace, dismiss and fail a request through one SidePanelSlot in modes/terminal/controllers/side-panel-slot.ts instead of two copies, and a /btw answer is held on its request instead of in controller fields cleared on close.
- No user-visible change: the collab host and guest connect through RelayClient from @veyyon/wire/relay-client instead of a private copy of the relay client, and collab/crypto no longer wraps sealFrame and openFrame.
- No user-visible change: the memory tools resolve their backend through memory-session, the JS eval host and worker encode errors through one eval/js/error-payload codec, and config get, set and reset report a missing or unknown key through one helper.
- No user-visible change: the Brave, Firecrawl, Jina, Synthetic, Tavily, TinyFish and Z.AI web search providers no longer export an unused findApiKey.
- No user-visible change: an MCP OAuth refresh and the credential it stores read the token endpoint, client and resource from one resolveMcpOAuthRefreshTarget.
- No user-visible change: prompt templates, file slash commands and the legacy loader's extra templates derive a missing description from one firstLineDescription.
- No user-visible change: the oauth hooks of an extension ProviderConfig and of ProviderConfigInput are one ProviderOAuthConfig type.
- Tool discovery and the system prompt select MCP tools by name before describing them, and tool examples render without converting each wire schema again, so a print-mode turn reaches its first request in 55.4 ms instead of 57.4 ms and exits in 76.3 ms instead of 80.6 ms.
- A launch of the installed binary after its first reads the bundled model a custom model id resolves to from cache/model-references.json instead of indexing the catalog's model ids, so a print-mode turn with a custom model reaches its first request in 55.9 ms instead of 59.9 ms and exits in 76.9 ms instead of 81.4 ms.
- The compiled binary carries the digest of the model catalog it embeds instead of reading and hashing the 2.3 MB catalog on every launch, so a print-mode turn reaches its first request in 54.9 ms instead of 57.9 ms and exits in 73.9 ms instead of 77.8 ms.
- A launch whose extension, command, rule, prompt, tool and hook directories are missing or empty no longer loads the native addon to scan them, so a print-mode turn reaches its first request in 55.9 ms instead of 60.9 ms and exits in 75.0 ms instead of 79.6 ms.
- A launch with arguments no longer constructs the stdin stream to describe why it skipped the first-frame replay when VEYYON_REPLAY_DEBUG is unset, so a print-mode turn exits in 79.3 ms instead of 81.8 ms.
- A launch of the installed binary after its first sends the built-in tools' stored wire schemas instead of building every tool schema, so a print-mode turn reaches its first request in 61 ms instead of 86 ms and exits in 82 ms instead of 116 ms.
- The first lsp query on a Rust file starts when rust-analyzer reports experimental/serverStatus with quiescent: true instead of after polling rust-analyzer/analyzerStatus for at least 2 s, so a cold definition on a small crate returns in 1.46 s instead of 2.16 s and on a crate with dependencies in 1.88 s instead of 2.42 s.
- No user-visible change: an abort inside the rust-analyzer workspace wait returns from waitForProjectLoaded, as it does for every other server, instead of throwing when it lands between two analyzerStatus polls.
- No user-visible change: the collab guest, eval executor, markit registry, launch client, MCP stdio transport, RPC prompt result, extension send, TTS client, IRC tool and memory tools coerce a thrown value with toError from @veyyon/utils instead of nineteen inline copies.
- Diagnostics after an edit or write are reported when the language server publishes them instead of at the next 100ms poll, so a server that answers in 50ms reports in 51ms instead of 101ms, or in 301ms instead of 402ms when it does not echo the document version.
- No user-visible change: ZIP central-directory reads, config-file read failures, LSP severity counts, patch line-hint matching, SQLite row updates, GitHub issue and pull request body sections, eval import rewriting, plugin enable and disable, QR penalty scoring and autoresearch loop entry each run through one helper instead of two or three copies.
- A modal card keeps its padding from 29 rows for large cards and 27 rows for medium and settings cards, instead of 39, 33 and 31, so cards on terminals 27 to 38 rows tall show up to four more list rows.
- No user-visible change: the lsp diagnostics action and the diagnostics reported after a write merge server reports through one uniqueSortedDiagnostics in lsp/utils.ts instead of two copies.
- No user-visible change: the /debug performance, dump and memory reports write their bundle through one sequence in debug/index.ts instead of three copies.
- No user-visible change: the agents and import setup steps take the checklist sentinel, key hints and space-to-toggle input from setup-wizard/scenes/wizard-list.ts instead of two copies.
- No user-visible change: the prompt history search overlay highlights the tokens historyQueryTokens in @veyyon/kernel/session/history-storage matched instead of its own copy of the tokenizer.
- No user-visible change: the session-compaction-stall and session-tree-nav benches build their usage records from emptyUsage() instead of restating its zero fields.
- No user-visible change: the LSP writethrough bench's header describes the event-driven diagnostics wait instead of the 100ms poll it replaced.
- No user-visible change: each MCP HTTP request builds its headers through one transport helper instead of five inline copies, and the HTTP transport, the legacy SSE transport and callMCP read the JSON-RPC POST headers from one constant in mcp/json-rpc.ts.
- No user-visible change: the selectors, dialogs, dashboards and the Ctrl+P model cycle step through their rows, tabs and models with wrapIndex instead of hand-written wrap arithmetic.
- No user-visible change: the pointer-band selectors and the MCP add wizard repaint on the result of HoverController.set instead of comparing the hovered row themselves.
- No user-visible change: the emoji, internal-URL, GitHub-reference and prompt-action completions return AppliedCompletion instead of restating its shape.
- No user-visible change: the status producer falls back to the kernel's emptyUsageStatistics() from session/session-entries.ts instead of restating the zero usage record, so the status row evaluates no session-context or compaction module.
- No user-visible change: the Antigravity, xAI, OpenRouter, Gemini and OpenAI image providers read a refused request's message through one providerErrorMessage in tools/web/image-gen.ts instead of five copies, and the GitHub run watch and run result reports write their header lines through one pushRunHeader.
- No user-visible change: the assistant message component takes its ImageContent type from the @veyyon/wire presentation contract instead of @veyyon/model, which the package does not declare.
- The composer reads the draft's queue shorthand and magic keywords once per frame instead of once per drawn row, so a keystroke and its frame in a 400-line draft take about 20 µs instead of about 85 µs.
- No user-visible change: a modal card is laid out and its chrome planned once per frame, and the body is painted against that plan (layoutModalCard, planModalChrome, renderModalShell).
- A start with no usable credential reports the missing credential without building the whole model catalog, so it reaches the prompt in 72 ms instead of 112 ms, and the message counts providers instead of models.
- No user-visible change: compaction model keys and ACP model option values come from formatModelString instead of two private copies of it.
- The Cursor exec bridge and the commit agent preview read message text through the shared contentText owner, and the commit map phase reads observations through the commit analysis reader instead of a second copy; no user-facing change.
- Transcript, custom-display and rewind-report text now flatten message content through the shared contentText owner instead of two local copies; no user-facing change.
- Hooks and extensions now share one definition of the tool_result event types, so a hook's ToolResultEvent.isError is typed boolean, matching what the extension runner delivers.
- The bash and eval blocks run from the composer draw through one execution block that folds streamed chunks at the next draw and builds its header once, so an eval block drawn after each of 5000 streamed chunks takes 47 ms instead of 575 ms.
- The tools.format setting takes its dialect list from a table keyed by the catalog's Dialect type instead of a second copy of the catalog's list; no user-facing change.
- The markit converters share one stream-acceptance rule, one XML parser with the 1,000,000 entity-expansion cap, and one repeated-element reader; no user-facing change.
- Session memory banks and memory stats readers open their Mnemopi store through one builder, createBankMemory; no user-facing change.
- AgentParsingError extends SourcedParseError from @veyyon/utils instead of copying its formatting; no user-facing change.
- The session's terminal-yield tracking and the subprocess executor decide whether a yield ends a run through one predicate; no user-facing change.
- local:// and memory:// confine a resolved path to their root through one shared real-path check; no user-facing change.
- The writethrough per-server loop drops a custom-linter check its callers already make; no user-facing change.
- The RPC client and the agent run monitor recognize agent events through AGENT_EVENT_TYPES instead of their own copies of the list; no user-facing change.
- Syncing a written file to its language servers and announcing its save share one per-server loop; no user-facing change.
- The plugin manager runs its bun subcommands through the command runner the git cache refresh uses; no user-facing change.
- veyyon stats --summary prints the summary and draws the sync progress line through @veyyon/stats, which the standalone stats entry point also uses; no user-facing change.
- The collab wire and the HTML export build a tool call's display and a tool result's display through the same two functions instead of a copy each; no user-facing change.
- The hook editor, hook input and MCP add wizard cards pass their whole body to the modal shell, which keeps the rows that fit, instead of planning the card chrome a second time to cut it first; no user-facing change.
- An MCP tool with a live connection and one waiting for its server send a call, resend it after a reconnect and build its result through the same functions, and a waiting tool's failure names the source of the connection it failed on as its success does; no user-facing change.
- The reftable HEAD lookup, the LSP document open and the eval kernel's result each have one implementation shared by their sync and async or success and cancellation paths; no user-facing change.
- The debug adapter client starts every adapter, and kills one that never connects, through one helper; the GitHub issue, PR and diff lookups return the cache's own lookup result type; and the custom tool loader declares its pending-action shape once; no user-facing change.
- check:types covers bench/, and the code-review-reminder, rendering, session-compaction-stall and session-tree-nav benches compile and run against the current APIs; no user-facing change.
- The streamed tool-argument preview copies each run of plain characters in a string argument at once instead of one character at a time, which cut the one-shot decode of a 160 KB write call from 1.75 ms to 0.68 ms and its live reveal in 100-character steps from 10.5 ms to 9.2 ms (median).
- A -p or --mode json run starts background model discovery once its first response starts streaming instead of when its session is created, which sends the first request about 33 ms sooner and cut a run against a provider that answers in 300 ms and streams for 150 ms from 580.8 ms to 560.0 ms (median of 15).
- A launch with no llama.cpp server running no longer rewrites the llama.cpp model-cache row and syncs models.db four times, which cut a print run against a provider that answers in 300 ms and streams for 150 ms from 558.3 ms to 553.9 ms (median of 15).
- read and the CLI @ attachment import the entry-kind phrase from @veyyon/utils/fs-error, which adds no module to any import graph; no user-facing change.
- The config-root, global-config and path-comparison helpers are imported from the @veyyon/utils modules split out of @veyyon/utils/dirs; no user-facing change.
- The provider-refusal check and the Mnemopi per-bank target dedupe each have one definition; no user-facing change.
- /todo with an unknown verb in the TUI lists the accepted verbs, the message ACP shows, instead of the full usage.
- veyyon ttsr scan compiles rule conditions, globs and scopes with the functions a session uses instead of a copy of them; no user-facing change.
- The secret vault and its key file remove a stale entry through one quarantine-and-recheck helper instead of two copies of it; no user-facing change.
- The Mnemopi stats command resolves a bank's database path through the function a session uses instead of a copy of it; no user-facing change.
- The terminal reports a /browser restart that fails after the mode is saved as Browser mode set to <mode>, but restart failed: …, the sentence ACP prints, and shows the /browser usage line as a warning.
- /effort checks a level through one function the terminal and the text surfaces share, with no change to what either accepts or prints.
- No user-visible change: /export path resolution moved from the session runtime into the HTML exporter.
- IRC message delivery moved out of AgentSession into the IrcDelivery session collaborator, with no change to how a message is steered, queued, woken on or auto-replied to.
- The first shell command of a session no longer compiles the output-stage regular expressions of every built-in output filter; each filter compiles them the first time it applies, so a first ls runs in 5 ms instead of 12 ms.
- The session_stop hook pass moved out of AgentSession into the SessionStopHook session collaborator, with no change to the event a handler receives, the continuation it queues or the cap that ends a chain.
- The hidden next-turn message queue moved out of AgentSession into the NextTurnMessages session collaborator, with no change to when a queued message reaches a turn or the order it arrives in.
- Model switching moved out of AgentSession into the ModelSelection session collaborator, with no change to what a saved switch, a session-only switch or the Ctrl+P cycle records, persists or pins.
- Esc repaints a frame sooner when it arms no double-press gesture: closing a selector takes 4 ms instead of 35 ms with the kitty keyboard protocol and 54 ms instead of 86 ms without.
- The /model picker resolves the default model against the models it lists, as startup does, so its first open paints in 4 ms instead of 41 ms and the default mark lands on the model a new session starts on.
- Views that load after the key that opens them, such as the Extension Control Center, paint up to 30 ms sooner.
- /cpu-limit status reports the kill policy in force: kill on sustained saturation for the CPU budget and kill when exceeded for the write budget.
- The tool argument repair registry lists path resolution in the file tools among the tool-local parsing it excludes; no user-facing change.
- Background job delivery, listing, cancellation and the settle wake moved out of the session class into one session collaborator (#892); no user-facing change.
- Releasing what a conversation ends with on /new, /resume and a handoff moved out of the session class into one session collaborator (#892); no user-facing change.
- Routing a finished run through retry, compaction and the stop-time passes moved out of the session class into one session collaborator (#892); no user-facing change.
- The message a prompt raises when a provider's credential produced no token is built in the credential-message module beside the other missing-credential messages (#892); no user-facing change.
- Queueing steers and follow-ups, the idle drain that starts a turn on them, and the queue count, chips, clear and pop moved out of the session class into one session collaborator (#892); no user-facing change.
- Running a shell command or Python cell typed with ! or $ moved out of the session class into the session collaborator that tracks those runs (#892); no user-facing change.
- The context threshold checks before a prompt and between the provider calls of a tool loop, and the switch to a larger-context model they share, moved out of the session class into the TurnContextCheck session collaborator (#892); no user-facing change.
- Starting a new transcript with /new or /drop and forking one with /fork moved out of the session class into the SessionSwitch session collaborator (#892); no user-facing change.
- The session-state block and the eager delegate reminders a turn carries ahead of its message moved out of the session class into the TurnPreludes session collaborator (#892); no user-facing change.
- The per-family service tiers, /fast and the tier each request resolves to moved out of the session class into the ServiceTiers session collaborator (#892); no user-facing change.
- The command registries, the resolution of a typed prompt through extension, custom and file-based commands and the prompt templates, and the agents a / command grants for its turn moved out of the session class into the PromptCommands session collaborator (#892); no user-facing change.
- The display copy of assistant content, tool intents, streamed deltas and rebuilt transcripts, with the stream decoder a streamed reply runs through, moved out of the session class into the DisplayCopy session collaborator (#892); no user-facing change.
- The normalization of a message's images for the active model, the description a text-only model reads in their place and the latest images a tool addresses moved out of the session class into the ImageAttachments session collaborator (#892); no user-facing change.
- The response and raw SSE interceptors every request runs with, the raw SSE capture, the side stream function and the preparation of a side request's options moved out of the session class into the ProviderTransport session collaborator (#892); no user-facing change.
- The redacted tool start markers, the session_exit entry and the process teardown callback that writes it when no dispose runs moved out of the session class into the ExitRecord session collaborator (#892); no user-facing change.
- The event subscribers, the extension event forwarder and the wire-level agent_end held while a prompt unwinds moved out of the session class into the SessionEvents session collaborator (#892); no user-facing change.
- The scheduled agent.continue(), the auto-continue prompt a compaction resumes with, their cancel and the recovery wait a prompt runs moved out of the session class into the TurnContinuations session collaborator (#892); no user-facing change.
- The HTML export, the /copy texts, the /dump text and its request snapshot moved out of the session class into the TranscriptExports session collaborator (#892); no user-facing change.
- The mode blocks a prompt restates (plan reference, plan-mode rules, goal, vibe mode), the reads of the files it mentions as @path and the before_agent_start extension input moved out of the session class into the TurnInput session collaborator (#892); no user-facing change.
- The release at dispose of the session's background jobs, owned job manager, eval kernels, session-scoped resources and MCP hold moved out of the session class into the SessionRelease session collaborator, and the memory backends' release into MemoryContext (#892); no user-facing change.
- How a custom message reaches the conversation, queued, held for the next turn, run as a turn or appended, and the custom-message prompt with its keyword notices moved out of the session class into the CustomMessages session collaborator (#892); no user-facing change.
- Doc comments left stranded above another declaration's doc comment moved onto the declaration they describe, and the stale duplicates beside a rewritten doc were deleted; no user-facing change.
- An extension command run on a session with no UI receives the extension runner's no-op UI context instead of a second copy of it; no user-facing change.
- The theme loader and the status-rail animation read 256-color palette indices through ansi256ToHex in @veyyon/utils/color instead of two copies of the xterm table; no user-facing change.
- The hook selector behind ask questions and extension ui.select counts the rows its options take once per terminal width instead of on every key, so an arrow key on an 80-option list takes 28 µs instead of 286 µs at 80x10 and 59 µs instead of 341 µs at 80x30.
- MCP initial connect and reconnect open and wire a server connection through one pair of manager methods, ACP reads the last assistant message through findLastAssistantMessage, vault extend, replaceValue and rename share one narrowest-first edit walk, the conflict warning and the :conflicts index print their scan note and side labels through one function, and the three JS eval worker handles share one close handshake; no user-facing change.
- The transcript builder and the presentation event bridge state a tool call's arguments through the tool block's own serializer instead of a second copy of it; no user-facing change.
- The Streamable HTTP and legacy HTTP+SSE MCP transports send every JSON-RPC message through one postJsonRpc with one auth-retry path, and a refreshed credential replaces the transport's headers instead of writing into the configuration object it was created with.
- The stdio, Streamable HTTP and legacy HTTP+SSE MCP transports classify a server's messages through one routeIncomingMessage, and the two HTTP transports POST the answer to a server request through one deliverServerResponse.
- The web_search tool guidance treats results that name versions, models or products newer than the model's training as current, confirms one with at most two targeted follow-up searches, and prefers corroborated results over prior knowledge (#882).
- No user-visible change: --mode json states why it dropped a line through errorMessage from @veyyon/utils instead of an inline copy.
- No user-visible change: a spawned agent's model list is split by the same normalizeModelPatternList the settings layers use instead of a second copy in the executor.
- No user-visible change: --models scoping and enabledModels filtering detect a glob selector through one predicate instead of two copies.
Fixed
- A model with no known context window no longer reports that the session compacts at 1 token; threshold compaction does not run for such a model and the compaction-threshold notice stays silent.
- A model selector with an effort suffix, such as kimi-code/k3.1-pro:high for an agent, resolves to the model it names at that effort instead of a sibling whose id contains the suffix's letters, such as kimi-code/k3.1-pro-highspeed; an unknown suffix such as :hgh is dropped with a warning instead of picking such a sibling.
- A spawned agent whose first configured model has no working credentials runs the next configured model that has them instead of falling back to the parent session's model, and a keyless local parent model (Ollama, llama.cpp, LM Studio) is accepted as the fallback.
- A model list that resolves after extensions load, such as an agent or --model naming an extension provider's model, starts on the first listed model whose provider has working credentials instead of the first listed model or the parent session's model, and accepts a keyless parent model as the fallback.
- inspect_image with no agent.helpers.vision.model set skips a text-only default or active model and reads the image with an image-capable one, the same order attached-image descriptions use, instead of failing with "does not support image input".
- inspect_image and the description a text-only session reads for an attached image run at the Vision helper's effort, or a :level on its model, else the session's effort, instead of the provider's default.
- Eval completion() with no model or with "default" runs at the Eval Completion helper's effort, else the session's, instead of the provider's default; a :off suffix disables reasoning, and an effort the model lacks is fitted down to its nearest offered effort instead of up to its highest.
- /guided-goal fits its effort to the efforts the plan model offers instead of sending one the model lacks.
- Session titles, agent labels, unexpected-stop checks, speech rewrites and memory passes run at the effort picked with their helper's model, else the session's effort, instead of with reasoning off or at a fixed low or medium effort; auto-thinking classification runs at its helper's effort, else with reasoning off, and veyyon commit honors a :off on its helper's model.
- An idle agent with a running background job, its own or one an agent it spawned started, stays awake instead of falling asleep on the idle timeout and cancelling the job.
- job cancel on an asleep agent stops the agents still running under it instead of reporting the agent already asleep.
- A background agent that ran in an isolated worktree reports that messages cannot reach it, and a stopped isolated agent's result no longer says to message it via irc, since its worktree is removed when the run ends.
- A stopped background agent's result offers to message it via irc once instead of twice.
- The /agents stop confirmation states that the agents a stopped agent spawned also fall asleep, and the job agent listing says cancel stops an agent, which then wakes when messaged, instead of saying it kills it.
- A message sent while an earlier one is still preparing its images no longer reaches the model ahead of it: a prompt claims the turn before preparing its images, and steers, follow-ups and extension messages are delivered in the order they were sent.
- --mode json prints every session event instead of silently dropping assistant tool-call messages, turn_end, agent_end and a persisted session's read results, and reports a line it cannot redact on stderr.
- A collab host with share.redactSecrets on and a configured secret delivers live entries and events that hold provider streaming state or a read's card to guests instead of failing to send them.
- An eval cell that throws, exits non-zero or times out is reported to the agent loop as a failed tool call, as a failed bash command is, instead of as a success whose text states the exit code.
- An active goal keeps driving after a turn that ends in prose without a tool call, including the reply to the /goal objective, and stops with a warning only after 3 such turns in a row instead of stopping silently after one.
- The goal continuation after a turn that ended without a tool call states that the turn did no work and directs the model to start an untried approach, so a model that answers with a status report returns to work.
- A goal continuation held by a draft or attached images in the composer is sent once the draft is deleted without being sent, instead of leaving the goal idle until the next message.
- Every process a session spawns (bash and PTY commands, eval kernels, launch processes, MCP and language servers, the managed browser) starts at nice 10 (BELOW_NORMAL_PRIORITY_CLASS on Windows), so a parallel build or media job no longer stalls the terminal and desktop.
- The advisor no longer receives a configured secret written in the primary model's thinking; thinking blocks pass through the secret obfuscator like text and tool arguments.
- A jj command ends after 5 minutes with exit code 124 and keeps at most 8 MiB of output, as git and gh do, and truncated gh output is labeled gh instead of git.
- veyyon plugin uninstall --json prints a JSON record for a marketplace plugin instead of the human success line; uninstall, enable and disable now share one loop that reads --json on every install route.
- Both MCP HTTP transports report a reply to a server request that the server answers with an HTTP error status, instead of dropping it.
- A Streamable HTTP MCP notification refreshes a rejected credential and retries once, as a request does, and a failed MCP transport POST or legacy SSE connect includes the server's WWW-Authenticate and Mcp-Auth-Server challenge.
- An MCP server's error that names no request ("id": null, such as a parse error) fails a Streamable HTTP request with the server's code and message instead of reporting that the server never answered, and on the Streamable HTTP GET stream it reaches the client's error handler instead of being dropped.
- A Streamable HTTP or legacy HTTP+SSE MCP server message that carries a method and a null id is delivered as a notification, as over stdio, instead of being dropped.
- An MCP server frame that is not a JSON-RPC message is logged as an ignored message on every transport, where an object with neither a method nor an id was dropped without a log line, and over Streamable HTTP a frame that is not an object failed the request it arrived on.
- A tool call aborted inside a JS eval cell rejects with ToolAbortError in the worker, the same error the host raises, instead of a plain Error carrying the abort's name.
- RpcClient.login is defined again; an unterminated doc comment above it had turned the method into a comment, so calling it threw client.login is not a function.
- A binary update whose download fails before the disk has created the partial file no longer leaves a veyyon.<id>.new beside the installed binary; the removal ran before the file's open landed, and the open then created it.
- An eval kernel whose runner exits during startup reports kernel exited with code N instead of EPIPE: broken pipe, write when the init request is written after the runner closed its stdin.
- A JS eval cell that displays or returns a value holding a cycle shows it with "[Circular]" where it loops back, in 41 ms instead of 915 ms, and a value holding a function or symbol shows "[Function: run]" or "Symbol(marker)" in place; both printed [object Object].
- A tool block whose arguments hold a cycle states them as [unserializable] in 2 µs instead of about 1 s, and arguments carrying code or prose serialize faster, 16.5 µs instead of 28.0 µs for a 50 KB write.
- The lsp diagnostics action and the diagnostics after a write name a language server or linter that failed, with its error, instead of reporting the file clean; a file no server checked is reported as not checked.
- The first lsp diagnostics call or rename_file against a project-aware language server returns in about a second instead of 15 seconds: the file opens before the project-load wait, and a server that reports no load progress is waited on only until it publishes or 2 seconds after the open.
- rename_file on a fresh TypeScript server proposes the import edits of every file importing the renamed one; it opened no file first, so tsserver loaded no project and proposed none.
- A write reports the diagnostics its language server published for the written text; a publish that arrived before the write's file-change notice was dropped, so the write waited out its diagnostics budget and reported the file clean.
- A language server that exits during startup is reported with its exit code and stderr, such as pyright: LSP server exited (code 1): ModuleNotFoundError: No module named 'pyright', instead of LSP reader stopped; client torn down.
- The lsp diagnostics action reports a Rust file's cargo check errors and lints instead of OK for a file that does not compile: it runs rust-analyzer's compiler check and waits for it, and a check still running at the call's timeout is named in the server notes.
- A discovery.type: ollama endpoint whose /api/tags body is not a model list fails with an error naming the URL instead of a TypeError, and a /api/tags entry that names no model is skipped instead of failing discovery.
- Fixed the plan review card dropping its option rows off the bottom on narrow terminals where the footer shortcuts wrap.
- Fixed the settings card cutting its last category tabs off the sidebar on terminals too short to list them all; the sidebar now scrolls to keep the selected tab in view.
- A bash block's $ command header takes the new theme's colours after a theme change.
- An expanded bash or eval block no longer states … N more lines under output it shows in full.
- agent:// and artifact:// completion skips a registered artifacts path that is a file instead of failing, as history:// completion already did.
- Aborting LSP workspace diagnostics ends the checker and every process it started and returns within milliseconds; it used to end only the checker and wait for its children to release the output pipes, which took 4.3 s with a child that ran for 5 s and never returned while a child kept running.
- A socket-mode debug adapter such as dlv that exits before it is reachable fails debug launch with DAP adapter exited (code N): <adapter stderr> as soon as it exits, instead of Adapter process exited before socket was ready on Linux and <adapter> did not connect within 10000ms after the whole readiness timeout elsewhere.
- An MCP transport that closes while the reconnection that produced it is still settling, such as a streamable-HTTP event stream that ends while the tool list is in flight, starts a new reconnection or trips the crash breaker instead of staying listed as connected, and the close of a connection a manual reconnect already replaced no longer tears down the live one.
- A write, an edit or lsp diagnostics against a language server that answers textDocument/diagnostic pulls returns the pulled diagnostics instead of waiting for a publish, so a tsgo write that broke the types reports the error in 38-123 ms instead of reporting no issues after 3,004 ms, and a warm rust-analyzer or Ruff write drops from 101 ms to 2-16 ms.
- Python, Ruby and Julia eval kernels cache their runner script in cache/<language>-runner under the veyyon config root (or $XDG_CACHE_HOME/veyyon) instead of <tmpdir>/veyyon-<language>-runner, where another account on the host could create the script first and have every kernel run it; a kernel start writes the script again when it is missing, instead of every later start in the process returning an empty cancelled cell, and writes it with the shared atomic writer so another process never runs a partly written one.
- An eval kernel whose runner exits during startup fails with <Language> kernel init failed: <Language> kernel exited with code N instead of returning an empty cancelled cell.
- veyyon completions with no shell lists the four accepted shells in its error again, which the subcommand flag check had dropped, and a bad shell prints the standard USAGE block.
- veyyon acp fails session/new on a flag neither the launch parser nor the session's extensions recognize, with the flag and the nearest known one, instead of serving every session with the flag ignored.
- veyyon trust exits 2 on an unknown flag instead of ignoring it, so a typo of --deny such as --denny no longer approves the project, and it accepts several paths and tab-completes them as files.
- veyyon rollback <version> moves to the named version instead of failing with Unexpected argument, and veyyon rollback exits 2 on an unknown flag instead of ignoring it.
- --compaction-model tab-completes model ids from the catalog like --model, instead of completing nothing.
- Hidden context queued for the next turn (an extension's nextTurn message, a parked advisor concern, a reminder) reaches the next turn when a prompt is interrupted or fails before its turn starts, instead of being dropped with that prompt, and an interrupt during that window still shows a parked advisor concern as advice.
- /force, the executor's final yield reminder and the eager todo require their tool on OpenRouter, Cursor, Devin and GitLab Duo models, where they required nothing and /force failed with Current model does not support forcing a specific tool.
- VEYYON_EVAL_PROMPTS accepts the tool-choice/any and tool-choice/named prompt ids instead of refusing them as unknown.
- write to a directory, or to a path ending in /, fails with an error giving the path to use, instead of creating a file named after the directory, replacing the file before the /, or failing with a bare EISDIR.
- veyyon @<path> naming a directory, named pipe, socket or device exits with Cannot attach <path>: it is <kind> instead of waiting forever on a pipe with no writer or failing with a bare EISDIR, ENXIO or ESPIPE.
- An update interrupted by closing the terminal, Ctrl+C, quitting the session or a fatal error during its download or checksum check removes its partial <binary>.<id>.new download instead of leaving it beside the binary until uninstall.
- read of a named pipe, socket or device, directly or through a symbolic link, selector, path list, suffix match or archive, SQLite or PDF member path, fails at once naming the entry's kind instead of blocking the turn until a writer appears or failing with a bare ENXIO.
- A prompt that @-mentions a named pipe, socket or device no longer hangs on submit; the entry is not attached.
- A truncated bash, eval, ssh or interactive shell result states the line ranges it shows, counting only whole lines, instead of ranges split evenly from the totals that named lines it did not show.
- A bash result larger than the inline budget is cut once and references one artifact holding the full output, instead of being cut a second time and naming a second artifact.
- A truncated eval result shows the first and last lines of its output within the inline budget, as bash does, instead of only its last 32.5KB.
- Streamed tool output whose chunk boundary leaves the head window less room than the next character keeps its order, instead of placing a later chunk in the head ahead of output already kept in the tail.
- A tool result whose output ends in a line break puts one blank line between its last line and its truncation or limit notice instead of two.
- defs() in a Python eval cell shows a list, tuple, dict or set as its type and length, such as list[1000000], and cuts a string before its repr, instead of building each value's whole repr to show 60 characters of it, which took 0.5s over 170MB of values and timed out on larger kernels.
- A Python or JS eval error shows the frames of the cell and the code it called, without the runner and worker frames that dispatched the cell, names each JS cell <cell> instead of a UUID file, and shows a JS cell that did not parse or a thrown non-Error as its text alone.
- A Python eval cell that does not compile reports its SyntaxError at its line in <cell> instead of under <unknown> behind runner and ast.parse frames, and a chained exception keeps the blank line after its separator.
- /todo in ACP and text mode tells the agent the list changed, and after a removal not to re-add what was removed, as the TUI does.
- /todo copy in the TUI reports a clipboard failure instead of Copied todos as Markdown to clipboard.
- /todo done, drop and rm, and /todo start in the TUI, match a task or phase named in quotes.
- /todo export in the TUI creates the directories a missing path needs, as ACP does.
- The terminal /move command records the directory change for the model, renders request paths against the new directory and rolls the move back when the destination fails to load, as the ACP /move command already did.
- The ACP /move command strips outer double quotes from its path, and the ACP /rename command prints Session name cannot be empty. for a title with nothing left once cleaned, as the terminal does.
- ACP and RPC clients receive the new directory's command list after /cwd or the agent's set_cwd tool, and ACP clients its config options, as they already did after /move.
- /cwd strips outer double quotes from its path, as /move does.
- In print mode, a slash command after /move or /cwd resolves relative paths against the session's current directory instead of the launch directory.
- /export and RPC export_html resolve the output path against the session's working directory and report the absolute path written, so an ACP session no longer exports into the server's launch directory.
- /export keeps a path that contains spaces whole and strips outer double quotes in the terminal, ACP and print mode, as /move and /cwd do.
- /fast and /browser accept an argument followed by whitespace outside the terminal, as the terminal does, instead of printing the usage line.
- /session Info and /session info (trailing space) now show session info over ACP and in print mode instead of the usage line, and a mistyped /session verb in the terminal prints the usage line instead of showing session info.
- /session info over ACP and in print mode prints Title: (untitled) for a session with no title instead of Title: undefined.
- /yolo with a word it does not know, such as /yolo disable, prints the usage line in the terminal instead of raising the confirmation that turns every permission prompt off.
- Bare /yolo in the terminal turns the bypass off when it is on, as it already did over ACP and in print mode, instead of reporting that it is already on.
- /model <query> in the terminal opens the model picker with <query> in its search field instead of ignoring the argument.
- /force <tool> accepts a tab between the tool name and the prompt instead of treating the whole line as the tool name.
- Fixed /memory:<action> in the terminal printing the usage line instead of running the action, as print mode and ACP already did.
- Fixed /mcp:<verb>, /ssh:<verb>, /btw:, /tan: and /omfg: in the terminal reading the colon as part of their arguments, so /mcp:list showed help instead of the server list.
- /ssh add and /ssh remove read one grammar in the terminal and over ACP, print mode and RPC: text mode accepts desc and compat, the terminal names the replacement for a plain host, port or scope, and an empty /ssh add reports the missing host name on both.
- A diff or read window of a multi-megabyte source copies the source to the block-boundary lookup in an event-loop turn of its own, so the copy no longer lengthens the turn that built the diff: a one-line edit of a 3.5 MB file held the loop longer than splitting the file.
- /mcp add and /mcp remove in the terminal refuse a plain scope, project, user or transport, in any letter case, with the sentence print mode and ACP give instead of Unknown argument, and the terminal /mcp smithery-search refuses a trailing scope as it already refused project and user.
- A whole-file read of a large source file no longer freezes the terminal while tree-sitter summarizes it: the parse runs off the event loop, so on an 18,000-line TypeScript file the longest event-loop stall falls from 69 ms to 1.4 ms, and four 6,000-line files read in one batch finish in 24 ms instead of 95 ms.
- Shell commands that start together at the beginning of a session share one shell snapshot and one persistent shell instead of each creating a snapshot, one of which stayed in the tmp directory after exit, and a snapshot still being created at exit is stopped and removed.
- A mid-session change to session.memoryLimitGb, session.maxProcesses, session.writeBudgetGb or session.writeBudgetKill applies to the live session budget at once instead of at the next bash, launch, write or edit call, so an eval kernel or MCP server started in between no longer joins a group with the old caps.
- A disposed session no longer registers a new budget when its session manager later starts another conversation.
- A session.memoryLimitGb the host cannot enforce is reported once, at session start, instead of at the first spawned command.
- The task brief and shared context escaped twice and a yield result sent as a JSON string are repaired once, before validation, so approval, hooks, the tool_execution_* events and the tool receive the same arguments (#896).
- Fixed a run that failed after one or more tool steps (a credential lookup or provider call that threw) being settled on its earlier tool-use reply, so a transient failure there is now retried and session_stop hooks receive the failure.
- An @-mentioned image is encoded for the active model, so a model that cannot decode WebP (Ollama, llama.cpp, LM Studio and other stb-backed local servers) receives PNG or JPEG instead of WebP, and with images.autoResize off the mention goes through the same canonical re-encode as the read tool.
- The shell snapshot, the local:// fallback root, the debug transcript, the /share HTML handed to a custom share script, the LLM request dump, PDF image extractions, git patches, task branch worktrees, screenshots, generated images, external-editor buffers, speech recordings, eval agent artifacts, the debug adapter socket and the /debug work-profile flamegraph are written under <tmpdir>/veyyon-<uid> (mode 700) instead of by a fixed or guessable name in the shared temp directory, where another account could read them or replace them first.
- A theme color, var or export given as a number that is not an integer from 0 to 255 fails theme validation with "colors.<token>" must be a string or a palette index from 0 to 255 instead of loading as a missing or malformed hex color.
- A card on a short terminal sheds its padding, tip and spare footer rows before its body, so the ask dialog keeps every option on a 10-row terminal, the plan review its options on a 12-row one, the /mcp add wizard its input row on an 11-row one, and the agent transcript viewer its notice and reply editor.
- The list cards (/resume, /tree, /copy, /theme, the message picker, history search, the spend-reset, rollback and subcommand pickers, and the thinking, queue-mode and image selectors) size their list to the rows the card shows, so a short terminal shows the rows around the cursor instead of a list the card clips from the bottom.
- The hook selector behind ask questions and extension ui.select keeps the highlighted option on screen on a terminal 8 to 10 rows tall instead of clipping it off with the options below it.
- /settings and the model picker on a short terminal show fewer rows instead of cutting the active row or the list off the card, and /copy gives its rows to the list instead of drawing a Preview heading with nothing under it.
- Stopping the interactive mode stops every spinner, rail, shimmer and loader in the transcript, nested groups included, so no transcript timer outlives the mode.
- A shell retained for a plain-& background job holds one reap interval, and retaining the same shell again arms no second one.
- No user-visible change: resetSessionCpuLimitsForTests disposes every limiter it drops, so a limiter's watcher interval ends with the test that created it.
- Extension tools, hooks and commands in print, RPC and ACP modes redact provider-bound text through ctx.obfuscateProviderText as the terminal does; they used to pass it through unredacted.
- An extension in a spawned agent reads the agent's context usage through ctx.getContextUsage() and compacts it through ctx.compact(); they used to return undefined and do nothing.
- A failed abort requested by an extension in print, RPC or a spawned agent is logged instead of exiting the process.
- No user-visible change: print, RPC, ACP and a spawned agent build their extension actions through one sessionExtensionActions in extensibility/extensions/session-actions.ts instead of three copies.
- The ask dialog and the plan review shed their padding on short terminals like every other card, showing up to four more body rows at 24 rows.
Removed
- The agent.prune.enabled, agent.prune.afterMs and agent.prune.waitingAfterMs settings are removed with the agent pruning they controlled; a config that sets them loads with the keys dropped.
- AgentSession.markMovedFromEmptySessionFile and the dispose step that read it are removed; nothing called the method, so the step never ran; no user-facing change.
- The terminal's emitCustomToolSessionEvent and showToolError are removed; nothing called them; no user-facing change.
- The ssr, expandMacro, runnables and relatedTests LSP server capabilities are removed from the built-in rust-analyzer entry and the documentation; no code read them, and a config that still sets them loads unchanged.