Unreleased
next release
Added
- The browser tool's open takes context, an isolated cookie jar that every headless tab naming it shares and no other tab sees, closed with its last tab (#947).
- The browser tool's save_state action and tab.storageState() write every cookie of a tab's context and the localStorage of its open origins to a Playwright-compatible state file with mode 0600, and open's storage_state and tab.loadStorageState() load one into a headless tab before the first request, writing localStorage once so a key the site later clears stays cleared; a spawned or connected browser's session can be saved but not overwritten by a load (#947).
- The browser tool's open with a url returns the loaded page's aria snapshot, whose refs a run can act on, when it is at most 6,000 characters, so a small page needs no separate call to read it; a larger page's size is stated instead, and the open's card draws its own rows as before.
- The browser tool's tab.uploadFile takes a button, label or other control that opens a file chooser and hands the chooser the files, refuses an input that takes no files, and fails naming a control that opens no chooser.
- The browser tool's tab.ref() and tab.id() can be called on without awaiting the handle first: await tab.ref("e5").click(); a call left unawaited on a ref that resolves to nothing no longer ends the tab.
- The browser tool's tab.ariaSnapshot(), and an open's page snapshot, nest each iframe's content under its line, cross-site frames and frames within frames included, with refs like f1e3 that tab.ref, aria-ref= selectors, fill and click act on inside the frame; a click there is refused, naming the cover, when the page above the frame covers its point.
- A browser action's selector may be a snapshot line's role "name" form, such as tab.fill('textbox "Email"', …) or tab.click('button "Save"'), or the attribute forms textbox[name="Email"] and role=button[name="Save"], which act on the element with that role and that exact accessible name instead of failing as invalid CSS; input[name="q"] stays CSS.
- A browser action's selector may be a comma list that mixes CSS with aria-ref=e5, a snapshot line's [ref=e5], role "name" or text/… alternatives, and acts on the first alternative in written order that matches; a list with no match, and a selector that does not parse, fail within 2 s instead of waiting out the action's 8 s; tab.select and tab.press's selector take refs.
- A browser action's selector may end in Playwright's :has-text("…"), such as button:has-text("Sign in"), which acts on the first element its CSS matches that holds the text, case and spacing aside, or on the innermost element holding it when no CSS precedes it; :has-text() nested in another pseudo-class stays refused.
- The browser tool's tab.reload() loads the tab's current URL again with goto's waiting and deadline, fetching a page that a form's POST produced with a GET instead of submitting the form a second time; a selector passed as an object names tab.id(n) and tab.ref("eN").
- A browser run whose fetch fails on a relative URL such as /api/items says that run code executes outside the page and gives the tab.evaluate form that makes the page's own request with its cookies.
- Added browser.naturalInput (default off): browser clicks, hovers and drags move the pointer along a curved, eased path and rest before pressing, typing pauses between keys, short fills are typed key by key, and off-screen elements are scrolled to with the mouse wheel; off restores the instant input.
- The browser tool reports a bot challenge it finds after open and run, naming its vendor, whether it clears on its own, needs a person or blocks the browser, and the evidence, once per page.
- The browser tool waits up to 20 seconds, or the call's timeout when shorter, for an interstitial bot check such as Cloudflare's to clear, and states where the page led or that it did not clear.
- The browser tool's open takes visible: true | false, which moves a tab between headless Chromium and a browser window with its page, cookies and localStorage, so a person can solve a challenge and the run continues.
- The browser tool's open takes profile: "<name>", a persistent Chromium profile under the agent directory whose cookies, storage, cache and history survive sessions; a profile another process holds is refused, naming its lock.
Changed
- A replace-mode edit whose old text is not in the file finds its closest candidate about 18 times faster (1.6 s to 90 ms on a 7,400-line file), and its streamed preview no longer repeats the search on every argument chunk, which held the terminal for seconds while a near-miss edit streamed.
- The browser tool sends a run's displayed and returned objects to the model as one line of compact JSON instead of two-space indented JSON, which is 36–41% fewer characters for a tab.observe() of a real page; the card still lays them out indented.
- A browser run no longer sends a return value that it already displayed, which was 20% of all run-result text in bench sessions.
- The browser tool's tab.ariaSnapshot() and an open's page snapshot leave out bare generic wrappers (layout <div>s with no name, text, state or pointer), 7–30% of a real page's snapshot.
- The browser tool's snapshots leave out a name that a row, list item, column header or other non-control node computes from the children listed under it, and [cursor=pointer] on links, buttons and other roles clickable by definition, 10.9% of the snapshot characters in calibration runs; controls keep their names and every ref stays.
- A browser run that reaches for document, window or another name the page defines fails with an error that says the name exists in the page and to use it inside tab.evaluate, instead of a bare "is not defined".
- The headless browser presents the host's own OS, architecture and browser brands, the same in the page, every worker kind, HTTP headers and client hints, instead of claiming Windows on Linux hosts; deviceandbrowserinfo.com/are_you_a_bot now reports it human where it reported a bot.
- The headless browser's WebGL renderer, screen size, window chrome and mouse-event screen coordinates match the host OS and agree between the page and its workers, and WebGL never reports SwiftShader.
- navigator.hardwareConcurrency is capped at 8 in the page and every worker alike, and navigator.languages and Accept-Language are the browser's own.
- A browser click presses a point inside its element other than the exact centre, checked again once the pointer arrives, and tab.fill types a one-line value of up to 24 characters key by key while browser.naturalInput is on, inserting longer values in one edit.
- A browser tab opened again without visible or profile keeps the window and profile it is on.
- tab.select selects by the keys a person types at a focused <select> (Chromium's type-ahead, including Cyrillic and CJK labels) and adds a multiple select's further options by a click with Control or Command held, so the page's input and change events are trusted; a disabled option, a value no option has and a customizable select are still set by script, and selecting the options already selected sends no events.
- tab.fill on a date, time, date-time, month or week input presses each field of its editor and types its digits, and on a range presses Home, End, PageUp, PageDown and the arrow keys, so the page's input and change events are trusted; a colour, and a range more than 40 keys from its value, are still set by script.
- A reused browser open with a viewport and no scale emulates scale 1.25 as a first open does, instead of 1.
- The browser tool no longer scans every evaluated script for a sourceURL suffix the pinned puppeteer never appends; no user-visible change.
- The browser tool description (1,815 estimated tokens, from 1,440) states that run code executes outside the page, that raw page.evaluate cannot see page globals, that a click never presses a cover and that refs chain, that a state file loads only into a headless tab, that iframe content nests in a snapshot with f1e3 refs, which selector forms act, what profile and visible do, and that a Challenge: line needing a person is handed off rather than solved, and asks the model to act on an open's snapshot without reading the page again, to act and read back in one run, and to act through tab actions instead of probing hit targets or page scripts first.
- The retry recovery helpers moved from session/runtime/retry-runtime.ts to session/failed-turn.ts; no user-visible change.
- Session switching and its rollback moved from session/agent-session.ts to the session/runtime/session-switch.ts collaborator; no user-visible change.
- The settings card's agent roster, model-chain picker and shared submenu pieces moved out of settings-selector.ts into agents-submenu.ts, model-chain-submenu.ts and settings-submenus.ts; no user-visible change.
- Branching, the /btw branch and tree navigation moved from session/agent-session.ts to the session/runtime/session-branch.ts collaborator; no user-visible change.
- The handoff document and the new session it seeds moved from session/agent-session.ts to the session/runtime/session-handoff.ts collaborator; no user-visible change.
- The active tool set, the base system prompt with its invalidation record, and the MCP, RPC host, SSH and vibe tool refreshes moved from session/agent-session.ts to the session/runtime/tool-activation.ts collaborator; no user-visible change.
- The collab guest dialogs, the hook dialog queue and the extension widgets moved out of extension-ui-controller.ts into collab-guest-dialogs.ts, dialog-queue.ts and hook-widgets.ts; no user-visible change.
- The login, logout, account card and saved-reset flows moved out of selector-controller.ts into account-flows.ts, and the session picker, resume and delete into session-flows.ts; no user-visible change.
- The /usage and /session panels and the /memory command moved out of command-controller.ts into usage-report-view.ts, session-info-view.ts and memory-command-controller.ts; no user-visible change.
- The tool-call cards, the compaction and retry loaders with the pinned turn error, the idle compaction and recap, the IRC cards, the replaceable job and todo blocks and the speech hooks moved out of event-controller.ts into tool-call-cards.ts, turn-recovery.ts, idle-follow-ups.ts, irc-cards.ts, displaceable-blocks.ts and turn-speech.ts; no user-visible change.
- Paste intake, the /queue batch and dequeue, submission routing and the Esc chain moved out of input-controller.ts into composer-paste.ts, queued-submissions.ts, prompt-submission.ts and composer-escape.ts, and a detached abort from those paths is logged under the new module's name; no user-visible change.
- The composer, plan review, plan annotation, /todo edit and hook-editor external-editor handoffs share one editInTerminal, so /todo edit now runs the editor on the controlling terminal like the composer's ctrl+g instead of on the inherited stdin.
- Plan mode, the plan-review overlay, plan approval and vibe mode moved out of interactive-mode.ts into plan-mode-controller.ts and vibe-mode-controller.ts, and the local-artifact copy into a fresh approved session moved to internal-urls/local-protocol.ts; no user-visible change.
- The anchored todo board, the agent HUD, their shared motion clock, the todo auto-clear and the agent-change rebuild moved out of interactive-mode.ts into anchored-blocks.ts; no user-visible change.
- The ACP plan-approval resolve types its input as ResolveParams, as the terminal plan mode does; no user-visible change.
- The LSP server selection, diagnostics wait, project diagnostics, rename_file and the edit/write writethrough moved from lsp/index.ts into lsp/servers.ts, lsp/diagnostics.ts, lsp/workspace-diagnostics.ts, lsp/rename-file.ts and lsp/writethrough.ts, so the edit and write tools no longer load the lsp tool; no user-visible change.
- The extension handler time budget and the lspmux liveness probe use settleWithin from @veyyon/utils instead of their own deadline racers; no user-visible change.
- The ephemeral side-channel turn behind /btw, /omfg, the idle recap and the composer prediction moved from session/agent-session.ts to session/agent-session-ephemeral-turn.ts; no user-visible change.
- The synchronous !command and environment config-value resolver moved from config/model-registry.ts to config/resolve-config-value-sync.ts; no user-visible change.
- The goal-continuation todo context and the magic keyword notices moved from session/agent-session.ts to session/agent-session-turn-notices.ts; no user-visible change.
- The Julia kernel's execute-request encoder is the exported buildExecutePayload in eval/jl/kernel.ts instead of an inline constructor option; no user-visible change.
- A prompt, a steer, a follow-up and a synthetic follow-up build their text-and-image content and the text-only model's image description through one session helper; no user-visible change.
- The model registry's persisted resolved-models.json stage codec moved from config/model-registry.ts to config/resolved-model-stage.ts; no user-visible change.
- The model registry builds custom-model and provider-override headers through createLiveConfigHeaders directly instead of two single-use wrappers; no user-visible change.
- The live config-header builder moved from config/model-registry.ts to config/live-config-headers.ts; no user-visible change.
- Model override, custom model and provider compat application moved from config/model-registry.ts to config/model-patch.ts; no user-visible change.
- The session appends a custom message without a turn, and merges queue-chip text into a queued message's details, through one helper each instead of four and two copies; no user-visible change.
- The last-assistant-message readers moved from session/agent-session.ts to session/agent-session-message-shapes.ts; no user-visible change.
- Doc comments that module splits left above the wrong declaration (stageModels, AUTO_THINKING, ConfiguredThinkingLevel) are back on their definitions; no user-visible change.
- Source comments that named a moved or deleted symbol's old module now name its current one; no user-visible change.
- Dropping a failed assistant turn and its never-ran tool results from active context moved from session/agent-session.ts to session/runtime/turn-context-check.ts, beside the function that puts one back; no user-visible change.
- The status line reads the session's provider and OAuth account through its typed members instead of a reflective record with a fallback for a session shape that no longer exists; no user-visible change.
- The GitHub cache reads its three settings by their declared paths instead of through an untyped cast; no user-visible change.
- MCP request params, the reloaded ssh tool and the argument display read through their declared types instead of as unknown as casts; no user-visible change.
- Settings reads in the status line, extension dashboard, speech input, browser, Hindsight memory, advisor roster, task agent settings, extension wrapper and ACP extension listing use their declared types instead of casts; no user-visible change.
- Permission intent and memory consolidation parsing read their arguments through the isRecord guard instead of repeating it as a cast; no user-visible change.
- The inline tool-output budget no longer re-checks for a non-finite tools.artifactSpillThreshold, which the settings read already replaces with its default and reports; no user-visible change.
- The session composition root drops a redundant field comment to stay within its measured size; no user-visible change.
- Session creation applies the web search and image provider preferences through the one reader the session scope uses; no user-visible change.
- Settings reads drop the fallbacks and kind re-checks the typed read already guarantees, and provider preferences read their declared types; no user-visible change.
- The read tool's default line limit is bounded through the shared clampLow helper; no user-visible change.
- Internal modules import output notices, tool status icons, formatters, the memory root, the active skill set, the IRC switch and the GitHub fetchers from the modules that define them instead of through compatibility re-exports; no user-visible change.
- The github tool's Actions run model, fetches, outcome rules and watch reports moved from tools/web/gh.ts to tools/web/gh-run-watch.ts; no user-visible change.
- Extension and custom slash-command resolution and the command context a session builds without an extension runner moved from session/agent-session.ts to session/agent-session-commands.ts; no user-visible change.
- The process agent lifecycle manager is rebuilt when the process agent registry is replaced, so it never terminates or parks agents through a registry nothing reads; no user-visible change.
- Deleted tool-renderer, output-notice, image and LSP helpers that nothing called (renderCollapsedOutputLines, formatMeta, formatEmptyMessage, formatTitle, the rendered-string memo, appendParseErrorsBulletList, the styled truncation notices, SUPPORTED_INPUT_IMAGE_MIME_TYPES, getServerForFile, hasCapability); no user-visible change.
- The benchmark CLIs share one time-to-first-token predicate, the guided-goal turn and the memory passes share one per-attempt credential refresh, the model registry and the sign-in list share one reader of disabledProviders, and the legacy plugin loader resolves package imports and exports targets through one function; no user-visible change.
- Removed the parameters autoresearch branch handling and the release installer accepted and never read; veyyon update --force still reinstalls the running version, with no user-visible change.
- The auth broker command imports the credential store from @veyyon/ai/auth-storage-sqlite, with no user-visible change.
- Modules import the timeout, whitespace, advisor transcript name and slash-command category helpers from their owning modules; the utils/fetch-timeout re-export module is removed, with no user-visible change.
- The LSP client and diagnostics, the web search tool and the web-search command import the language-id, grouped-diagnostic and search-provider metadata helpers from their owning modules instead of the lsp/utils and search/provider re-exports, which are removed except getSearchProviderLabel; no user-visible change.
- Built the OpenRouter and Gemini image-generation URLs from the @veyyon/catalog/provider-endpoints constants instead of repeated host literals; no user-visible change.
- 96 modules that read only the active theme import it from theme/theme-binding instead of loading the theme engine, so 60 of them no longer reach theme JSON loading, highlighting or mermaid; no user-visible change.
- Removed two section comments in the github tool module that described fetchers now in gh-fetch.ts; no user-visible change.
- The status line's context gauge finds its usage anchor by walking back from the newest entry instead of scanning the whole branch, so drawing it on a 97,000-entry resumed session takes 0.19 ms instead of 2.1 ms per frame.
- Writing a message after a custom entry adds only the new entries to the session's message-timestamp index instead of re-reading the whole branch, so each such write on a 97,000-entry session takes 0.49 ms instead of 5.4 ms.
Fixed
- A tools.approvalMode in a config file that is not one of its values reads as ask instead of the default auto, and every configured value that does not match its declared type is shown at startup with the file to edit.
- A failing !command for a provider API key or header in models.yml names the setting it was resolving in its log report, as the other config values already did.
- ctrl+g in a hook or ask-input editor whose $VISUAL/$EDITOR cannot start shows the failure under the text instead of ending the session, and with no editor configured shows that instead of doing nothing.
- A provider error that ends a turn is logged at warn with the provider's message, model and error classification, and a failure's retry classification is logged once instead of once per recovery check (#935).
- A headless browser launch reads the binary's product and version again after a --version probe that failed or timed out, instead of launching every later tab of that binary without a matching --user-agent, which named itself HeadlessChrome.
- A credential-free web search that returns before its soft or hard deadline cancels the deadline's timer instead of holding the embedding process open until the deadline passes.
- Disposing an advisor during the one-second delay before it retries a failed review ends the delay instead of holding a timer, and the embedding process, until it runs out.
- A web search cancelled while its headless-browser fallback is opening a page closes that page once it opens instead of leaving it open in the shared browser, and a search cancelled during the fallback's retry delay ends at once instead of after the delay.
- When several sessions start the launch process broker for one directory at once, one broker runs instead of a second one that read the first one's lease before its claim was written and took it as stale.
- With browser.naturalInput on, a click whose delay an op deadline or abort cuts short releases the button at once instead of holding it for the rest of the delay, which failed the tab's next press with 'left' is already pressed.
- A browser :has-text() selector matches the text Playwright matches, so a page whose <title>, inline <script>, <style> or <noscript> holds the words acts on the element showing them instead of timing out on an element with no box, and a submit input's value and a shadow root's text count as their element's text.
- tab.fill on a time or datetime input whose min and max narrow the hour field, such as min="13:00" max="17:00", types the hour as a person does instead of setting the value by script with untrusted input and change events.
- With browser.naturalInput on, a click through a tab.id() or tab.ref() handle whose element the page replaces while the button is held is made again on the replacement instead of reporting success with no click sent.
- The browser tool removes the temporary Chrome profiles a crashed or killed process left in the system temp directory before it makes the next one.
- A headless browser tab opens while another user of the host has a browser open or left a temporary profile in the shared temp directory, instead of every launch failing with EACCES on that user's profile.
- A browser open with visible whose move fails or is interrupted after the tab left its browser, such as on a page that cannot load, puts the tab back on its browser with its page, context, cookies and localStorage instead of losing it, and a move whose tab has a page holding a dialog open fails within the call's timeout instead of after a minute.
- A browser click or hover on an element in a cross-site frame, including a frame inside a frame, waits for the frames to draw first, so a press right after the page loads reaches the element instead of the document above its frame while reporting success.
- With browser.naturalInput on, a tab.id() or tab.ref() handle whose element the page re-renders while the wheel scrolls to it scrolls to the replacement instead of failing as detached, and loading the browser tool no longer evaluates arktype before its first schema is built.
- After a sign-in form in a headless browser tab, clicks and keys reach the page again: the browser runs with the password manager's save offer and breach check turned off, whose prompts Chrome drew over the tab a few seconds after sign-in and which blocked every later action without an error.
- A session without the browser tool no longer reads about it: the read description names the browser only when the tool is on, and the reminder to verify an edit names only the check tools (bash, eval, debug, browser) the session has.
- The browser tool's tab.fill replaces a value in one trusted text insertion, so a React or Vue field's state follows it, the empty value included, and a fill takes about 1.5 ms where the locator fill waited 33 ms for a stable bounding box; it fills contenteditable elements, sets date, time, colour and range inputs with input and change, accepting a value such an input stores in another form (#FF0000, 2026-09-26T10:30:00, 50.0), refuses checkboxes, radios, file inputs, <select> and read-only fields with the call that handles them, and refuses an element that cannot take focus instead of typing into the field that has it.
- A browser click, hover or drag in a headless tab other than the most recently opened one completes instead of stalling for eight seconds and failing as hidden or covered: each run brings its tab to the front.
- A browser tab.click by selector or aria ref, and a handle's click() and hover(), no longer press whatever covers the element: a cover that goes within 2.5 s is waited out, one that stays fails the call naming it with nothing pressed, and a disabled control is waited for until it is enabled. A handle's click(), hover() and fill() run under the deadline of the run that calls them and end with it.
- A promise that browser run code does not await and that rejects, such as page.click("#missing") without await, fails its run as Unhandled rejection (missing await?), or is logged once that run has ended, instead of ending the tab's worker so the next run hung until the tab was killed.
- A browser tab closed twice at once, or opened again while it closes, is torn down once, instead of releasing its context and browser twice and closing them under other open tabs; a tab closed while a timed-out run's worker is being replaced no longer keeps that new worker running.
- A browser run stuck in synchronous work past its deadline, such as an execSync or a busy loop, gets its worker replaced and keeps its page, its fields and its session for the next run, instead of killing the tab.
- A failed browser run's text, what it displayed and its error, is held to the same inline budget as a result's, keeping its head and its tail, instead of sending an execSync's stderr or a dumped page whole on that turn and every turn after it.
- A browser tab.waitForNavigation() written after the click that navigates, with only reads or wait between them, returns once that page has loaded instead of timing out on a navigation that committed before it started listening.
- Opening a second headless browser tab no longer resets navigator.platform in the first to the host's native value.
- Workers the headless browser starts no longer run from blob: URLs, which exposed their location and broke relative importScripts.
- Opening a visible browser on a Linux host with no display fails with a message naming DISPLAY and WAYLAND_DISPLAY instead of Chromium's launch error.
- A browser run stuck in synchronous work no longer holds new tabs and page workers in every tab until the stuck command returns: the main thread holds each tab worker's browser connection and closes it when it replaces the worker, so during an execSync with 8 seconds left a new tab opens in 301 ms instead of 8,077 ms and another tab's page worker answers in 32 ms instead of 8,022 ms.
- A browser tab whose stuck run had its worker replaced keeps its host-true identity, page scripts and viewport, and still loads state files, activates for each run and closes its page on close, instead of reporting empty client hints and a default-size window on its next page.
- A headless browser tab opened with its own viewport reports a window around that viewport, instead of the shared window's width around it, which devtools detectors read as an open DevTools panel.
- The headless browser screen leaves the host's taskbar or menu bar out of its work area and opens the window inside it, instead of a work area the size of the whole screen, and the functions its page scripts patch refuse a prototype cycle as native functions do, with chrome.runtime stubs that are not constructors: CreepJS rates the browser 31% like headless and 0% stealth instead of 38% and 40%.
- The browser tool's bot-challenge check reads the page in puppeteer's isolated world, instead of making about 30 document.querySelector calls and a text walk in the page's own world after every open and run, where a page that replaces DOM methods saw them.
- A visible browser tab emulates no viewport: its page lays out in the window's content area at the display's scale and follows the window when a person resizes it, viewport resizes the window to hold that content area, and a stuck run's replacement worker keeps the window's size, instead of a 1365x768 page at scale 1.25 whose bottom 87 px the window did not show; viewport.scale on a visible tab is refused.
- The browser open result and tab.observe() report a visible or attached tab's content area and scale as its viewport, instead of 1365x768 at scale 1.25.
- tab.observe() no longer fails with Cannot read properties of null (reading 'nodeType') while a date, time or colour picker is open; the picker's own controls are left out of the list.
- A browser tab.drag() or multiple tab.select() cut short by its deadline or an abort releases the mouse button or modifier it pressed, instead of leaving it held so every later click on the tab failed with 'left' is already pressed or arrived as a Control-click.
- A browser text/ click releases its page handles and its deadline timer when it returns, instead of leaking a handle per clickable ancestor on every poll.
- A browser the browser tool spawned is killed on session end or a failed attach when the native addon cannot load, instead of being left running.
- A foreground bash command that finishes before the auto-background threshold, a client-terminal command that exits before its timeout, an edit whose LSP diagnostics arrive inside the 500 ms inline wait, MCP startup, an LSP or DAP adapter shutdown, the GPU probe, the speech recorder's start wait, an async job delivery wait, the browser's selector hint, file-chooser and frame-draw waits, memory dispose and session shutdown clear their deadline timer when the work finishes, instead of leaving it armed for the rest of the window (up to 300 s for the bash threshold) and holding the process open.
- A hook or custom command's ctx.ui.terminal.custom factory is typed (tui, theme, keybindings, done), the arguments the terminal passes, and the qna and handoff hook examples call done instead of the keybindings manager.
- A custom command on a session built without an extension runner redacts through ctx.obfuscateProviderText instead of throwing a TypeError.
- A discovered custom tool's factory-scoped ui and hasUI receive the host's UI, instead of staying a no-op context and false in every mode.
- The image-generation, speech and Exa tools join a session's custom tools without a type cast; no user-visible change.
- Fixed the task card crashing or drawing NaN% confidence when a reviewer yields a verdict whose explanation, confidence or correctness has the wrong type; the card now draws the last well-formed verdict.
- Fixed a resumed session restoring a goal whose recorded status is not one this build writes, which ran as neither active nor finished while its badge showed it running; a usage record with such a status no longer overwrites the goal's counters.
- Fixed a scalar where a settings group belongs in config.yml (such as tools: on) moving the whole file aside to config.yml.corrupt; the scalar is ignored and every other setting in the file still loads.
- Setting the todo reminder limit no longer turns todo reminders back on, and turning reminders off no longer drops the limit; the limit is todo.reminderLimit, and a stored todo.reminders.max moves there on load.
- Startup configuration warnings state what a setting of the wrong kind or outside its allowed values reads as, and report such values in --config overlay files as well as the profile config.
- The task tool's context argument streams into the pending preview as it arrives in batch mode instead of advancing in throttle-sized steps.
- The update check, release-list and checksum fetches, release binary download, Smithery requests and Hindsight requests bound the response body with their deadline and clear its timer when the request finishes, instead of leaving it armed afterwards.
- /mcp smithery-login falls back to API-key entry after five minutes when a browser-login poll request stalls, instead of hanging.
- The /mcp add wizard repaints a finished connection check and the review step that follows it without waiting for a key, and Esc or a newer check during the check or its pause keeps the step the wizard is on instead of jumping to the review step.
- Fixed the /tree and /branch pickers cancelling themselves 100 ms after opening over an empty list, which also cancelled twice when dismissed inside that window; they now stay open on their empty state until dismissed.
- The ! command block draws every streamed output window instead of skipping any that arrive within 50 ms of the previous one, which left the last line before a quiet period undrawn and joined the lines on either side of a skipped window; the ! and % blocks also no longer overflow the call stack on an output window of about a million lines.
Removed
- The headless browser no longer returns a made-up font list from queryLocalFonts.
- Removed the tinyModelClient alias of tinyTitleClient and the OPENROUTER_BASE_URL alias in the Perplexity search provider; no user-visible change.
- Removed the parseSha256Sidecar re-export from @veyyon/coding-agent/cli/update-cli; import it from @veyyon/natives/sha256-sidecar.