- Replaced the legacy model selector with a full-screen Model Hub, introducing mouse support and a fuzzy-searchable browser.
- Integrated comprehensive model management, including role assignment, thinking-level visualization, and manual provider discovery.
- Implemented a cancellable OAuth login flow and integrated it directly into the Model Hub for provider authentication.
- Centralized model logic and migrated existing tests to support the new component architecture.
- Transitioned DeepSeek models to an explicit `[High, Max]` effort ladder, removing stale alias maps.
- Updated the OpenAI compatibility layer to enforce authoritative `supportsReasoningEffort` and `omitReasoningEffort` flags.
- Synchronized `models.json` definitions to reflect accurate reasoning effort capabilities across the catalog.
- Introduced `Max` as a first-class reasoning effort tier across all packages, including AI providers, coding agent configurations, and RPC protocols.
- Refactored model effort ladders to use wire-exact mappings and removed legacy effort aliasing (e.g., `max-to-xhigh` mapping).
- Updated model registry and provider configurations to support `Max` tier routing, color themes, and UI icon associations.
- Expanded test suites to provide end-to-end coverage for the new reasoning tier, including updated compatibility and fallback scenarios.
Timer-driven reveal and spinner ticks (streaming reveal, tool-args reveal,
tool-execution spinner, todo strike animation) now hand the changed
component to `TUI.requestComponentRender(component)` instead of forcing a
full-tree render at 30fps. Every other root subtree reuses its previous
frame rows, cutting the Box/Container tree walk out of the compose
pipeline while the transcript grows.
Shimmer:
- Intern the working-message palette per accent (WeakMap-keyed) so the
Symbol-slot compiled-ANSI cache in `shimmerSegments.compile` actually
hits between frames — the fresh palette literal in `renderWorkingMessage`
guaranteed a per-tick miss.
- Add an `activeBand` fast-path: outside the sweep window the intensity
is guaranteed zero, so those code points coalesce into a single low-tier
run without running `intensityFn` or `tierFor`. On the typical ~60-char
working message the classic band is 12 cells wide, so ~80% of the per-char
loop disappears.
Widen `ToolExecutionHandle` to extend `Component` (matches every
concrete impl — `ToolExecutionComponent`, `ReadToolGroupComponent` —
which already extend `Container`) so the reveal controller callback
sites are type-checked.
Fixes#4377
- Introduced a dedicated advisor rail symbol to differentiate note cards from thinking output.
- Applied bold custom header tags and severity-tinted rails to distinguish advisor notes from surrounding text.
- Shifted note body text to the default content color to prevent blending with dimmed thinking-output styles.
Four tightly-scoped hot-path fixes covering the highest-impact items in the
reporter's CPU profile (13.1 s profiled / 30 s window):
1. `event-controller.ts:handleEvent` no longer fires a blanket
`statusLine.invalidate() + ui.requestRender()` before every session event.
The pre-render was a leftover from #4145 when `updateEditorTopBorder()`
still eagerly rebuilt the border; the lazy provider added in #4145 made
it redundant. It fired on every `message_update`/`tool_execution_update`
during streaming — the pre-render's frame ran while the handler was
awaiting, then the handler's own `requestRender` scheduled a second
identical frame. Every handler that mutates visible state already calls
`requestRender()`.
2. `shimmer.ts:shimmerSegments` iterates the segment string in place instead
of building a code-point array with `Array.from(seg.text)` every animation
frame. Runs of same-tier chars are emitted via a single `slice` per run
rather than accumulating into `runBuf`. Surrogate pairs stay atomic — the
code-point index still advances by 1 per emoji. Microbench over 30k
frames: 45 ms → 18 ms (2.53x), allocation rate down from ~N-per-frame to
a handful per frame. New tests cover mixed BMP+surrogate and all-emoji
inputs. `Array.from` was the #1 self-time hotspot in the reporter's
profile at 10.2%.
3. `Markdown.setText` gains an equality guard mirroring `Text.setText`
(returns `false` when `text === #text`). Providers re-emit identical text
on ticks with no delta (throttled frames, reconciled tool-execution
updates); each of those now short-circuits instead of dropping
`#cachedLines` and forcing a full lex + wrap on the accumulated paragraph
(the reporter's #3 hotspot at 8.4%). New test asserts render-reference
stability + return-value semantics.
4. `SPINNER_RENDER_INTERVAL_MS` aligned with `SPINNER_GLYPH_ADVANCE_MS`
(both 80 ms). The previous 33 ms cadence emitted ~2.4 paints per glyph
step; the differential-output dedup only skips the write, not the
compose walk. Visually identical (glyph advance was already 12.5fps),
halves paints during tool execution.
Skipped (out of scope for a bug fix, deserve dedicated PRs):
- Freezing streaming prefix on single `\n` boundaries — correctness-bound
to `\n\n` block separators (CommonMark loose-list continuation).
- Compose-phase idle gate + adaptive-backpressure moving-average — need a
component-level dirty flag; the 200 ms cap in `#scheduleRender` was set
for a reason (#4145 tail-latency guard).
Tests updated: two IRC-expiry tests in event-controller-message-start.test.ts
that were asserting the pre-render's second `requestRender` call now expect
one.
Fixes#4353
- Added `git.repo.linkedWorktreeSync` to identify and resolve git worktree metadata without spawning subprocesses.
- Updated `StatusLineComponent` to detect linked worktrees and resolve project/worktree context names.
- Modified path segment rendering to collapse nested git worktree paths and display the worktree name when it diverges from the active branch.
- Introduced `icon.worktree` symbol across themes to visually distinguish git worktree paths.
- Enhanced the edit renderer to support visual tracking of delete and move/rename operations.
- Updated diff computation to correctly handle file-level changes and suppress erroneous "No changes" warnings.
- Improved terminal output with a clearer activity indicator and accurate state representation during multi-file operations.
- Extended the rendering pipeline to display source-to-destination paths for file renames and added validation tests for edit workflows.
- Introduced `web-palette.ts` to implement the collab-web pink/purple brand identity for HTML exports.
- Updated `generateThemeVars` to support a `palette` option, allowing users to choose between the brand-web aesthetic and a specific TUI theme.
- Configured public exports and the share-viewer script to default to the brand-web palette rather than inheriting the user's terminal theme.
- Refactored `AgentSession.exportToHtml` to align with the new branding defaults while allowing for per-export theme overrides.
- Adjusted `dark.json` background values to ensure better visual consistency across internal surfaces.
- Switch all UI components and tests from sharp box corners (`boxSharp`) to rounded ones (`boxRound`).
- Update `Theme` to re-export sharp junction symbols (tees and cross) under `boxRound` to ensure consistent divider rendering in rounded boxes.
- Remove outdated architectural notes regarding forced tool-choice queues in documentation.
- Added `display.cacheMissMarker` setting to enable visual indicators for prompt cache invalidation in assistant messages.
- Included updated theme symbols to represent cache misses across supported icon sets.
- Configured the selector controller to rebuild the chat display when the marker visibility setting is toggled.
- Added `typeof theme === "undefined"` guards to all four theme getters.
- Fallbacks return a usable plain-ASCII or plain-text theme instead of crashing.
- Added a pre-init test asserting the getters never throw.
- Added `#disposed` flags and guards to `StatusLineComponent` to stop async callbacks running post-dispose.
- Updated `onThemeChange` to return an unsubscribe function and registered it in `InteractiveMode`.
- Fixed schema validation for `spinnerFramesSchema` to narrower types instead of dynamic or-validation.
- Resolved settings leaks by saving and restoring `tuiTight` state in tests.
- Added regression tests verifying that pending async microtasks do not trigger updates after disposal.
- Migrated all wire protocol, schema definitions, and tools validation from Zod to ArkType across multiple packages.
- Updated extension runtimes, custom tools loader, and TypeBox compatibility shim to expose and use ArkType instances.
- Added a comprehensive ArkType migration guide, validation parity tests, and helper utilities.
- Removed redundant PDF asset routing and parsing implementations from the read tool.
- Removed render_mermaid from tool discovery, task definitions, and registries.
- Removed renderMermaid setting and prompt/docs references tied to the deleted tool.
- Added maxWidth and theme color options to Mermaid ASCII resolution in markdown flow.
- Re-rendered Mermaid ASCII in both directions and clipped output to available width.
Derived HTML default text from the resolved export card/page surface, falling back to the derived user-message export base when themes omit export overrides.
Covered light-status custom themes whose dark userMessageBg drives derived export backgrounds.
Fixes#2516
ToolExecutionComponent.#updateDisplay() re-ran renderResult (O(result-size)) on every invalidate - spinner ticks, stream chunks, resizes, keystrokes - so large results blocked the loop for seconds and typing lagged. It now early-returns on an unchanged dirty key (result version, expanded, partial, spinner frame, image visibility, theme epoch), and theme.ts exposes getThemeEpoch() bumped on every theme swap so cached blocks re-render on theme change.
The HTML session export rendered empty text tokens (text, userMessageText,
customMessageText, toolTitle) as the dark-theme grey #e5e5e7 on every theme
whose name was not literally 'light'. getResolvedThemeColors picked
defaultText off name === 'light', and the standalone isLightTheme helper
classified on userMessageBg — both diverged from Theme.isLight, which has
classified on statusLineBg since porcelain proved userMessageBg unreliable.
Factor the status-line luminance into isLightThemeJson(themeJson) and route
both helpers through it. defaultText now falls back to #000000 on every
light theme (sandstone, limestone, porcelain, light, …) and to #e5e5e7
elsewhere, matching Theme.getColorHex.
Regression test extends test/theme-islight.test.ts with the standalone
helper and getResolvedThemeColors HTML-default coverage for sandstone +
dark.
Fixes#2516
- Added a SessionFocusController to switch transcript and input context between main and subagent sessions.
- Added agent-hub Enter activation and double-left return behavior for focused local agents.
- Added view-session-based event and render logic to avoid stale focus-session state.
- Added status-line focused agent display with ghost icon and focused-mode border dimming.
- Added `handleUsageResetCommand` support to list and redeem usage reset credits.
- Refactored `/usage` into `show` and `reset` subcommands and removed `/reset-usage`.
- Handled ACP/TUI `/usage` flows so `show` reports usage and `reset` redeems credits.
- Kept selected theme setting values dirty-colored while selected in the UI list.
- Added `parentToolCallId` and `index` fields to subagent session records and propagated them from task lifecycle and progress events.
- Reworked subagent ordering to sort by parent-group order, spawn index, and stable creation order so out-of-order updates no longer reshuffled active HUD rows.
- Updated task execution to pass spawn indices through sync and async paths, switched the `tool.task` icon to Octicons tasklist, and added a registry test for out-of-order progress ordering.
- Added mouse hover, wheel, and click routing for `/settings` rows and tabs.
- Changed `/settings` to open as a fullscreen alternate-screen overlay.
- Added hover, hit-testing, and wheel APIs across TabBar, SelectList, and SettingsList.
- Added SGR mouse parsing plus `routeMouse` dispatch and exported it from the `tui` entrypoint.
Also normalizes setting labels/descriptions and carries the package changelog entries for this batch (adjacent unreleased bullets are not splittable per commit), restoring section placement disturbed by the two prior changelog hunks.
- Added a new `icon.job` symbol entry to all symbol sets and exposed it on the theme icon map.
- Updated the status line to render running background jobs with the new job icon and count instead of the previous text label.
- Reordered the job status segment insertion so the job indicator is added to the leading position in the right-side parts.
The status-bar auto-thinking pending indicator used `?`-bearing symbols
in all three presets (`▣?` Unicode, nf-md-help_box Nerd Font, `[?]`
ASCII), which is visually indistinguishable from a terminal missing-
glyph fallback. Users assumed the icon was broken and toggled presets
to no effect.
Replaced each preset with an unambiguous loading indicator:
- Unicode: `⟳` (Clockwise Open Circle Arrow)
- Nerd Font: `\uf1ce` (fa-circle-o-notch)
- ASCII: `[~]`
Fixes#2267
The session-observer overlay is gone. The Agent Hub (ctrl+s, alt+a, or double-tap left arrow on an empty editor) presents one overlay with two views: a live registry table (status, unread irc count, current task, last activity; j/k to navigate, r to revive, x to abort/release) and per-agent chat (transcript + input line) — submitting revives a parked agent and steers it via the normal prompt path. Main is the ambient chat and stays out of the table.\n\nrenderInitialMessages no longer takes a prebuilt context: every redraw now reaches for AgentSession.buildTranscriptSessionContext() (full-history transcript with each compaction emitted inline at the point it fired, snapcompact frames re-attached on rebuild). UiHelpers drops the deferred-compaction render and the IRC autoreply branch that the new mailbox bus deprecated. CompactionSummaryMessage renders as a slim divider (── 📷 compacted · ctrl+o ──), expanding to the summary + snapcompact frame count. session-manager.buildSessionContext gains a { transcript: true } mode; an exported buildSessionContextFromFile() reads a session file without taking the writer lock so the hub chat view can tail any agent (parked or live). Theme picks up icon.camera + tool.irc symbol entries.
- Dark themes: warm hues (0-120), Light themes: cool hues (180-300)
- Collision check against 17 major theme colors with 10° threshold,
clamped to the hue band so it never drifts into an unrelated range
- Hex values stored at construction instead of round-tripping through
ANSI escape parsing (removes #ansiToHex)
- WCAG AA contrast bisection on light surfaces
- Per-session uniqueness via name hash within the hue band
- Added status.done and tool.* symbols to theme mappings and presets.
- Replaced generic success glyphs with contextual +/-, tool icons, and warnings.
- Mapped tool/task/job completions to status.done or status.enabled with icon overrides.
- Triggered runtime provider refresh after extension registration and warned on failure.
- Reworked classic and KITT shimmer intensity math to drive the scan head by a constant 30 cells/second.
- Introduced length-aware travel periods for classic padding and KITT ping-pong motion while removing the prior fixed-duration phase helpers.
- Updated shimmer tests to validate crest movement stays within one cell per 30fps frame and is consistent across text lengths.
- Synchronized shimmer progression by switching `shimmer.ts` to a shared 2-second phase sweep, with crest/head position interpolated across the full visible span.
- Updated classic and KITT intensity calculations so shimmer starts at index 0 and reaches index length-1 together for any task row length.
- Adjusted shimmer tests to assert crest boundary and phase alignment behavior across short and long strings.
- Updated classic shimmer to compute band position from elapsed time at 30 cells per second instead of a fixed sweep duration.
- Updated KITT shimmer to use fixed-speed ping-pong motion over the same 2xrange cycle so round-trip timing scales with message length.
- Reduced shimmer-driven UI animations and borders to a 30fps cadence.
- Collapsed consecutive read tool calls into one finalized transcript block.
- Tracked visible assistant content to finalize and reset read groups correctly.
- Updated running task rows to static bullet markers with shimmer-only subagent labels.
- Added `icon.search` to theme symbol maps and used it for Search, Find, AST Grep, and BM25 success headers.
- Reworked `searchToolBm25Renderer` results into framed bullet lists with expand-item hints.
- Updated renderer tests to match the new search-tool output contract.
Move the color math added for the session-accent fix into the shared
@oh-my-pi/pi-utils color module instead of a coding-agent-local file:
- colorLuma, relativeLuminance, and hslToHex now live in
packages/utils/src/color.ts (hslToHex lifted out of session-color.ts).
- Drop the duplicate hex parser: toRgb reuses the existing hexToRgb and
paletteToRgb returns the shared RGB type, so hex parsing lives once.
- Delete packages/coding-agent/src/utils/color.ts; theme.ts and
session-color.ts import from @oh-my-pi/pi-utils.
- Move the color unit test into the utils package; repoint session-color
test imports.
No behavior change to accent luminance capping.