Skip to content

v1.5.0

Choose a tag to compare

@github-actions github-actions released this 19 Sep 01:29
· 1620 commits to main since this release

@veyyon/stats

Changed

  • Removed the duplicate JavaScript build script; the TypeScript build entrypoint produces unchanged dashboard assets.
  • Centralized shared request and error data table renderers across overview, requests, and errors routes.
  • The per-type breakdown labels spawned sessions as "Spawned agents" while preserving the subagent discriminator and historical token and cost totals.
  • The package directory is apps/stats instead of packages/stats; the published package name, entry points and behavior are unchanged.
  • Array copies that allocated with a spread now use .slice(), .concat() or Array.from(). No user-visible behavior changes.
  • Repointed a doc comment at @veyyon/kernel/session/session-entries, where the session header type now lives; no behavior change.
  • Array spreads over iterators in the dashboard aggregators are Array.from calls and source comments reference apps/stats and clients/web; no user-visible change.
  • The /api/stats/* routes are one table of reads in the server rather than one if block each; every route answers the same JSON.
  • Every backfill reads and writes its meta sentinel through one pair of helpers, and the two file-offset re-parses share one enrol-and-wipe step; each backfill runs once, as before.
  • User and assistant session messages qualify for linking through one role-and-id check; a message without an id is skipped from both statistics, as before.

Fixed

  • Corrected comments that named a distribution channel or runtime API the project does not use; no behavior change.

@veyyon/collab-web

Changed

  • The package directory is clients/web instead of packages/collab-web; the package name, the relay and the guest client are unchanged.
  • Updated collaboration client to protocol v4, carrying tool execution displays on active tool state and tool result messages.

@veyyon/host

Added

  • @veyyon/host states what a host offers a plugin: HostNotification and HostNotifier, each capability optional and absent when the host cannot honour it. The package imports nothing.

@veyyon/model

Added

  • Model.longContextCost declares the second rate card a model bills at once its prompt crosses inputThreshold tokens.
  • @veyyon/model states the model and message vocabulary: the Model row with its thinking config and the Effort ladder, the Message envelope with its content blocks and the streamed AssistantMessageEvent union, the ToolCallMetrics and AssistantTurnMetrics study records, and the service-tier vocabulary. A provider implements a stream and a host reads a turn without importing the catalog that resolves the model or the client that drives it. The package has no dependencies.

@veyyon/plugin

Added

  • @veyyon/plugin states the plugin manifest vocabulary: PluginManifest, PluginFeature, PluginSettingSchema with its four setting kinds and PluginSettingType. A package declares what its package.json veyyon field contributes without importing the loader that installs it or the host that draws its settings. The package has no dependencies.

@veyyon/session

Added

  • @veyyon/session states what a session file is made of: the SessionEntry vocabulary, the AgentMessage union and the CustomCompactionSessionEntries and CustomAgentMessages hooks a package augments. It imports only types from @veyyon/model.

Changed

  • Doc comments refer to a spawned session as an agent and to its record as AgentSpawnEntry; the persisted subagent_spawn entry type is unchanged.

@veyyon/settings

Added

  • @veyyon/settings states the setting declaration vocabulary: SettingDef and its seven definition kinds, SettingType with SETTING_TYPES and isSettingType, SettingTab, AnyUiMetadata and SubmenuOption. A package declares a setting in this vocabulary without importing the store that persists it or the host that draws it. The package has no dependencies.

Changed

  • The SettingTab member for the agent pages is agents instead of subagents; the tab is an in-memory id and no persisted setting key changes.

@veyyon/tool

Added

  • @veyyon/tool states what a tool declares and returns: ToolSpec with its examples, the ToolTier and ToolApproval vocabulary, ToolResult and ToolUpdateCallback. A tool declares itself without importing the loop that schedules it or the host that prompts for approval. The package imports the message content blocks from @veyyon/model, type-only, and nothing else.

@veyyon/view

Added

  • @veyyon/view states the host-agnostic tool view model: ToolView, StatusRowView, TextBlockView, ViewSpan, ViewTone, ViewStatus and ToolViewRenderer. A tool returning one of these describes its output without importing a terminal component or receiving a theme, so any host can draw it. The package has no dependencies.
  • ToolViewContext states the disclosure state a host passes to a view renderer, so a tool can show a shorter summary collapsed and its full output expanded without naming a host.
  • FramedBlockView states a framed panel: a StatusRowView header, an optional ViewStatus the host reads for the rail colour, and ViewSections of ViewLines, so a tool describes a card without negotiating a width.
  • StatusRowView.emblem names a symbol a host resolves from its own registry, falling back to the row's status icon when the host has no such symbol.
  • ToolViewRenderer.renderResult receives the call arguments alongside the result, so a card whose header states the operation still states it when the call failed and returned no details.
  • ViewSpan.symbol names a symbol a host resolves from its own registry for one span inside a line, drawn in that span's tone; text is what a host without such a symbol draws instead.
  • HeadedBlockView states a frameless card: an optional StatusRowView header, ViewLines under it, and a ViewHiddenCount naming what a preview held back, so a terse tool card describes its rows without picking an indent, a width or the gesture that reveals the rest.
  • ViewTone carries an output tone for text the tool itself produced, so a card that shows tool output states that rather than reaching for a colour name.
  • ViewSpan.link and StatusRowView.descriptionLink name a target a run of text points at, so a card states where a URL leads without writing a terminal escape sequence, and ViewTone carries a link tone for text that is a target.
  • ViewSection.hidden states what a section held back as a ViewHiddenCount, so a framed card discloses the rows it did not draw the same way a headed card already did.
  • FramedBlockView.contents distinguishes a card whose body is a status report from one whose body is fetched data, so a host paints the outcome across a report and leaves fetched content on its own ground.
  • NoticeView states a short notice whose whole body carries one state — a headline, an optional tag and lines under it — so a tool describes a decision without filling a plate itself; a span inside a notice states emphasis and structure, never colour.
  • ToolViewContext.partial states whether the result in hand is the tool's last word or an update it will replace, so a card that streams reports running rather than reading an outcome off a half-finished result.
  • ViewSection.tail states that a section is a window onto the end of its lines, measured by the host in the rows the lines occupy after wrapping, so a tool that cannot know a width or a viewport height still keeps the end of a long command or a long output on screen.
  • HeadedBlockView.tail states that a headed card's lines are a window onto their end, the bargain ViewSection.tail strikes for a card with no sections to strike it in, so a stream of output whose newest rows matter is stated whole and cut by the host.
  • ViewSpan.trailing states that a run is trailing detail on its line, so a host with columns to spare places it at the line's end and pads the runs that lead it, and a host drawing one column after another writes it where it falls.
  • StatusRowView.descriptionFileLine states the line inside descriptionFile a row names, so a card that read one window of a file sends a reader to that window rather than to the file's first line.
  • ViewCodeLines.lineNumbers states the number each line has in the file, one entry per line, for a section that is several windows onto one file: the rows are in file order and the numbers jump, and a null entry is a row standing in for an elided span.
  • ViewSection.markdown states that a section's lines are a document rather than source, so a tool that read a .md file says what the text is and leaves headings, emphasis and layout to the host.
  • ViewSpan.markdown states that one run of a line is Markdown written for a single line, so the label of an offered choice keeps the emphasis its author wrote without the tool deciding what bold looks like; a host with no renderer draws the source.
  • ViewSpan.status states that one run is the state mark of its line, drawn from the host's own glyph set and animated while the state moves, so a card whose body is a set of things each doing something marks every row without naming a glyph.
  • ViewSpan.badge states that one run is a short label the host sets off in its own bracket grammar, the line-level twin of StatusRowView.badge, so a row that marks what kind of thing it names leaves the brackets to the host.
  • ViewSpan.live states that the thing a run names is still in flight, so a host with a clock may animate that run alone while the settled columns beside it stand still, and a host with no clock draws it in its tone.
  • StatusRowView.descriptionFits states that a row must keep its description whole by shortening it inside itself, so a card whose description is the file it acted on keeps both the path and the counts after it, and a card whose description is prose is left for the host to clip at its own edge.
  • ViewSpan.agentId states a semantic agent identifier for subagent drill-down navigation in hosts that support it.
  • UNICODE_SYMBOLS and SymbolKey are the one table of glyphs every host draws a symbol, emblem or status mark from, moved here from the terminal's theme so the GUI and the web renderers draw the same ✎ for tool.edit.
  • ToolViewContext.showResolvedModel states whether the surface shows the model a spawned agent resolved to beside its name; the surface reads the setting and the view receives the answer, so a transcript export with no setting store draws the same card with the badge off.

Changed

  • Doc comments refer to the spawned-agent wall as the agent wall. No behavior change.
  • ViewSpan.symbol names a key of UNICODE_SYMBOLS; a key no host has draws the span's text, never the key.

@veyyon/wire

Breaking Changes

  • Bumped COLLAB_PROTO to 4: tool execution display projections (ToolExecutionDisplay) are carried on tool calls, tool results, and live execution events. Old guests speaking proto v3 or below are rejected with the protocol-mismatch error.

Added

  • Tool execution display metadata includes per-call grouped-read status, paths, previews and line numbers.
  • @veyyon/wire/presentation states the renderer contract: PresentationContext, the transcript, status, composer and overlay view-models, the UIEvent union and an abstract hex-colour theme. A renderer implementing it draws a session without importing coding-agent. The subpath adds no dependencies.
  • @veyyon/wire/collab-link owns the collab link grammar: encodeBase64Url, decodeBase64Url, isLocalHostname, normalizeRelayOrigin, generateRoomId, formatCollabLinkPayload, formatCollabLink and parseCollabLink, beside ROOM_ID_BYTES, ROOM_KEY_BYTES, WRITE_TOKEN_BYTES, DEFAULT_RELAY_URL and ParsedCollabLink, which moved there and are re-exported from the barrel unchanged; base64url goes through atob/btoa so the browser guest and the host run the same code.

Changed

  • TextContent, ImageContent, ThinkingContent, RedactedThinkingContent, ToolCallContent, FallbackContent, WireStopReason and WireUsage are Pick projections of the shapes @veyyon/model owns, imported type-only, with the same fields as before; the package declares @veyyon/model and no runtime dependency.
  • AgentProgressPayload and AgentLifecyclePayload are the payloads on the task:subagent:progress and task:subagent:lifecycle bus channels; SubagentProgressPayload and SubagentLifecyclePayload remain exported as the same types, and the channel spellings are unchanged.
  • generateRoomKey and generateWriteToken return the filled random buffer directly; the key and token lengths are unchanged.

@veyyon/tui

Breaking Changes

  • @veyyon/tui exports rendering only. The string, escape, keyboard, mouse, motion and layout-math primitives it also carried are now @veyyon/utils modules, imported by subpath: @veyyon/utils/{ansi,autocomplete,bar,bracketed-paste,deccara,fuzzy,keybindings,keys,kill-ring,kitty-graphics,latex-block,latex-unicode,loop-watchdog,motion,mouse,padding,paint-columns,paint-ground,paint-surface,sgr,symbols,tab-width,text-sizing,tight-mode,tmux,width,word-nav,wrap}. The barrel re-exports none of them.
  • MOTION and the grow, hover, paint and settle curve tables are one module, @veyyon/utils/motion.
  • EditorComponent is @veyyon/tui/components/editor-component.
  • Render engine modules are available through @veyyon/tui/core/*: component-types, container, cursor, image-budget, mouse-routing, overlay, renderer, scroll, terminal-session and tui; existing TUI contract imports remain available through @veyyon/tui/tui.
  • TUI.overlayStack is private. The overlay stack's behavior is OverlayStack in @veyyon/tui/core/overlay.
  • getTerminalId is @veyyon/utils/ttyid, and ImageFallbackReason is @veyyon/utils/image-fallback. Neither is rendering, and a caller that needs a session id or the name of a cause no longer depends on the terminal renderer to get it.
  • detectTerminalId and TerminalId are exported from @veyyon/utils/terminal-emulator instead of @veyyon/tui/terminal-capabilities.

Added

  • Components can provide measureHeight(width) for layout measurement without constructing rendered rows.
  • Forced render requests accept preserveViewport to paint changed rows immediately while retaining pending full-repaint and scrollback requests.
  • TUI.frameScrollable states whether the last composed frame had content above the viewport, so a host can render a scroll affordance without re-deriving it from row counts it cannot see.
  • The package is hosts/terminal/engine in the repository, beside the other halves of the terminal host. The published package name, exports and entry points are unchanged.

Changed

  • Editor.setCursor clamps its line and column through clampLow from @veyyon/utils/math; no behavior change.
  • Editor and Input deliver a chunk's typed prefix, paste payload and remainder through BracketedPasteHandler.route with sinks built once per component; the bytes each part reaches are unchanged.
  • Input drain and terminal stop pop the kitty keyboard protocol, cancel the pending modifyOtherKeys probe and reset modifyOtherKeys through one #disableKeyboardProtocols; the bytes written at shutdown are unchanged.
  • The Markdown component derives the default text style's SGR prefix from the same style chain that paints its text; no behavior change.
  • Cursor movement sequences and overlay percentage parsing use shared implementations without changing terminal output.
  • Collapsed and deduplicated scroll math, search input filtering, border framing, cursor deletion and autocomplete helpers across engine components without changing rendering or behavior.
  • Direct writes and component-scoped rendering share layout-reuse checks, and frame rendering shares cursor-marker extraction, selection and row-divergence scans without changing terminal output.
  • Markdown rendering uses the shared HTML entity decoder without changing displayed text.
  • Text layout and pointer-hover state use shared implementations without changing rendering or input handling.
  • The overlay stack resolves its topmost painted and topmost focusable entries by one reverse scan; focus and pointer routing are unchanged.
  • The ConPTY post-full-paint settle arms and cancels its trailing render timer through one pair of steps; a render requested inside the window still paints once the window closes, and a forced render still discards the pending trailing render.
  • The single-line Input answers its editing keybindings from one ordered table, and a key bound to two of them runs only the first.
  • Array copies that allocated with a spread now use .slice(), .concat() or Array.from(). No user-visible behavior changes.
  • onBackgroundColorChange and onAppearanceChange register and replay a late subscriber through one step; a subscriber that throws during replay is logged and the others still run, as before.
  • TerminalNotification extends HostNotification from @veyyon/utils/host-notification, so a terminal is one host that can deliver a tool's notification and the two shapes cannot drift apart.
  • visualColAtOffset and offsetAtVisualCol are exported from @veyyon/utils/width and shared by Editor and Input.
  • Source comments refer to the spawned-agent HUD as the agent HUD. No behavior change.

Fixed

  • Disposing a Box disposes its child components and their resources.
  • Enter submits the current message when an edit has invalidated the open file-completion popup.
  • Independent offscreen edits no longer accumulate into a false history rebuild, and changes to plain components preserve committed history until an explicit replay.
  • A sixel-capable terminal now renders inline images on Linux and macOS: the terminal is asked at startup instead of being matched against a list that named no sixel terminal at all, so images no longer silently fail to appear outside kitty, ghostty, wezterm, iTerm2 and Warp.
  • An inline image whose top has scrolled above the viewport, or which is taller than the terminal, is left undrawn until a repaint can reach its origin, instead of being stamped at full size over the top of the live transcript.
  • An inline image is handed pixels at exactly the cell box the terminal will scale it into, so the terminal's own scaler no longer smears a downscaled screenshot; the transmitted payload shrinks by more than half at the same size on screen.
  • The row shown in place of a picture names the setting that undoes the reason when there is one, instead of stating the reason alone.
  • A frame that shrinks below the committed boundary with the composer focused (an IRC card expiring, a displaced todo snapshot retracting, an agent sub-row or HUD row going, the ask dialog's inline editor collapsing) re-shows the frame tail without lowering the commit index, so the rows it re-shows are never appended to native scrollback a second time when the frame grows back; under a tall running tool card this appended the same rows on every insert/retract cycle and left thousands of copies of one status row in scrollback.
  • A frame that grows back while its window still shows rows below the commit boundary keeps sliding the window in place until it passes that boundary, with or without a focused cursor, instead of painting a window shorter than the viewport with blank rows under it.

@veyyon/kernel

Added

  • @veyyon/kernel is a workspace member: the loader, the contribution registry and the session spine, moved out of @veyyon/coding-agent unchanged. It names no tool, no host and no mode, and scripts/the-kernel-names-no-tool-and-no-host.test.ts fails on the first edge that does.
  • @veyyon/kernel/session/* publishes the session spine: entries, storage backends, persistence, migrations, listing, paths, retry policy, compaction policy, machine budget and the turn's owned resources.
  • @veyyon/kernel/loader/* publishes plugin discovery, manifest parsing, the installed registry, the marketplace client and load-failure reporting.
  • @veyyon/kernel/registry/* publishes generic contribution interfaces, tool proxying, widget and host-view declarations, and TypeBox schema conversion.
  • @veyyon/kernel/registry/tool-domain declares ToolDomainManifest, the name and lazy-factory table a tool domain contributes, so a host reads a domain's tools without depending on the coding agent.
  • SubagentSpawnEntry and SubagentSpawnRecord in @veyyon/kernel/session/session-entries are AgentSpawnEntry and AgentSpawnRecord; the persisted subagent_spawn entry type is unchanged.
  • @veyyon/kernel/registry/message-kind declares AgentMessageKind, a transcript role a tool domain records with its conversion to provider messages and to text, and ToolDomainManifest.messageKinds carries a domain's kinds; @veyyon/kernel/session/message-kinds is the role-keyed table the session spine converts them through, which throws on a role no domain declared and on a second kind for one role.
  • @veyyon/kernel/session/session-manager, session-context, session-loader and agent-storage publish the session manager, its context builder, its file loader and the credential store, moved from @veyyon/coding-agent/session/* unchanged; session/custom-message-payload publishes the custom-message payload normaliser and the rehydration sanitiser they call.
  • @veyyon/kernel/settings/schema publishes the settings schema registry: declareSettings registers a package's table and rejects a path declared twice, DeclaredSettings merges each table's type so SettingPath and SettingValue span every registered table, and getDefault, getType, getUi, hasUi, getPathsForTab, retiredBy, isSettingPath, getEnumValues, isUnsetNumberPath and describeSettingTypeMismatch answer from the registry; a query before any table has registered, or for a path no table declares, throws naming the cause. @veyyon/kernel/settings/optional-number publishes the unset-number owner, moved from @veyyon/coding-agent/config/optional-number unchanged.
  • @veyyon/kernel/settings/store publishes SettingsStore, the layered settings store moved out of @veyyon/coding-agent/config/settings: the profile, overlay and runtime layers and their merge, get, set, unset, override, getSource, isConfigured, layerValue, the YAML load with quarantine and type-mismatch collection, the debounced locked text-preserving save with its failure report, forkWithRuntimeOverrides, cloneForCwd, reloadForCwd and the one-shot migration stamp (stripLegacyUnsetSentinels, stampOwnedConfigMigrations, SETTINGS_MIGRATION_VERSION), with RawSettings, SettingsOptions, SettingSource, SettingsSaveFailure, InvalidSettingValue, QuarantinedSettingsFile, getByPath, setByPath, deleteByPath and deepMergeSettings. The store takes a SettingsStoreHooks at construction (globalBinding, migrate, loadLegacySources, afterOwnedConfigLoaded, resolveForCwd, applyHook, applyAllHooks, notifyEffectiveChange, mergedViewRebuilt) and names no setting. @veyyon/kernel/settings/signal publishes SettingSignal, clearSettingSignals and settingSignalListenerCounts, moved unchanged.

Changed

  • Session listing reuses a per-directory index for files whose size and mtime are unchanged instead of rescanning every file, cutting a 4,825-session /resume list from 6.8 s to 185 ms when a session changed and 88 ms when none did.
  • Resolving a session id that no directory in the active profile holds reads the other profiles through the same per-directory index, cutting that lookup from 2.5 s to 126 ms.
  • Settings mutations and session storage writers share implementations without changing persistence, hook ordering or error behavior.
  • Installed plugin registry readers share JSON validation while preserving numeric-version handling and malformed-file behavior.
  • Plugin runtime configuration uses the shared record validator; behavior is unchanged.
  • Edit-specific event normalization remains in @veyyon/coding-agent/extensibility/tool-event-input; event payloads are unchanged.
  • Settings lookups reuse immutable registry key snapshots and build derived indexes in one pass after registrations or resets.
  • Settings stores share layer copying and override application while retaining profile values, per-directory resolution and isolated save-failure reports.
  • Session title overlays avoid encoding and copying the full transcript when the first line occupies the fixed 256-byte slot.
  • The TypeBox unknown converter is the any converter, which had the same body; emitted schemas are unchanged.
  • Session entry validation shares non-empty string checks, and branch labels avoid temporary identifier arrays.
  • @veyyon/kernel/settings/store exports groupSettingPaths, memoizing prefix-grouped schema paths on the schema index with automatic invalidation on schema resets.
  • Array copies that allocated with a spread now use .slice(), .concat() or Array.from(). No user-visible behavior changes.
  • @veyyon/kernel/session/session-entries reads the shared entry vocabulary from @veyyon/session and registers its own entry kinds there; every name it exported is still exported and no file format changes.
  • The plugin manifest vocabulary (PluginManifest, PluginFeature, PluginSettingSchema and its setting kinds, PluginSettingType) moved from @veyyon/kernel/loader/plugins/types to @veyyon/plugin; InstalledPlugin, the lock-file state, the project overrides and the doctor and install option types stay.
  • SQL session storage consolidates parameterized queries across PostgreSQL, MySQL, and SQLite dialects.

Fixed

  • Settings queries ignore inherited object properties.
  • Type.Pick emits the keys it was asked for in the order they were asked for, and keeps a picked key that is own-but-non-enumerable on the validated value.
  • A session whose recorded leaf id no longer names an entry reopens on its last entry instead of on an empty conversation.
  • MemorySessionStorage.deleteSessionWithArtifacts deletes the session entry and its artifact files from memory instead of returning early as a no-op.
  • walkBranchPath terminates when traversing cyclic parent entry chains.
  • StringEnum options in the legacy plugin shim avoid any.
  • listSessionsReadOnly writes no session list index, so it makes no write to a directory it states it does not mutate; it still reads an existing index, which is not a mutation.

Removed

  • @veyyon/kernel/session/content-text is gone: the session spine calls the contentText owner in @veyyon/utils, which carries the separator, image, trimBlocks and trimString options that copy held.

@veyyon/natives

Added

  • PtyRunResult.signal names the signal (SIGTERM, SIGKILL) that ended a PTY-run command, recovered from the wait status; a signal death also reports exitCode 1, so a consumer that attributes the death reads signal first.

Changed

  • Shell filters share token matching and JSON field lookup while preserving exact-token matching and grep record-ending behavior.
  • Shell filters share result finalization while preserving output bytes, rewrite status and original-capture accounting.
  • Deduplicated tree-sitter language dispatch, filesystem isolation backend declarations, and syntect scope color matching across native crates.
  • Shell builtins share cancellation and argument-error handling, and GitHub and GitLab output filters share Markdown cleanup, without changing output or exit status.
  • Linux native CPU detection reads current kernel flags without consulting persisted CPU verdict files.
  • Typed tuple copies use spreads rather than .concat(), which those types do not define. No user-visible behavior changes.
  • The loader's diagnostic comment names the addon crate at natives/bridge/addon/src/lib.rs, the path it moved to. No user-visible behavior changes.
  • The rebuild instruction in the stale-addon refusal reads bun --cwd=natives/bridge/bindings run build, the package's path after it moved out of packages/. The package name @veyyon/natives and every import specifier are unchanged.

Fixed

  • Restored APFS isolation compilation on macOS and stale destination preparation for Windows block-clone isolation.
  • Plain isolation diffs classify binary contents on either side of a symlink transition without dereferencing links.
  • IsoResolveResult.reason is set only when fellBack is true; a resolution that honoured the preferred backend carried the first unavailable probe's text as if it explained a fallback.

@veyyon/agent-core

Added

  • AgentTool.view takes a host-agnostic ToolViewRenderer from @veyyon/view, so a tool describes its call and result cards without receiving a theme or returning a host component; where a tool also declares renderCall/renderResult, the host-specific pair still wins.

Changed

  • Legacy hook messages reuse custom-message fields without changing their public type signatures.
  • The turn loop rejects a Harmony leak through one rejectHarmonyLeak whether the stream ends with a done event or without one; no behavior change.
  • AgentToolResult, AgentToolUpdateCallback, ToolTier, ToolApprovalDecision and ToolApproval are defined in @veyyon/tool as ToolResult, ToolUpdateCallback and the same approval names; @veyyon/agent exports every name it exported before, so no caller changes.
  • The session-entry vocabulary (SessionEntry and every member), AgentMessage and the CustomAgentMessages and CustomCompactionSessionEntries hooks are defined in @veyyon/session; @veyyon/agent-core and @veyyon/agent-core/compaction/entries export every name they exported before, and an augmentation now names @veyyon/session.
  • Typed tuple and Set copies use spreads rather than .concat() or .slice(), which those types do not define. No user-visible behavior changes.
  • A source-path comment in thinking.ts names the coding-agent module its reader moved to; behavior is unchanged.
  • Doc comments refer to a child run as an agent rather than a subagent. No behavior change.
  • onSpanStart and onSpanEnd run through one guarded call that reports a throw as the same on_span_start_failed / on_span_end_failed warning, with the thrown error attached; no behavior change.
  • The steering and follow-up queues drain through one step that takes the first message in one-at-a-time mode and every message in all mode; delivery order and turn count are unchanged.
  • A tool call's arguments and result serialize through one telemetry path keyed by the contentSerializer field, and an onChatUsage throw or rejection is reported as on_chat_usage_failed from one place on both the agent-loop and the manual-record path; no behavior change.

Fixed

  • AgentTool.renderResult accepts the optional call arguments already supported by custom and extension tool renderers.
  • A history summary whose single request times out, or does not fit the summarizing model's context window, is produced in stages: the span is summarized as consecutive segments of up to 32k tokens, four at a time, and the segment summaries are merged in rounds into one summary, so a 234k-token session on a model that never begins a whole-span answer still compacts; compact() reports the segment count in summaryStages and takes summaryStaging: "staged" to start staged.
  • A provider's server-side compaction runs under its own ten-minute deadline instead of the three-minute remote-summarizer deadline that cut every codex compaction of a large span.
  • The staged-summary segment budget and worker count clamp through the shared clampLow, and the engine classifies a summary timeout through the error-flag leaf modules rather than the error barrel; no behavior change.

@veyyon/ai

Breaking Changes

  • Provider-specific test override setters are replaced by setProviderModuleOverrideForTest(api, module).

Changed

  • Command Code API-key validation calls the provider's default model, claude-sonnet-4-6.
  • The NVIDIA, Xiaomi, Xiaomi Token Plan and Alibaba Coding Plan logins take the pasted key through the same promptApiKey as every createApiKeyLogin provider: the key is trimmed, an empty paste is ApiKeyRequiredError, an abort during the paste is LoginCancelledError, and a host without onPrompt is OnPromptRequiredError; no behavior change.
  • API-key and OAuth credential ranking score usage windows and block a credential at its scoped limit through one #rankUsageResults, an OAuth refresh reads its candidate row before and after the lease through one readRefreshCandidate, and the plan-filter and usage-limit rejection runs before and after a refresh through one usageRejects; no behavior change.
  • Tool-argument validation runs its seven pre-validation normalizations through one ordered pass table, before the first check and after every issue-driven coercion, and the two schema-agnostic value walks share one copy-on-write array step; no behavior change.
  • GitLab Duo Workflow shares fresh-workflow restart handling without changing retry limits, request ordering, or surfaced errors.
  • Streaming provider initialization shares assistant-message construction without changing emitted metadata or mutable-state isolation.
  • Provider error projection, Google request options and Hermes/Qwen3 tool-call rendering share implementations without changing wire formats.
  • Removed duplicate OpenAI Responses moderation type declarations without changing exported types or wire formats.
  • OpenAI Responses computer actions share field declarations while preserving independent public namespace augmentation.
  • Response input and output items share field declarations while retaining namespace-specific nested types and augmentation.
  • GitLab Duo and GitLab Duo Workflow share token-response decoding and PKCE types without changing login or refresh behavior.
  • API-key validators share error-body handling without changing request formats, error messages or deadlines.
  • Lazy provider streams share import-promise caching while preserving provider loading and timeout behavior.
  • API-key logins share credential prompting and validation with unchanged provider messages and cancellation behavior.
  • Credential refresh and usage requests share cancellation handling while preserving abort reasons and independent completion of shared work.
  • The GLM, Gemini, Gemma and Qwen3 in-band scanners share one outside-text step (scanOutsideText) and Hermes and Qwen3 share one closed-body completion (emitClosedToolCall), with unchanged events at every chunking of a stream.
  • Provider module declarations share one typed stream signature with unchanged runtime output.
  • Hermes and Qwen tool calls use one JSON decoder with unchanged repair and partial-stream recovery.
  • Tool extends ToolSpec from @veyyon/tool, which owns the schema-independent declaration and the ToolExample kinds; @veyyon/ai exports every name it exported before, so no caller changes.
  • The message envelope, content blocks, AssistantMessageEvent, StopDetails, the turn and tool-call study records and the streaming partial-JSON symbol are defined in @veyyon/model; @veyyon/ai re-exports every name it exported before, so no caller changes.
  • A source comment in the OAuth callback page names the shared sun source at apps/site/sun-field.js; behavior is unchanged.
  • Typed tuple copies use spreads rather than .concat(), which a as const array does not define. No user-visible behavior changes.
  • A source-path comment in register-builtins.ts names the benchmark module it cites at its new path under tests/evals/; behavior is unchanged.
  • A source-path comment in message-text.ts names the coding-agent module its caller moved to; behavior is unchanged.
  • CONTEXTUAL_USER_PREFIXES is exported from the codex compaction module so the retained-window rule is asserted against the real list rather than a copy of it.
  • A source-path comment and the barrel-shortcut suite name the Perplexity search provider at tools/web/search/providers/perplexity.ts. No behavior change.
  • Hermes and Qwen3 share truncated-call recovery with unchanged tool names, arguments and completion events.
  • The Codex responses provider parses usage through one numeric field picker, resets the websocket chain (append baseline, turn state, models etag) through one helper at every site, and the whitespace-loop, stale-response and retryable-error recoveries share one turn restart and one delayed reopen; no behavior change.
  • The Qwen3, Hermes, GLM, Kimi, pi-native, Gemma and Gemini scanners and the thinking-healing scanner stream a reasoning section through one ThinkingSection and close a tag-delimited one through one scanThinkingText step, with the same events, the same held partial close tag and the same flush at end of stream.
  • The Cursor exec channel buffers stdout and stderr through one ShellOutputChannel per stream, sending on a newline, past 4 KiB or 100 ms after the first byte and holding an incomplete ANSI escape until its tail arrives; no behavior change.
  • deleteAuthCredential and deleteAuthCredentialsForProvider soft-delete through one step that reports a failed statement with the credential or provider it names; no behavior change.
  • AuthStorage delivers its credential-disabled, credential-failover and usage-limit-withheld notices through one subscriber call that isolates a throw or a rejection and logs it against the hook, addresses a refreshed broker row through one matcher that ignores the shared refresh sentinel, and reads a report's unanimous scope account or project id through one step; no behavior change.
  • Doc comments refer to child runs and roles as agents rather than subagents; the Codex protocol header x-openai-subagent and its subagent_kind metadata key are unchanged.
  • Both streaming gateway routes (the format endpoints and the pi-native fast path) abort the upstream call on a closed client response through one SSE cancel hook; no behavior change.

Fixed

  • Corrected comments that named a distribution channel or runtime API the project does not use; no behavior change.
  • A stream that stalls after its first event (" stream stalled while waiting for the next event") classifies as a timeout as well as transient, so auto-compaction moves to the next candidate model instead of re-sending the full context to the model that stalled up to retry.maxRetries times.
  • A Codex websocket turn that the server accepts and then leaves without progress for the idle window is retried on the websocket once and then run over SSE, instead of spending the whole websocket retry budget on stalls, which held one compaction summary for thirty minutes per attempt.
  • A codex server-side compaction cut by its deadline fails as a timeout, and one the caller cancels fails as a cancellation, instead of both reporting "stream closed before response.completed" as a backend fault.
  • The codex websocket watchdog message reports the time since the last progress as of the moment it fires, instead of a value computed before the wait.
  • Cursor and Devin protobuf regeneration invokes the workspace compiler, and Cursor output is written to the catalog package.
  • Credential-store startup applies SQLite busy handling and WAL mode before initializing refresh leases, allowing concurrent launches to wait for database locks.
  • Google's generic RESOURCE_EXHAUSTED 429 body ("Resource has been exhausted (e.g. check quota)") classifies as a per-minute throttle retried on the same account after 45-75 s, instead of a daily quota wall whose 30-minute wait exceeded the retry budget and ended the turn on the first 429; a body that states a quota keeps the quota classification.
  • AuthStorage.disabledCredentialAccount states which account a provider's refresh failure belongs to, so a note about a dead login can name it instead of reading as a statement about whichever account it renders beside.

@veyyon/catalog

Added

  • calculateCost bills a request at a model's longContextCost rates when the prompt crosses that model's threshold, so a model the upstream charges two rate cards for is no longer reported at the cheaper one.
  • closeModelCache() closes the shared model-cache database and permits reopening it at the current cache path.

Changed

  • The command-code provider defaults to claude-sonnet-4-6, discovers models without an API key, and reads COMMANDCODE_API_KEY after its two existing key aliases.
  • GitLab Duo Workflow discovery reads a record's declared root namespace (root_namespace_id, rootNamespaceId, or the id or path of its root_namespace/rootAncestor record) through one declaredRootNamespaceId for the explicit and nested lookups; no behavior change.
  • The Antigravity, Codex, Gemini and Ollama discovery readers report a non-ok status as the status stage and an unparseable body as the body stage through one exported readDiscoveryJson in discovery/failure; no behavior change.
  • Model spec rejection checks its string, cost and limit fields from ordered tables, reporting the same field names in the same order; no behavior change.
  • The canonical-id generator runs its multi-candidate expanders from one ordered table; no behavior change.
  • Canonical model normalization shares ordered transformation dispatch without changing identity resolution or cache precedence.
  • Effort, ThinkingConfig and the model and message types are re-exported from @veyyon/model, which is their single definition; the exported names and values are unchanged.
  • Bundled models resolve on demand per provider while explicitly installed full-registry snapshot stores remain supported.
  • Provider cache namespaces resolve without constructing discovery options; persisted cache keys are unchanged.
  • Bundled model snapshots use the shared integrity-framed format without repeated JSON serialization or durability flushes, and obsolete snapshots rebuild on load.
  • The model row, thinking config, effort ladder and service-tier vocabulary are defined in @veyyon/model; @veyyon/catalog/types, @veyyon/catalog/effort and @veyyon/catalog/provider-models/wire-capabilities re-export every name they exported before, so no caller changes.
  • Typed tuple copies use spreads rather than .concat(), which a as const array does not define. No user-visible behavior changes.
  • A comment on OPENROUTER_BASE_URL names the Perplexity auth module at tools/web/search/providers/perplexity-auth.ts. No behavior change.
  • An OpenAI-compatible listing's model name falls back to its id through the shared non-empty-string reader; discovered names are unchanged.

Fixed

  • Command Code models carry the provider's published prices, reasoning ladders and output ceilings instead of arriving at zero cost with no thinking control; the bundled catalog grows from 3 hand-written rows to the 69 the Provider API serves.
  • Case-insensitive host classification no longer treats control characters as URL punctuation.

@veyyon/coding-agent

Breaking Changes

  • Agent settings use agent.*, tier.agent, advisor.agents, and argot.agents; legacy keys migrate on load, while historical runtime export names, custom-theme color tokens, session records, and RPC spellings remain unchanged.
  • Account formatting helpers are exported from @veyyon/coding-agent/session/account-format instead of @veyyon/coding-agent/slash-commands/helpers/format.
  • Screen takeover moved off ExtensionUIContext and HookUIContext onto an optional ui.terminal capability: custom() and setEditorComponent() are now ctx.ui.terminal?.custom(...) and ctx.ui.terminal?.setEditorComponent(...), and a component widget goes through ctx.ui.terminal?.setWidgetComponent(key, factory) while setWidget(key, lines) keeps the text form every host draws. A host that is not a terminal omits ui.terminal rather than declaring members with empty bodies.
  • Removed ui.setHeader() and ui.setFooter(), which every host implemented as an empty function, interactive mode included.
  • Session spine, plugin loader and contribution registry modules moved from src/session/ and src/extensibility/ to @veyyon/kernel: affected subpath imports now use @veyyon/kernel/session/*, @veyyon/kernel/loader/* and @veyyon/kernel/registry/*; edit-specific event normalization remains at @veyyon/coding-agent/extensibility/tool-event-input.
  • The 152 modules under src/tools/ moved into one directory per domain — core/ for the plumbing every domain reads, then fs/, search/, shell/, web/ and agent/ — so a subpath import previously resolved through @veyyon/coding-agent/tools/<name> is now @veyyon/coding-agent/tools/<domain>/<name>.
  • sanitizeStatusText is @veyyon/utils/sanitize-status-text, so a subpath import previously resolved through @veyyon/coding-agent/modes/sanitize-status-text names the utils module: the function is text-only and every host reduces a value to one line the same way.
  • The 81 site scrapers, the page loader and the Parallel extraction client moved to @veyyon/web: a subpath import previously resolved through @veyyon/coding-agent/web/scrapers/* or @veyyon/coding-agent/web/parallel is now @veyyon/web/scrapers/* and @veyyon/web/parallel, and the search providers' withHardTimeout is @veyyon/web/hard-timeout.
  • escapeMarkdownTableCell and the Turndown helpers are @veyyon/utils/markdown-table and @veyyon/utils/turndown, and htmlToBasicMarkdown is @veyyon/utils/html-markdown, so a subpath import previously resolved through @veyyon/coding-agent/utils/markdown-table, @veyyon/coding-agent/utils/turndown or the scrapers barrel names the utils module.
  • CliUsageError is @veyyon/utils/cli-usage-error; @veyyon/coding-agent/cli/usage-error is removed, so a flag the root parser rejects and one the command framework rejects are the same class.
  • @veyyon/coding-agent/session/auth-storage is removed; the credential types and AuthStorage it re-exported are imported from @veyyon/ai/auth-storage, the module that defines them.
  • The setting declaration vocabulary — SettingDef and its definition kinds, SettingType, SETTING_TYPES, isSettingType, SettingTab, AnyUiMetadata and SubmenuOption — moved from @veyyon/coding-agent/config/settings-schema to @veyyon/settings; the schema, its readers, the tab metadata and the tab groups stay where they were.
  • URL target parsing is exported from tools/web/read-url-target instead of tools/web/fetch.
  • Setting-change signals are exported from config/settings-signals instead of config/settings.
  • fgOrPlain is exported from theme/theme-binding instead of theme/theme.
  • Diagnostic cards use diagnosticsSection from tools/core/diagnostics instead of formatDiagnostics from tools/core/render-utils; diagnostic parsing and sanitisation helpers are imported directly from tools/core/diagnostics.
  • Read selector helpers parseSel and isRawSelector are exported from tools/core/path-utils instead of tools/fs/read.
  • readSourceFsPath is exported from tools/fs/read-view instead of tools/fs/read and remains available from the package root.
  • metadataLine is exported from tools/core/render-utils instead of tools/web/search/view.
  • Removed the unused inheritedAgentDir keybinding option and ohMyPiXAIUserAgent alias; xAI requests continue to use veyyonXAIUserAgent.
  • @veyyon/coding-agent/extensibility/hooks no longer exports HookRunner, HookToolWrapper, discoverAndLoadHooks, loadHooks and execCommand: the legacy hook runtime is removed, a plugin's hooks entry loads through the extension runner, and the subpath keeps the hook types.
  • UNICODE_SYMBOLS is @veyyon/view's; theme/symbols re-exports it and keeps the terminal-only Nerd Font and ASCII presets, the spinner frames and the bar ramps.

Added

  • Exported projectToolDisplay from presentation/web-tool-display.ts, projecting canonical tool execution displays for collab live sessions and HTML export.
  • tools/view-registry.ts exports toolViewDefinitions, the host-agnostic ToolViewDefinition for every tool card the terminal drew, and tools/renderers.ts derives the terminal adapters from it; the set of cards and their chrome are unchanged.
  • A tool card value-imports no tool: the search card limits are tools/search/search-card-limits.ts, the web search provider label is tools/web/search/types.ts, and the launch card owns callMeta and readyPendingSummary; each name is exported from that one module only. Print mode and the HTML export load a card without the tool behind it, and no output changes.
  • presentation/transcript-builder.ts is split into content-text.ts, custom-display.ts and session-projection-engine.ts, each under the presentation module size ceiling; no output changes.
  • src/presentation/ builds the @veyyon/wire/presentation view-models from session state, and PresentationEventBridge turns session events into transcript updates, so a renderer draws a session without importing one.
  • src/modes/terminal/driver.ts implements PresentationContext on @veyyon/tui: it renders every transcript block kind, the status line, the composer and the dialogs from view-models alone, and reports operator input back as UIEvents.
  • InteractiveMode.presentation applies transcript, status, composer and overlay updates to the existing terminal surface without constructing a second terminal or composer.
  • /process-manager opens the Agent Control Center across every conversation this process is running rather than only the one on screen, and a switches the roster, the comms stream and the transcript guard between the two scopes together.
  • read accepts a semicolon-delimited list of internal resources (skill://demo/one.md;skill://demo/two.md), the same list form grep and glob take, and returns one section per entry.
  • @veyyon/coding-agent/session/facade exposes AgentSessionFacade, a thirteen-member session API a front end drives — start, stop, submit, interrupt, retry, per-call tool approval, six event streams and the model, provider and context-usage readings — over a live AgentSession; the facade owns the permission prompt while it is started, and refuses a session another host already routes through.
  • setImageDisplayProbe lets a front end state whether it draws pictures, which is what the session reports to the model about an image a tool returned; a client that installs nothing draws nothing, so a host embedding the engine no longer inherits a terminal's answer.
  • ToolDefinition.view lets an extension describe its tool's call and result cards as a ToolView instead of building a terminal component, and re-exports the @veyyon/view vocabulary (ToolView, StatusRowView, TextBlockView, ViewSpan, ViewTone, ViewStatus) so a plugin can construct one; the terminal draws it, and a tool that also declares renderCall/renderResult keeps those.
  • src/modes/terminal/draw/draw-tool-view.ts draws a ToolView as terminal bytes through exhaustive tone-to-colour and status-to-icon records, so a tone added to the contract fails the type check until the terminal states how it looks.
  • A ToolView span carries file for a filesystem path a host opens however it can, separate from the link a URL rides on, and the tone vocabulary adds diffAdded and diffRemoved for the two sides of a change; the terminal wraps a file run in an OSC 8 link to its file:// URI.
  • Tool domains declare lazy factories in manifest.ts, while tools/view-registry.ts defines their views and policies and tools/renderers.ts derives terminal adapters with shared identities for provider aliases.
  • CustomTool.view lets a tool authored as a custom tool describe its call and result cards as a ToolView, the member ToolDefinition.view already offered an extension, and both adapters forward it to the host.
  • The ask tool emits a host-agnostic HostNotification through ToolSession.notify instead of calling the terminal, and the running host installs its delivery through setToolNotifier; a host that cannot reach an operator outside its own window installs nothing and the capability reads as absent.
  • Source-path comments in modes/terminal/controllers/input-controller.ts, tools/render-utils.ts, utils/block-context.ts and utils/shell-snapshot.ts name the Rust modules they cite at their new paths under natives/. No user-visible behavior changes.
  • A commit whose changes all sit under a grouping directory named natives proposes the directory below it as the scope, the way crates, packages and tests already do, instead of proposing natives.
  • The set_cwd renderer drops name, renderArguments and renderPending: no host reads any of the three, and the tool-renderer contract declares none of them. No user-visible behavior changes.
  • showHookInput's inputOptions.mask is a boolean rather than the character to draw, so the host that paints the field owns the glyph and /secret add no longer names the terminal engine to ask for a hidden field; the masked prompt draws the same • it drew before.
  • The todo tool states where a closed task's completion strike has reached with todoStrikeSplit and writes no SGR escape itself: the terminal draws the struck run with the theme's strikethrough attribute, so a session with terminal attributes off receives the task text without the \x1b[9m bytes it cannot render, and the transcript card sweeps the strike exactly as it did before.
  • runCommitAgentSession reports its run through a CommitAgentReporter instead of writing to stdout itself, and commit/agentic/agent-render.ts is the only module that draws it, so the commit domain names neither the terminal engine nor chalk; veyyon commit prints the same thinking line, markdown, tool tree and totals it printed before.
  • The /collab QR block draws its own leading blank row instead of being presented behind a spacer, so the slash-command registry no longer imports the terminal engine; the transcript shows the same blank row above the code and above the too-narrow hint.
  • The compaction policy vocabulary (check outcomes, the bar a pass is measured against, the truncation edge budget, the prune cache window and idle flush, and the recovery band) lives in @veyyon/kernel/session/agent-session-compaction-policy rather than interleaved with shutdown timeouts and credential backoffs in agent-session.ts, with no behavior change.
  • resolveSlashCommand in slash-commands/helpers/parse resolves a parsed invocation against a command set, joining the first argument token back onto a namespaced plugin command name.
  • SingleResult.isolationFallback records the requested and actual isolation backends and the reason when an explicit mode was not honoured; isolationModeName in task/worktree states a backend's setting spelling.
  • TruncationMeta.totalLinesUnknown states that a read's scan stopped after the shown window, so the notice omits a total it does not have.
  • PLUGIN_MANIFEST_ENTRY_KEYS and PluginManifestEntryKey in extensibility/plugins/loader state the four manifest entry keys a plugin contributes through.
  • cli/stdout-drain exports awaitStdoutDrain and exitAfterStdoutDrain, the one way a non-interactive mode exits after its last frame is written.

Changed

  • Typing a line that does not name a builtin slash command no longer loads the builtin implementations: slash-commands/dispatch.ts answers that from the declarations and loads the handlers only once a name matches, taking the launch graph from 1,642 modules to 1,545 and the interactive input controller from 1,296 to 1,059. No command, argument or completion behavior changes.
  • The 1,806-line builtin handler object moved out of slash-commands/builtin-registry.ts into one module per command category (builtin-setup.ts, builtin-modes.ts, builtin-session.ts, builtin-context.ts, builtin-share.ts, builtin-workspace.ts, builtin-model.ts, builtin-info.ts), with the autocomplete builders in builtin-completions.ts and the category map in builtin-categories.ts; the registry assembles the eight maps and still exports every name it carried.
  • The 860 lines of raw settings migrations moved out of config/settings.ts into config/settings-migrations.ts, which also drops the theme-classifier, builtin-tool-name and compaction-strategy imports from the module most of the product reaches for a setting. No setting, default, order or migration behavior changes.
  • Model target selection moved out of session/agent-session.ts into session/agent-session-model-targets.ts: role resolution, a configured target's model, the compaction candidate list and its configured thinking efforts read only the settings, a model and the available list, so they are callable without a session. No selection, ordering or fallback behavior changes.
  • A running tool card animates one mark: the "… (streaming)" spinner row is drawn only under a header that carries no running spinner of its own, and an agent an eval cell spawned shows the task card's static accent mark instead of a second spinner.
  • The read and write cards parse their arguments and details through @veyyon/utils/fs-tool-args, so a terminal launch no longer evaluates @veyyon/tool-render; the cards draw the same rows.
  • The legacy memories.enabled key is no longer a declared or host-defaulted setting: a config that still holds it migrates to memory.backend on load, the key is dropped on the next rewrite, and the local memory pipeline is enabled by memory.backend: local only. The presentation module's error messages, read-target parsing and cursor clamping use the @veyyon/utils helpers; no behavior change.
  • Reworded the Include Model in Prompt, Max Retry Delay, Hindsight Bank ID, Subagents and Subagent Delegation setting descriptions shown in /settings.
  • The HTML export attaches sub-sessions, redacts and writes the file through one finishExport for the session-manager and file entry points, a stage-1 memory job is completed under its ownership token through one completeOwnedStage1Job for the with-output and no-output paths and the global phase-2 claim reads its row through one readGlobalJobRow, the JavaScript eval cell builds its success, cancelled and failed results through one finish, the GitHub commit watch reports each poll and its grace and confirm notes through one reportWatching, and a capability scope splits (, [ and { groups through one keyed depth table so a closer of another kind never ends the open group; no behavior change.
  • The token-rate helpers are presentation/token-rate.ts, moved from modes/components/status-line/token-rate.ts without a forwarding module, and the status view-model is presentation/status-producer.ts with no presentation/status-builder.ts beside it; the search card limits are exported from tools/search/search-card-limits.ts only, and callMeta and readyPendingSummary from tools/shell/launch-view.ts only.
  • The propose-commit and split-commit tools state the summary and detail limits in their verdict through one verdictWithLimits, Enter and Ctrl+Enter run a builtin slash command and recall the line as typed through one #consumeBuiltinSlashCommand, and a read records its hashline snapshot through one recordFullHashlineContext and renders an over-long first line through one oversizedFirstLineText on the ranged and whole-file paths; no behavior change.
  • Tool-argument repair parses a stringified payload and a parse-sentinel payload through one repairJsonObjectArguments, the launch path resolves the prewalk strong, prewalk cheap and plan-yolo models through one resolveLaunchModel, --resume relocates a session whose recorded directory is gone through one block for local and cross-project matches, and the vault key scans staging entries through one bounded stagedKeyEntries for orphan recovery and crash-stage cleanup; no behavior change.
  • The models-config schema rejects an empty id, name, baseUrl, apiKey, contextPromotionTarget or compactionModel through one narrow helper, the URL fetcher assembles every render result through one buildRenderResult, the patch mode resolves a space-separated outer inner context through one resolveInnerAfterOuter, and the clipboard reads images and text from Windows through one runPowerShell bridge; no behavior change.
  • The plugin manager runs every bun install, bun update, bun uninstall and dry-run through one #runBun, the marketplace manager resolves where a plugin is installed and which scope a verb applies to through one lookup and one #scopeFor, the microphone recorder and its streaming twin walk the detected recorders through one fallback loop, the ZIP64 locator and end-of-central-directory record are parsed by one reader for the in-memory and ranged paths, and head and tail truncation build their single-line results through one helper each; no behavior change.
  • The profile bootstrap reads --profile and --alias through one flag reader, the vault's Linux and macOS atomic exchange share one rollback builder, the eval tool builds its cancelled, failed and completed results through one finishCell, and the lexical bracket scanner opens quote modes from one table; no behavior change.
  • ToolExecutionComponent rebuilds its block through one #syncBlock whenever the spinner frame, the expansion or the freeze changes, including when the spinner starts and stops, so a card that reads context.frame in its view (the vibe composer caret) blinks from its first paint and holds still once stopped.
  • The terminal tool card keeps four behaviors the producer split had dropped: a settled streaming diff preview is drawn without waiting for the next spinner tick, a live-widget call that never ran (ask) falls back to its label, a background task result finalizes its block while the job runs, and the write card coerces non-string content to text; EventController.attachTo seeds the projection engine from the engine's own message source.
  • DapSessionManager.launch and attach share one start sequence, and each breakpoint kind (source, function, instruction, data) replaces its list through one helper for both set and remove; no behavior change.
  • CustomEditor dispatches its twenty app-level shortcuts through one ordered table with the same precedence, deferral during early startup, bash-background fall-through and retry-yields-to-extension rules; no behavior change.
  • Every ModalShell host (tree, session, settings, model hub and model picker selectors, the agent dashboard and transcript viewer, the plan review, login, ask and MCP wizard dialogs, the extension dashboard) routes its close glyph, outside click, breadcrumb and footer chips through one routeModalChrome prelude instead of a hand-typed hitTestModalChrome sequence; no behavior change.
  • Goal and vibe mode context reach the model through one delivery step that keeps the hidden display, the agent attribution and the requested channel; plan-mode context is unchanged.
  • The /btw and /omfg panels settle every outcome (complete, saved, rejected, aborted, error) through one step per panel that clears a previous error and ignores a dismissed panel; the painted rows are unchanged.
  • The reftable ref readers answer null for a missing ref and rethrow an abort through one read-only git query, and the github ops raise the same "unavailable" ToolError for a missing repository root, primary root, branch or head through one check; no behavior change.
  • The legacy extension resolver's six promise caches (package root, manifest, bare dependency, native addon, addon-require scan, realpath) share one memoizer and one importer-scoped key; each result is cached once per key, as before.
  • veyyon setup speech picks the speech-to-text and text-to-speech models through one step that persists only a value the setting declares and flushes it before the next pick; no behavior change.
  • The SearXNG endpoint, token and Basic-auth readers share one settings-then-environment lookup, with an empty endpoint or token still read as unset, and the tool result builder records match, result and head limits through one setter; no behavior change.
  • ACP session/resume opens a stored session through the same step as session/load (same cwd check, same MCP configuration, same not-found error) without replaying history, and the symbolPreset and colorBlindMode settings apply to the theme through one signal hook that logs a fallback or a failed setter under the setting's name; no behavior change.
  • Single-range and multi-range read clip long lines to tools.outputMaxColumns through one display-only clipper that records each clipped line, including a block-context line outside the window, out of the seen-lines record and reports the column cap once; no behavior change.
  • /btw, /tan and /omfg read their argument and clear the composer through one handler; the composer's locked and awaitingApproval flags enter and leave their modes through one step; an agent's extension sendMessage and sendUserMessage are tracked and error-logged through one step; the b, c and down-arrow empty-composer keys install through one listener; and the token_in, token_out, cache_read and cache_write status segments render through one counter; no behavior change.
  • Async job deliveries and in-flight deliveries filter by owner and suppression through one step; the projection engine's running and background-task marks toggle set membership through one step; and skill loading resolves authored and managed skill real paths through one helper, so one SKILL.md reached through two paths loads once without a collision warning; no behavior change.
  • RPC agent and session events pass one typed-set check; every streaming edit mode recovers its matcher paths and entries through one non-empty and one single-path step; and skill, rule, context-file, prompt-template, slash-command and custom-command discovery resolve their project and agent roots through one step; no behavior change.
  • Streamed text and thinking segments reveal through one generic step; both Gemini search request paths build their per-attempt body through one transform step; and the cmux tab's waitForSelector is waitFor; no behavior change.
  • The theme.dark and theme.light setting hooks republish their slot through one factory; the RPC host_tool_result and host_tool_update frames pass one guard parameterised on the frame type and payload key; and the Hindsight retainMode, recallBudget and scoping env and settings values are accepted through one listed-value picker; no behavior change.
  • Built-in model discovery selects its standard and special providers through one predicate (disabled set, configured discovery rows, provider filter) and resolves each provider's discovery endpoint through one lookup (runtime override, models.yml, catalog); the ! and % shortcuts present, stream into, settle and release their transcript block through one step with their own failure labels; no behavior change.
  • The agent executor reads a message's or event's content and usage through one field reader, and a symbol-preset or colour-blind change re-renders the committed theme through one ephemeral re-apply step; no behavior change.
  • The legacy pi-coding-agent read, bash, grep, find and ls tool definitions draw their call card through one step (title, then the call's own summary), with the same bytes as before; no behavior change.
  • Grouped read displays and path expansion share delimiter scanning while preserving escaped separators, glob braces and selector ranges.
  • Learn and Memory Edit result views share text projection while preserving titles, empty results and error formatting.
  • Collab and HTML tool previews build display data without serializing discarded transcript input.
  • Tab expansion imports @veyyon/utils/tab-width; no user-facing effect.
  • The speech-to-text, tiny-model and TTS worker clients share one request lifecycle (subprocess/worker-request-client) and one progress-event shape; a worker error or client shutdown settles every pending request the same way in all three, with no user-facing effect.
  • Collab link formatting and parsing (formatCollabLink, parseCollabLink, generateRoomId) are @veyyon/wire's, re-exported from collab/protocol; a link the host mints and the browser guest parses read one grammar, with no user-facing effect.
  • Claude plugin hooks, tools, skills, slash commands and MCP servers resolve their plugin roots through one pluginRootsFor step, and no loader can mutate the cached root warnings; no user-facing effect.
  • Terminal and presentation event handling share tool-argument correlation and retry traces, with stable block identities across streaming snapshots and session resets.
  • Standalone and adopted terminal drivers share transcript updates and composer bindings with unchanged read-card presentation.
  • Grouped read projections preserve per-call status, paths, previews and line numbers across serialization.
  • Live and rebuilt tool cards share single-card rendering while preserving renderer precedence, streamed arguments and failure notices.
  • Status-line usage fields use the shared presentation contract without changing rendered output.
  • Bash and job cards load shared presentation helpers without importing their execution modules.
  • Plugin discovery shares installed-registry validation and root collection without changing scope precedence or warnings.
  • Runtime initialization no longer waits for first-frame replay cache publication.
  • Eval bridge options share field declarations while preserving exported interfaces and runtime behavior.
  • Settings selectors share list initialization, model-picker attachment and inline updates without changing input routing or persistence.
  • Extension API instances no longer allocate unused flag maps and provider-registration arrays; flags and registrations remain in the shared session runtime.
  • MCP commands share argument parsing without changing terminal and CLI option policies or interpreting argument text as a second command.
  • DAP and LSP configuration discovery share source ordering, and Gemini and OpenCode share user-context loading, without changing precedence or warnings.
  • Editor keybinding matching shares default key sets while preserving remap isolation and fallback chords.
  • Autoresearch experiment tools share active-branch session resolution without changing errors or session selection.
  • Autoresearch session updates share optional-column assembly without changing stored values, omitted-field handling, or database errors.
  • Extension dashboard loading shares disable and shadow precedence across capability kinds without changing state or metadata.
  • settings-selector.ts writes its escape comparison as \x1b instead of a raw control byte. No user-visible behavior changes.
  • Output minimizer tool filters collapse repetitive line filtering, table formatting and subcommand routing into declarative specs with unchanged output behavior.
  • GitHub search operations in the web domain and vibe mode tools use shared parameterized dispatch tables; behavior is unchanged.
  • Terminal selector cards and command controllers share modal select delegation, mouse routing, subcommand dispatch, and markdown panel presentation without changing TUI behavior.
  • Settings selectors share model-chain summaries, heading rows, picker targeting and candidate deletion without changing display or persistence behavior.
  • Parity ledger schema validation, module specifier resolution, and workspace traversal logic are consolidated into shared script helpers without changing validation verdicts.
  • Custom and legacy hook messages use the agent-core type definitions without changing message conversion or public type signatures.
  • Filesystem reads and writes share range calculations, candidate probing and result assembly while preserving SQLite row-operation exports.
  • Terminal code cells and tool views share row splitting, truncation summaries and text sanitization without changing rendered output.
  • Image inspection, Mnemopi initialization and search-provider loading use AI subpath imports while preserving usage-provider and credential-ranking registration.
  • Launch and live status rows share configuration reads, and launch redraws avoid enumerating the settings schema.
  • Settings group reads reuse declaration-path membership without caching setting values.
  • Filesystem and goal previews load without their tool execution modules.
  • Task cards share row construction, nested traversal, finding normalization and yield labels without changing rendered output.
  • Web-search providers share credential resolution and HTTP error handling, and discovery providers share equivalent scanners without changing provider behavior.
  • Tool cards share hidden-count formatting and symbol presets share identical glyph definitions without changing rendered output.
  • Tool cards share error rows, metadata and empty-result construction without changing rendered output.
  • Python, Ruby and Julia kernels share runtime probing and subprocess initialization while preserving language-specific environment and execution behavior.
  • First-frame replay recordings are written asynchronously so filesystem operations do not block editor input.
  • Launch status rendering reuses repository discovery when reading branch and worktree metadata.
  • Launch layout measures the fixed-height status footline without rendering it.
  • Every setting in every domain states what it does and what each value selects in one to three sentences, with no design history or internal vocabulary; the idle timeout row is labelled "Park Idle Agents After", the Park and Prune groups are one "Idle Agents" group, the artifact rows are "Artifact Threshold", "Artifact Head Size" and "Artifact Tail Size" with the unit on their options, and the MCP debounce row is "MCP Notification Delay".
  • AgentRegistry.setStatus consults AGENT_TRANSITIONS and throws AgentTransitionError for a status move the lifecycle does not perform, so a turn event that arrives after a kill or a park no longer revives the agent; a collab guest mirrors the host's roster through mirrorStatus.
  • An agent adopted at hand-over, revived from disk or listed from a previous run receives its idle and prune budgets through one builder, so a zero quiet budget disables pruning and a waiting budget is never shorter than the quiet one on every path; no behaviour changes on the paths that already agreed.
  • A retired agent.* setting is retired in one place: rejectedAgentModelSettings sweeps the keys the schema itself marks retiredBy instead of a second hardcoded list, and a retired key with no page hint is reported against the replacement the schema declares rather than being named as undefined; agentsEnabled is the one predicate for whether agents exist, replacing the delegationEnabled alias that returned it unchanged. No setting changes what it does.
  • Interactive chat and the transcript viewer use one replay implementation without changing displayed content or live-tool lifecycle.
  • Transcript rebuilds and live updates share tool-text projection without changing displayed content.
  • Settings group types derive from the settings schema while preserving their existing optional fields and value types.
  • JSON tree projections share bounded traversal, and code and Markdown cells share output assembly, with unchanged rendering.
  • Plugin discovery, import rewriting, MCP configuration toggles, Exa responses, secret-preserving text transforms, todo state cloning and projection, and session-file discovery use shared implementations without behavioral changes.
  • Tool views share progress rows, diff statistics, disclosure metadata, and task separators without changing rendered output.
  • Extension kind labels use a shared function without changing list or sidebar text.
  • Handbook HTML and search assets are generated during documentation and website builds instead of being tracked in Git.
  • Startup reads setting-change signals and terminal formatting without constructing the settings store or unrelated tool modules.
  • The terminal host constructs autoresearch screens and styled slash-command reports; headless command execution no longer imports those terminal components.
  • Subagent statistics and output sanitization are shared across the task tool's view and dashboard through host-independent presentation helpers in task/agent-stats.ts without terminal imports. No user-visible behavior changes.
  • Account inventory formatting no longer imports terminal progress-bar or theme helpers; displayed values are unchanged.
  • Terminal mode initialization overlaps session discovery instead of loading during sequential runtime warmup.
  • Interactive startup begins background model discovery after terminal initialization.
  • Model registry initialization defers unused provider records until lookup or enumeration while preserving runtime provider updates and model overrides.
  • Startup reads provider caches without constructing unrelated discovery options or their bundled reference maps.
  • Model discovery snapshots from earlier host-classification rules rebuild before model selection.
  • Resolved model snapshots no longer duplicate the bundled catalog; model selection and provider overrides are unchanged.
  • Resolved model snapshots use shared byte-integrity checks and atomic replacement without durability flushes; obsolete snapshots rebuild before model selection.
  • Startup imports AI streaming helpers and usage registration without evaluating the AI package barrel; usage providers and credential ranking remain available.
  • Evaluator implementations load on the first enabled request instead of session or tool construction, and loaded runtimes register their own process-exit cleanup.
  • URL reader initialization is deferred until an enabled URL read or search; local tools and transcript previews no longer initialize it.
  • Vibe tools load on /vibe activation rather than during startup, and failed loading preserves the session toolset.
  • The settings schema registry is @veyyon/kernel/settings/schema and config/optional-number is @veyyon/kernel/settings/optional-number; config/settings-schema composes the domain tables through declareSettings and still exports the queries (getDefault, getType, getUi, hasUi, getPathsForTab, retiredBy, isSettingPath, getEnumValues, isUnsetNumberPath, describeSettingTypeMismatch), the SettingPath and SettingValue types, SETTINGS_SCHEMA, the tab metadata and the typed groups, so importing a query loads the tables it reads. No setting, default or config file changes.
  • Settings (config/settings) extends SettingsStore from @veyyon/kernel/settings/store and passes the product's hook set (the global setting bindings, the SETTING_HOOKS table, the config migrations, the settings.json and agent.db legacy read, the changelog-marker seed and the per-directory resolution of enabledModels); config/settings re-exports the store's vocabulary and SettingSignal under the same names, so every import resolves. No setting, default, config file or write order changes.
  • Settings.getGroup delegates prefix path lookup to groupSettingPaths from the kernel store; model role dictionary normalization is consolidated across layer accessors.
  • The tool session, the terminal mode and the SDK read HostNotifier from @veyyon/host instead of @veyyon/utils/host-notification; behavior is unchanged.
  • session/messages.ts registers its message kinds on @veyyon/session's CustomAgentMessages rather than @veyyon/agent-core's; behavior is unchanged.
  • SessionManager, buildSessionContext, the session file loader and AgentStorage are @veyyon/kernel/session/*; session/messages.ts re-exports the custom-message payload helpers and the rehydration sanitiser from @veyyon/kernel/session/custom-message-payload under the same names. appendMessage accepts every AgentMessage but a branch or compaction summary, the set it accepted before. Behavior is unchanged.
  • BashExecutionMessage, PythonExecutionMessage, bashExecutionToText and pythonExecutionToText are @veyyon/coding-agent/tools/shell/execution-messages, declared on the shell domain's manifest as message kinds; session/messages.ts no longer exports them, and convertToLlm converts the two roles through the kind table the tool registry fills. formatGroupedDiagnosticMessages is tools/core/grouped-file-output, which lsp/utils re-exports. Behavior is unchanged.
  • The plugin loader, the plugin manager, the plugin settings dialog and the extensibility/plugins barrel read the manifest vocabulary from @veyyon/plugin; the barrel exports the same names. No user-visible behavior changes.
  • The website source is apps/site and the Python clients are clients/python, the directories that were website and python; the source-checkout veyyon launcher, the binary build scripts and the brand-parity checks read them there, and nothing they produce changes.
  • veyyon update on a source checkout regenerates the HTML export tool views from clients/web, the directory that was packages/collab-web, and the compiled binary reads the embedded stats dashboard from apps/stats, the directory that was packages/stats; the commands and their output are unchanged.
  • Typed tuple and Set copies use spreads rather than .concat() or .slice(), which those types do not define. No user-visible behavior changes.
  • web_search is constructed from tools/web/search rather than the retired web/search module. No user-visible behavior changes.
  • normalizeRoots takes one working-directory string. No user-visible behavior changes.
  • createCachedComponent lives in modes/terminal/draw/utils with the rest of the terminal drawing helpers, and the source map no longer lists src/web/ or src/swarm/ as top-level directories. No user-visible behavior changes.
  • Tests that lived under retired folder names (test/tui, test/capability, test/web, test/vibe, test/repair, and the other pre-cutover aliases) now sit next to the source they cover. No user-visible behavior changes.
  • ToolView cards that walk a JSON value read their bounds and scalar formatting from json-tree-view.ts rather than loading the terminal walker that draws tree-rail glyphs. No user-visible behavior changes.
  • The terminal's ToolView drawer names every kind the contract declares, so a kind added to the union fails the type check instead of drawing as a one-line text row. No user-visible behavior changes.
  • Session persistence, plugin loading and generic contribution registration use @veyyon/kernel; the turn loop, prompt rendering and tool-specific extensibility remain in the coding-agent package.
  • Terminal drawing of a ToolView lives in src/modes/terminal/draw/, and web search lives in src/tools/web/search/. Subpath imports previously resolved through @veyyon/coding-agent/tui and @veyyon/coding-agent/web/search now name those directories. No user-visible behavior changes.
  • Every tool module is grouped by domain under src/tools/<domain>/, each domain importing only core/ and itself, so the dispatch table no longer sits above 139 flat modules. No user-visible behavior changes.
  • Source-path comments and the gallery search fixture name the terminal renderer at its new path, hosts/terminal/engine, and a new architecture gate records every module outside src/modes/terminal/ that still imports it. No user-visible behavior changes.
  • A source-checkout update runs the native-addon ensure step in natives/bridge/bindings, the bindings package's path after it moved out of packages/.
  • Sixteen tool modules keep their terminal drawing in a <tool>-render.ts sibling — ask, ast-edit, bash, debug, fetch, file-search, job, launch, read, resolve, search-tool-bm25, ssh, structure-search, text-search, todo and write — completing the convention eight tools already followed, and tools/json-tree.ts is tools/json-tree-render.ts. Twenty-four of the twenty-six modules under tools/ that name @veyyon/tui are render siblings, print mode no longer loads the renderer through the todo tool, and subpath imports of the moved renderers follow the new layout. No user-visible behavior changes.
  • All five autoresearch experiment tools — init_experiment, update_notes, certify_arms, log_experiment and run_experiment — describe their call and result cards as a ToolView instead of building terminal Text, so none of them imports @veyyon/tui; the terminal draws the same bytes it drew before, including run_experiment's collapsed five-line output preview and its expanded full output.
  • ToolDefinition.view renderers receive a ToolViewContext stating whether the card is expanded, so a tool can shorten its collapsed output without asking a host what it is.
  • The goal tool describes its call row and result panel as a ToolView instead of constructing terminal components; panel and error formatting are unchanged.
  • The report_finding tool describes its call row and its result row as a ToolView instead of building terminal Text, so it no longer imports @veyyon/tui; the terminal draws the same priority mark, tone and truncation bounds it drew before.
  • The set_cwd tool describes its call row and its result frame as a ToolView instead of building terminal components in a set-cwd-render.ts sibling, which is deleted; the terminal frames the same header, rule counts and no-op wording, and the call row loses the blank row above and below it that Text's default padding added, drawing in the column every other tool's call row draws in.
  • The retain, recall and reflect tools describe their cards as a ToolView instead of building terminal components in a memory-render.ts sibling, which is deleted; the terminal draws the same rows, memory bullets, preview caps and expand hints, and a memory cut to a terminal narrower than the eight-column floor the sibling clamped to now ends in an ellipsis inside its own colour instead of a hard cut mid-escape.
  • The read_url tool describes its call row and its result card as a ToolView instead of building terminal components in a fetch-render.ts sibling, which is deleted; the terminal draws the same panel, metadata rows, preview caps, truncation notice and OSC 8 links, and the line stating how many preview lines were held back now draws in the dim its expand hint already used instead of a lighter muted.
  • The resolve tool describes its call row and its result plate as a ToolView instead of building terminal components in a resolve-render.ts sibling, which is deleted; the terminal fills the same plate in the same colour for an accepted, failed and discarded resolution, and the renderer's inline flag goes with it, since the only reader was the gallery CLI.
  • The debug tool describes its call row and its result panel as a ToolView instead of building terminal components in a debug-render.ts sibling, which is deleted; the panel draws the same session lines, output preview and truncation note, the header states the action after the tool name instead of concatenating both untoned, and the line stating how many preview lines were held back draws in the dim its expand hint already used instead of a lighter muted.
  • The ssh tool renders through a host-agnostic ToolView instead of an ssh-render.ts sibling, and a command or output longer than the viewport is windowed by the rows it occupies after wrapping instead of by its line count.
  • The todo tool describes its call row, its board and its finished line as a ToolView instead of building terminal components in a todo-render.ts sibling, which is deleted; the card draws the same rows, phase names, task marks, completion sweep and one-line summary it drew before, a task's mark and its words carry the tone as two runs instead of one, and a task whose status this build has no case for is drawn as open work rather than reaching the terminal with no colour to resolve.
  • The inspect_image tool describes its call row, its answer panel and its failure card as a ToolView instead of building terminal components in an inspect-image-renderer.ts sibling, which is deleted; the card draws the same question line, four-line collapsed and sixteen-line expanded answer windows, held-back note and sanitised failure text, the settled panel states the outcome on its rail instead of taking the muted legacy edge, and the model and media type ride on the header row rather than a second row beneath it.
  • The search_tool_bm25 tool describes its call row and its match list as a ToolView instead of building terminal components in a search-tool-bm25-render.ts sibling, which is deleted; the card states the same status line, five collapsed matches, label and description truncation bounds and fallback warning, the matches are a list the terminal marks and indents rather than rows carrying their own bullet and dot separators, and the held-back count closes the list with the expand gesture left to the host.
  • ast_edit uses ToolView cards with host-managed expand hints; change content and error formatting are unchanged.
  • The irc tool renders through a host-agnostic ToolView instead of an irc-render.ts sibling, a message states its direction in words (IRC to AuthLoader) rather than an arrow, and createIrcMessageCard and ircToolRenderer are no longer exported from the package root.
  • The write tool renders through a host-agnostic ToolView instead of a write-render.ts sibling, the line-number gutter is three columns wide through a 999-line file instead of widening at each power of ten, and a partial result no longer leaves two blank rows between its progress line and the file.
  • The file_search tool renders through a host-agnostic ToolView instead of a file-search-render.ts sibling, and a path too long for the frame is cut to the frame's width instead of wrapping onto a second row.
  • The text_search tool renders through a host-agnostic ToolView instead of a text-search-render.ts sibling, and the rows of a card with no matches indent two columns under their header instead of starting in column zero.
  • The structure_search tool renders through a host-agnostic ToolView instead of a structure-search-render.ts sibling, and a match group is held whole or dropped rather than cut at the row the budget ran out on.
  • The launch tool renders through a host-agnostic ToolView instead of a launch-render.ts sibling, and each captured terminal row is cut to one row with an ellipsis instead of wrapping onto a second.
  • The search tool describes its dispatching card as a ToolView instead of building terminal components in a search-renderer.ts sibling, which is deleted; a call reaches the same file, text or structure card it reached before, whether its type comes from the call or from the result's details, and the row for a call whose type is still arriving, the row naming a type that is not one of the three and the row for a failure that named no type draw in the column every other tool's row draws in instead of one column past it.
  • The github tool renders through a host-agnostic ToolView instead of a gh-renderer.ts sibling, each failed job's held-back log count closes that job's own log rows instead of sitting under the last job in the group, and one held-back log line reads as 1 more log line.
  • The browser tool renders through a host-agnostic ToolView instead of a render.ts sibling, a spawned browser's binary path is shortened against the home directory, and a rebuilt transcript holding a result without its call states no section for the script it does not have.
  • The read tool renders through a host-agnostic ToolView instead of a read-render.ts sibling, the file is the row's description so the row reads Read: src/example.ts, and the line-number gutter is a constant three columns instead of widening at the 10, 100 and 1000-line crossings.
  • The ask tool renders through a host-agnostic ToolView instead of an ask-render.ts sibling, and an option label keeps the emphasis its author wrote through an inline-Markdown run instead of the tool colouring it.
  • The web_search tool renders through a host-agnostic ToolView instead of a render.ts sibling, and veyyon q's answer cap counts the answer's own lines rather than the rows a terminal wrapped them into.
  • The five vibe tools render through a host-agnostic ToolView instead of a vibe-render.ts sibling, and the composer's caret blinks only on a surface that repaints and never on a message already delivered.
  • The job tool renders through a host-agnostic ToolView instead of a job-render.ts sibling, and a collapsed card closes with one held-back note instead of a count on the closing branch of each tree it cut.
  • The eval tool renders through a host-agnostic ToolView instead of an eval-render.ts sibling, a call is one card whose cells are labelled groups instead of one railed box per cell, and output that rewrote its row with a carriage return shows the row's last text instead of every draft of it.
  • The lsp tool renders through a host-agnostic ToolView instead of a render.ts sibling, a diagnostic is one row of location, severity and message, and references are one row per location instead of a per-file tree two levels deep.
  • The bash tool renders through a host-agnostic ToolView instead of a bash-render.ts sibling, and a sentence shaped like one of the tool's truncation, exit, wall-time, background or artifact notices that the program itself printed now survives instead of being deleted.
  • The edit tool renders through a host-agnostic ToolView instead of an edit/renderer.ts sibling, an edit spanning several files is one card whose sections are the files instead of one card per file, and the change counts are stated at the end of the row instead of a ⟦+3/-1⟧ suffix in the tool's name.
  • The task tool renders through a host-agnostic ToolView instead of a task/render.ts sibling, a call whose result has arrived stops repeating the brief and the agent list, and the live output under an agent is bounded to six rows instead of measured against the terminal's height.
  • formatContextUsage is stated once in @veyyon/utils, which the status-line gauge, the subagent progress row and an eval cell's subagent tree all read, so a tool no longer imports a terminal module to format a token count. No user-visible behavior changes.
  • Tool cards and late diagnostic messages share diagnostic parsing, severity ranking and structured rendering, with home-directory paths shortened in late diagnostic output.
  • formatSessionSnapshot is stated once in debug/session-snapshot.ts for both halves of the debug tool. No user-visible behavior changes.
  • A subprocess tool handler declares renderFinal alone: renderInline had one producer and two readers that both skipped it by name, so nothing it returned ever reached a screen. No user-visible behavior changes.
  • A custom tool's renderCall and renderResult return HostView instead of a @veyyon/tui Component, so the published tool-plugin contract no longer makes every tool plugin a terminal plugin; a renderer that returns a Component still satisfies it unchanged. No user-visible behavior changes.
  • An extension's MessageRenderer and AssistantThinkingRenderer and a hook's HookMessageRenderer return HostView instead of a @veyyon/tui Component. No user-visible behavior changes.
  • The autoresearch setup console and init_experiment clamp breadth and attempts through clamp/clampLow from @veyyon/utils/math instead of local copies of the same arithmetic. No user-visible behavior changes.
  • Time-Traveling Stream Rules, the todo board and thinking-effort resolution are session collaborators under src/session/runtime/, each owning its own state behind a host interface it declares (8, 13 and 12 names), instead of 22 fields spread across AgentSession, which drops from 19761 to 18356 lines. No user-visible behavior changes.
  • #processAgentEvent records the turn's last assistant message once, in its synchronous prologue, instead of twice in the same synchronous stretch. No user-visible behavior changes.
  • The hidden nudge vocabulary is session/nudges.ts alone: the customType names the session injects, the continuation cap, the plan-mode reminder budget, the plan-decision and prewalk trigger tables and the mid-run todo nudge gates, with HIDDEN_MESSAGE_TYPES naming the set. agent-session-message-shapes.ts reads message shapes and declares no type name, and runtime/todo-runtime.ts keeps no copy of the nudge thresholds. No user-visible behavior changes.
  • Source-path comments in prompts/all-registries.ts, prompts/eval-overrides.ts and tools/reroot-hint.ts name the benchmark modules they cite at their new paths under tests/evals/; behavior is unchanged.
  • Imports of the string, escape, keyboard, mouse and motion primitives name @veyyon/utils instead of @veyyon/tui, which no longer re-exports them. No user-visible behavior changes.
  • No file under session/ names the terminal renderer: the session id comes from @veyyon/utils/ttyid and image visibility from the front end's probe, so @veyyon/tui is no longer loaded on the conversation engine's account. No user-visible behavior changes.
  • Source directories are consolidated: memory (memories, memory-backend, mnemopi, hindsight), discovery (tool-discovery, capability), speech (tts, stt) and debugging (dap) each live under one directory, and seventeen single-module directories fold into the concern they belong to. Subpath imports of @veyyon/coding-agent follow the new layout. No user-visible behavior changes.
  • The terminal component tree is grouped by concern: the 94 modules that sat flat in src/modes/terminal/components/ now live under transcript/, composer/, selectors/, dialogs/, chrome/, dashboard/ and account/. Subpath imports of @veyyon/coding-agent/modes/terminal/components/* follow the new layout. No user-visible behavior changes.
  • The interactive terminal lives under src/modes/terminal/, and the modes that draw no terminal — print-mode.ts, rpc/, acp/ and the magic-keyword parsing in keywords/ — sit beside it rather than inside it. The palette moves to src/theme/ and extension state aggregation to src/extensibility/extension-state/, both of which a headless run reads. Subpath imports of @veyyon/coding-agent/modes/* and @veyyon/coding-agent/theme/* follow the new layout. No user-visible behavior changes.
  • Goal mode, the working line and push-to-talk are their own modules under src/modes/terminal/controllers/ instead of living inline in the interactive mode, which drops from 5605 to 4495 lines. No user-visible behavior changes.
  • The session runtime's module-level declarations move out of src/session/agent-session.ts into six siblings — agent-session-types.ts, agent-session-permissions.ts, agent-session-queue.ts, agent-session-retry-fallback.ts, agent-session-compaction-policy.ts and agent-session-message-shapes.ts — so importing the session event union or config record no longer pulls in the 20909-line runtime, which drops to 19704 lines. Subpath imports of @veyyon/coding-agent/session/agent-session follow the new layout; the package root barrel still exports every moved name. No user-visible behavior changes.
  • The session factory's free declarations move out of src/sdk.ts into six siblings under src/session/ — factory-options.ts, factory-extensions.ts, factory-prompt.ts, factory-tools.ts, factory-mcp.ts and factory-notices.ts — so naming CreateAgentSessionOptions or calling a discovery helper no longer imports the composition root, which drops from 4861 to 3832 lines. Subpath imports of @veyyon/coding-agent/sdk follow the new layout; the package root barrel still exports every moved name. No user-visible behavior changes.
  • Multi-target ast_grep searches now execute concurrently while preserving globally ordered paging, totals, parse errors, cancellation, and target-order failures.
  • The vibe screens, the image-inspection call and an LSP hover code block draw no border of their own inside a tool block, so a block keeps one left edge; a tree connector remains only where a row belongs to the row above it, in the eval value tree, the grep line gutter, the job tree and the LSP reference tree.
  • The legacy Pi bundled-module generator states which package roots a compatibility shim serves, so the compiled-mode override sweep derives that set instead of carrying its own copy; behavior is unchanged.
  • The /agents dashboard uses a centered, bordered card with bracketed tabs and explicit agent status words.
  • The status row reads its truncation limits from tools/core/render-limits, a leaf that imports nothing, rather than tools/core/render-utils, which drops the tool renderers, path helpers and image resizing from the launch card's import graph; first-frame time is unchanged, because those modules only declare functions.
  • An MCP tool describes its call and result cards as a ToolView instead of building terminal components in mcp/render.ts, which is deleted; the terminal states the same arguments, structure walk, raw rows, held-back count and spill warning, indented two columns under the row that heads them and without the branch glyph the call row opened with.
  • SETTING_KIND_HANDLERS is typed per setting kind, so each handler receives the definition variant its key selects instead of the whole union widened to any. No user-visible behavior changes.
  • Subagent drive paths share abort-aware promise handling with unchanged cancellation behavior.
  • Hook and user-message selectors share search editing without per-keystroke callback or query-array allocations.

Fixed

  • Fixed the daemon broker client and broker startup exiting 0 silently mid-read: Bun.file().text()/.json() does not ref the event loop, so a process that reached the token, lease, presence, or metadata read with nothing else pending could drain and exit before the read settled; the reads now use fs.readFile.
  • A pasted OAuth callback typed after a slash command that takes no arguments is consumed instead of being sent to the model as a prompt.
  • Automatic maintenance cuts an oversized body until a summarization request fits a summarizer, instead of parking the session with "Compaction freed too little context to make progress" when every candidate was skipped for holding fewer tokens than the summary needed.
  • Print, JSON and RPC mode flush Bun's stdout sink before exiting, so a piped consumer receives the whole last frame instead of losing up to 1 MiB of queued output.
  • A spawned agent's card shows its resolved-model badge again on the live block, the registry path an extension wraps and the rebuilt transcript, per agent.showResolvedModelBadge.
  • With VEYYON_FORCE_IMAGE_PROTOCOL=sixel and VEYYON_ALLOW_SIXEL_PASSTHROUGH=1, a bash card draws an inline Sixel image row as the program wrote it instead of blanking it.
  • The extension graph walk reads each module file from disk once; the CommonJS check on a candidate reuses the walk's read instead of opening the file again.
  • veyyon --help summarises worktree with the profile-scoped path ~/.veyyon/profiles/<name>/wt, matching the command's own description.
  • Legacy hindsight.dynamicBankId and hindsight.agentName written as flat keys migrate to hindsight.scoping and hindsight.bankId and are dropped on rewrite instead of surviving as dead entries.
  • Antigravity turns from a consumer Google account (aicode-consumers) no longer fail with 429 RESOURCE_EXHAUSTED on every request: the backend rejects any system instruction carrying the upstream oh-my-pi conventions header, so the preamble now opens Keywords follow RFC 2119: instead of RFC 2119:.
  • A Gemini or Antigravity 429 whose body is Google's generic Resource has been exhausted (e.g. check quota) / RESOURCE_EXHAUSTED is a per-minute throttle and retries on the same account after 45–75 seconds instead of being read as a 30-minute daily quota that exceeded retry.maxDelayMs and ended the turn; the "exhausted your capacity … quota will reset" body still reads as the daily wall.
  • Removing the retired providers.parallelFetch key no longer leaves an empty providers: section in the rewritten config.
  • A nested legacy task.isolation.enabled no longer overrides an explicit task.isolation.mode during migration.
  • Corrected comments that named a distribution channel or runtime API the project does not use; no behavior change.
  • A subagent that inherits the session's model shows that model and effort in its Subagents row, the task widget and the /agents roster, and keeps the badge when a follow-up turn wakes it.
  • An inherited subagent model's badge prints the effort the session settled on, so a parent running auto shows the resolved level rather than auto.
  • A subagent's row label is generated with the session's live model when no tiny, commit or smol role is configured, as a session title is, instead of the persisted default role.
  • A compaction candidate whose single-request summary timed out and was completed in stages starts the next compaction on that model staged, instead of waiting out the same timeout first.
  • Command-mode detection recognizes shell and Python prefixes after Unicode leading whitespace in loaded drafts.
  • An MCP server that repeats a pagination cursor or never ends its pages no longer hangs resources/list, resources/templates/list or prompts/list; every list method ends with a warning and the pages collected so far, as tools/list already did.
  • Tool result cards preserve raw-string and multipart text, including MCP output and grouped read previews, while runtime result cards select eval or launch from their call arguments.
  • Collab reconnect snapshots preserve completed tool result text and display metadata across current and historical tool-call record formats.
  • HTML export preserves provider fallback transitions and redacts their model text in primary and nested transcripts.
  • Empty job results render their success or error message for every registered job alias.
  • The website model catalog refreshes from its relocated repository path before using the deployed copy.
  • Release validation checks native sentinels under every discovered workspace root, and shortened release notes link to the complete tagged changelog.
  • Terminal input delivery continues to later subscribers when an earlier subscriber throws.
  • Tool cards retain wire-provided results during expansion and sealing, and restore expanded output when subsequent updates contain raw results.
  • Clearing or replacing presentation rows stops detached animations, preserves completed-call history, and keeps subsequent reads visible.
  • Stopping a terminal presentation driver releases card animation clocks even when its engine is not running.
  • Extension discovery resolves installed plugins from the session's selected profile.
  • Python composer borders retain their mode color when an agent view is focused.
  • Completed and rebuilt tool cards retain their call arguments and rendered call details.
  • Disposing a tool card stops its animation clocks and detaches its presentation subscription.
  • Legacy tool result renderers receive the completed call arguments for partial and final results.
  • Background provider usage refreshes repaint the status line without requiring further input.
  • Tool cards retain both call and result renderer failure notices with their fallback output.
  • The library entry point retains read-selector functions, write-preview helpers, filesystem-source extraction and the goal view after renderer separation.
  • An auto-compaction pass that finds nothing to summarize warns about a compaction dead end only while the context is still over the recovery band, so a pass that runs after maintenance already freed the context no longer advises starting a fresh session.
  • The MCP add wizard retains manually entered credentials for stdio, HTTP and SSE servers and opens OAuth settings after authentication errors.
  • Retry and Edit OAuth settings accept pointer input immediately after OAuth failure or cancellation in the MCP add wizard.
  • Kernel codec and shutdown defaults remain active when optional configuration fields are undefined.
  • Startup frame capture stops retaining terminal output after typing, recording settlement, or frame release.
  • Disabled launch status rows remain blank when the terminal is narrower than the composer inset.
  • veyyon licenses reads the Open Sans and Source Code Pro notices from the tracked docs/handbook/fonts/ copies instead of the ignored mdbook build output, so the bundle regenerates and its test passes on a checkout without a handbook build.
  • A compaction that had just freed context no longer reports "Compaction freed too little context to make progress" when its entry is written in the same millisecond as the kept assistant turn; whether a turn predates the latest compaction is read from its position on the branch, not its timestamp.
  • The home anchor sizes its fills, the welcome hero mounts, and the per-frame sizing pass runs on the screen the interactive mode renders on rather than the one its constructor built, and startup.quiet is read from the session's settings like the other startup reads.
  • An agent whose session is on screen is no longer parked under it: opening an agent pins it for as long as the main view points at it, a park deadline that elapses meanwhile is deferred, and the idle TTL counts again from the return to the main session.
  • The task card lifts the missing-yield warning out of an agent's output under both its current spelling, SYSTEM WARNING: Agent exited without calling yield tool, and the Subagent spelling a session file recorded before it; the task:subagent:progress and task:subagent:lifecycle bus channels a collab guest matches on keep their spelling.
  • Custom tools retain their declared call and result views when converted into extension tool definitions.
  • Terminal tool cards shorten home-directory paths and replace tabs in metadata, notices, code, diffs, and generic argument previews before width fitting.
  • Failed task results without agent details retain error text styling.
  • Launch model context estimates exclude project context and clamp the remaining percentage to zero.
  • Launched processes report completion even when they exit before the completion watch registers.
  • Autoresearch best-result and confidence calculations exclude unmeasured placeholder zeros while retaining measured zero values.
  • Configured shortcuts apply from the first editable frame, and model-selector shortcuts entered during startup preserve the draft through initialization.
  • The launch composer repaints typed input before runtime initialization continues and preserves the mounted editor's change handler.
  • Ordinary CLI launches no longer evaluate ACP authentication, and startup applies the configured theme once.
  • Submissions entered during startup execute after initialization without waiting for a model turn, preserving later drafts and attachments through command handling and editor replacement.
  • The launch composer shows the same linked-worktree location and draft-token estimate as the mounted session, avoiding a late footline repaint.
  • bun run gen:tool-views in the coding-agent package uses the relocated clients/web generator.
  • A web_search refusal, missing-credential, or provider-failure result is marked isError, so the model sees the failure as a failure.
  • A tool result whose image-visibility notice is unchanged is memoized across turns instead of allocating a new wrapper each time.
  • edit and apply_patch share one renderer object again, so a host that groups by identity (streamed-arg keys, the first-result replay) treats them as the same card. No user-visible change.
  • A debug adapter command resolves against the debug working directory only when it is spelled as a relative path (./tool, ../tool), restoring the prefixes a mechanical path rewrite had widened to any leading dot.
  • An overflow whose summary cannot fit any model no longer waits on a restore that never runs: the dead-end rescue truncates the oversized message to an artifact and the turn is retried, which the overflow-refusal suite now pins.
  • A live tool card drawn from a ToolView spins again: a view is handed the frame it draws, so the spinner tick now rebuilds the card instead of only repainting it, and the streaming glyph on the browser, debug, eval, launch, ssh and vibe cards no longer sits frozen on the frame it was born with.
  • The debug, resolve, retain, recall and reflect renderer entries declare the inline policy they declared before their cards became views. The flag reaches no reader in the terminal today, so nothing on screen changes.
  • A tool card's header keeps the subject it states: the middle-cut the edit card needs was applied to every framed card, so ast_edit, file_search, text_search, structure_search, search_tool_bm25, inspect_image, launch and ssh cut a query, a glob or a path to … in a narrow terminal to keep counts that already fitted. A card now asks for the cut, and the edit card is the one that asks.
  • A live card whose tool describes a ToolView obeys the merge policy its renderer entry states: debug, resolve, retain, recall, reflect and ssh drew the call row above the result card while the call was on screen, and drew one card when the transcript was rebuilt.
  • veyyon config set and veyyon config reset create the profile's config directory before taking the file lock, so a first write into a profile that has no directory yet persists the setting instead of exiting 1 with ENOENT.
  • goal-tool.ts no longer imports a ViewSpan type it stopped using when the goal card became a view. No user-visible change.
  • A structure in a tool card stops at the nesting level its bound names: the view walk numbered a root key 0 where the terminal walk numbered it 1, so every task, eval and argument card showed one level deeper than the collapsed and expanded bounds allow.
  • A JSON structure in a tool card states 1 more line where one line of a long string is held back, instead of 1 more lines; the view walk wrote the phrase by hand where the terminal walk it replaced counted through the shared formatter.
  • The launch shell no longer evaluates the prompt registries: modes/keywords/{orchestrate,ultrathink,workflow}-keyword.ts kept imports of prompts/subagent/rows, prompts/turn-control/rows and the @veyyon/utils barrel after the notices they fed moved to magic-keyword-notices.ts, which pulled 62 extra modules into the first frame's import graph.
  • The subagent model resolver's own precedence list no longer points at a fifth case it lost when subagent.model became the shared-switch layer. No user-visible change.
  • HTML export works again: the five assets export/html/index.ts loads as strings — the markdown renderer, the template CSS, HTML and JS, and the generated tool views — lost their with { type: "text" } import attributes while unreleased, so each one loaded as a module and every export threw Missing 'default' export in module.
  • Seven published entry points the reorganization dropped are served again at their new paths: ./modes/terminal/components/*, ./modes/terminal/components/extensions/*, ./modes/terminal/components/status-line/*, ./modes/terminal/controllers/*, ./modes/terminal/setup-wizard/*, ./modes/terminal/utils/* and ./export/markit/*. Every subpath 1.3.0 published now has a successor key.
  • A source build of the compiled binary reaches the native addon package at natives/bridge/bindings, its path since the bindings moved out of packages/, instead of failing on a chdir to packages/natives.
  • Tools are offered in BUILTIN_TOOL_NAMES declaration order again: composing the registry from the five domain manifests reshuffled seven names, and tool order is prompt order, so every cached prompt prefix was invalidated for a session whose tool set had not changed.
  • The source launcher (scripts/veyyon, scripts/veyyon.cmd) finds the native addon at natives/bridge/bindings/native and provisions it through that package's ensure-native.ts, instead of dying at boot on packages/natives/scripts/ensure-native.ts after the bindings moved out of packages/.
  • The compiled binary builds again: its legacy-extension module surface resolved each bundled package by its directory name under packages/, so the build died on packages/natives/package.json once the bindings moved to natives/bridge/bindings and the terminal engine to hosts/terminal/engine. Each bundled package is now found by the name its manifest declares, wherever the member sits.
  • The compiled binary builds through its legacy-extension shims again: the @veyyon/ai and TypeBox compat shims were named by hand-written paths under packages/coding-agent/src/extensibility/, so the build reported two unresolvable entrypoints once both shims moved into @veyyon/kernel. Each shim is now resolved through its owning package's published subpath, at build time and at extension load.
  • The launch card states the context percentage recorded at the end of the last launch instead of ?, and falls back to ? when the release, the model or the project changed.
  • A debug adapter that exits during startup now reports its own stderr — pip install debugpy for a missing debugpy — instead of EPIPE: broken pipe, send.
  • A submission arriving in the tick after the terminal asks for input is no longer dropped, and loop mode's auto-submit timer no longer arms a tick late: the goal-mode exit check ran as an await on every turn, and an async guard that returns early still yields before the input callback is installed.
  • A run that draws nothing no longer tells the model its picture is on screen. -p, rpc and acp emit text, and the sentence attached to an image a tool returned was written from the launching terminal's graphics protocol, so the same read in a Kitty window reported the image as displayed to a pipe; the front end now states whether it draws, and a client that says nothing is taken at its word.
  • A debug launch whose interpreter refuses now reports the interpreter's own exit code and stderr instead of EPIPE: broken pipe or DAP adapter disposed: writing the first request onto an adapter that has just died fails at the pipe, and the pipe's error names nothing the user can fix.

Release notes were shortened from 430,143 characters to fit GitHub's 125,000-character body limit. Read the complete changelog and full commit range.