Right-click any workspace file, folder, or root now shows
'Open in VS Code' alongside the existing Reveal in File Manager action.
- POST /api/file/open-vscode: resolves path via safe_resolve, finds VS
Code via shutil.which() with fallbacks for macOS (/usr/local/bin/code,
app bundle CLI), Linux (/usr/bin/code, /snap/bin/code), and Windows
(%LOCALAPPDATA% and %PROGRAMFILES% user/system installs). Returns a
descriptive error if not found rather than a bare OS error.
- Optional vscode block in config.yaml: command (default: code),
host_path_prefix + container_path_prefix for Docker path mapping.
- i18n: open_in_vscode and open_in_vscode_failed translated in all 10
locales (it, ja, ru, es, de, zh-CN, zh-TW, pt, ko).
- 26 tests in tests/test_2735_open_in_vscode.py covering source wiring,
command resolution, i18n completeness, and live endpoint error paths.
Two confirmed bugs in the thinking/reasoning display:
1. reasoningText was initialized once when the SSE stream opened and never
reset between turns. On the done event, the last assistant message
received the union of every turn's reasoning. Now reset at both turn
boundaries: tool (alongside existing liveReasoningText reset) and
interim_assistant (the other turn boundary where prior reasoning closes).
2. ui.js renderMessages preferred m.reasoning (which could be corrupted by
bug 1) over m.reasoning_content (the clean per-turn value from the
backend). The fallback now reads m.reasoning_content || m.reasoning.
Both fixes are needed: bug 2 alone cannot cover providers that stream
reasoning events without populating reasoning_content on the final API
message.
Updated test_streaming_race_fix.py to scope its reconnect-accumulator
guard to the _wireSSE preamble only, since turn-boundary resets inside
event listeners are intentional and correct.
9 new regression tests in test_issue2565_reasoning_accumulation.py.
Fixes#2713 — live assistant text can truncate at tool-call segment
boundaries during streaming.
Before _resetAssistantSegment() in the tool and interim_assistant SSE
handlers, synchronously flush any pending rAF render work so tokens that
arrived during the 66ms throttle window are written to the DOM before
assistantBody is cleared. Without this flush, the pending _doRender
callback fires after assistantBody is null and skips the write silently,
causing the tail of the pre-tool segment to disappear from the live view.
Implementation:
- Extract _flushPendingSegmentRender() helper (guarded by assistantBody
&& _renderPending) that cancels the pending rAF and synchronously
writes via smd/renderMd/esc — same cascade as _doRender.
- Call the helper from both the tool and interim_assistant handlers
before their respective _resetAssistantSegment() calls.
- Normal cases where the rAF has already fired are unaffected (guard
skips immediately).
Completed transcripts were never affected (renderMessages rebuilds from
the full assistantText accumulator on done).
Adds tests/test_issue2713_streaming_segment_flush.py with 11 static
analysis regression tests pinning the helper shape and call-site
ordering.
Cherry-pick of PR #2796 by @ai-ag2026, squashed from 5 author commits onto current master:
- dcee0563 fix: drop stale optimistic sidebar rows
- 3a73400d fix: clear stale busy state before send
- 46c3b902 fix: preserve server idle rows during optimistic merge
- de51d271 fix: let chat start survive pre-start UI errors
- d2f5c906 fix: hide nonfatal pre-start send warnings
Authorship preserved via --author. Code-only squash (no CHANGELOG).
Add Hepburn skin with full light/dark palette derived from the
Hepburn TUI theme. Brand color #c6246a with pink-magenta accents.
- Light: soft pink surfaces (#fff3f7 / #fbe4ed)
- Dark: deep aubergine (#110a0f / #1e0f19)
- Accent: #d44a7a (light) / #f278ad (dark)
- Styled: send button, new chat button, tool cards, session indicator
Also fix settings panel skin picker to prioritize localStorage
over server defaults, so newly selected skins reflect correctly
in the dropdown.
Closes#2771.
v0.51.117 (PR #2766) introduced a top-level function _inflightStateLimits()
in static/ui.js that collided with the window._inflightStateLimits config
object set in static/boot.js. Because top-level function declarations in
classic (non-module) scripts attach to window, boot.js's assignment
overwrote the function reference, and every later _inflightStateLimits()
call threw TypeError. _compactInflightState() runs on every send(), so
no new chat session could be created — v0.51.117 is effectively unusable.
Reported by @jahilldev, with multiple users (@isma3iloiso, @theDanielJLewis,
@JHVenn) confirming the bug or reverting to v0.51.116.
Fix: rename the function to _getInflightStateLimits() — the window-attached
config key stays under its original name (unchanged for any downstream
code that reads it). Updates all 4 call sites in static/ui.js.
Tests:
- Update tests/test_inflight_storage_quota.py — the existing test
asserted 'function _inflightStateLimits()' in UI_JS as a positive
presence check, which certified the bug. Now asserts the renamed
function name is present AND the old colliding name is absent AND
no stale call sites remain.
- Add tests/test_window_function_collision.py — generalized regression
that scans every static JS file for top-level function declarations
whose name also appears as the target of 'window.X = {...}' or
'window.X = <number>'. This is the exact shape that broke #2715
(_pinnedSessionsLimit in v0.51.106) and #2771. Test fails with a
precise diagnostic naming the file and symbol if the bug class
returns. Confirmed test FAILS on current master (unfixed) and PASSES
on this branch.
Verified end-to-end against the live browser before commit:
- typeof window._inflightStateLimits === 'object' (config preserved)
- typeof window._getInflightStateLimits === 'function'
- _getInflightStateLimits() returns the limits object
- saveInflightState() persists to localStorage without throwing
Full pytest suite: 6308 passed, 6 skipped, 3 xpassed, 8 subtests passed.
Opus advisor: SHIP.
Original PR: #2676 by @lucasrc
Adds POST /api/skills/toggle endpoint that flips skills.disabled in
config.yaml, and a UI toggle in the Skills panel that shows all skills
(including disabled ones) with a per-skill on/off control.
- Backend: new endpoint validates skill exists in filesystem before
toggling. Read-modify-write wrapped in _cfg_lock for thread safety.
Writes through to platform_disabled.webui when present.
- Frontend: each skill-item now has a toggle switch; disabled skills
appear muted but still listed (previously they were filtered out).
- i18n: new toggle keys translated across all 9 non-English locales.
- Tests: round-trip test for disabled list normalization + toggle
endpoint behavior.
Squash-merged from contributor's branch (19 commits + 1 merge commit)
onto current master via the cherry-pick-stale-contributor-prs procedure.
- Replace text 'Collapse'/'Expand' button labels with Lucide chevron SVG
icons (chevron-down expanded → click to collapse, chevron-up collapsed
→ click to expand). Matches the iconographic design language of the
rest of the chrome (composer buttons, sidebar controls).
ARIA label + title attributes carry the same semantics for assistive
tech, so no accessibility regression vs. the text labels.
- Fix collapsed-card edge clipping at viewport bottom. Original
.clarify-card { bottom: -24px } was sized for the expanded card
(300-420px tall); adding a 72px collapsed variant pushed the header
below the parent's visible region. Override bottom to 8px and reduce
inner padding for the collapsed state so the entire header sits cleanly
inside the viewport at both desktop and mobile sizes (verified card
fits with ~115px margin desktop / ~125px margin mobile).
Per Nathan's 2026-05-22 UX feedback on the screenshot package.
The original PR #2663 added 2 new English i18n keys but didn't extend them
to the 10 non-English locale blocks. Five locale-coverage tests
(zh/ja/ko/ru/es) failed because they verify every English key has a paired
entry in their locale. Added the keys to all 10 locales:
- zh + zh-Hant: actual translations
- it/ja/ru/es/de/pt/ko/fr: English + // TODO: translate markers (the
repo's established pattern for these locales)
Light-theme review revealed white text on gold chips (color: var(--bg-page)) was
washed out and hard to read. Switched to fixed dark text #1a1a1a with font-weight
600 so the on-state reads clearly on the gold accent in both light and dark
themes. Off-state unchanged (muted text on transparent).
Per deep-review verdict SHIP-WITH-FIXES on PR #2636:
1. Profile-switch reconciliation: _refreshProfileSwitchBackground now re-fetches
/api/settings and re-applies hidden_tabs for the new profile. Without this,
Profile A's hidden-tabs choice stayed in effect under Profile B until the
user opened Settings → Appearance.
2. A11y: switched chips from role=button + aria-pressed to role=switch +
aria-checked. The pressed/not-pressed wording confused screen-reader users
because chip-off looks like the off state. Added role=group +
aria-labelledby on the container, and a :focus-visible style on the chips.
3. Server-side belt-and-suspenders: api/config.py now strips 'chat' and
'settings' from hidden_tabs at validation time, matching the client's apply-
time filter. A tampered POST can no longer persist the forbidden values.
3 new regression tests added (chat/settings rejection, profile-switch wiring,
chip a11y attributes).
Co-authored-by: FrancescoFarinola <francesco.farinola@example.com>
Three tweaks from reviewer:
1. Harden _applyTabVisibility to skip always-visible panels even if
they appear in hidden_tabs (localStorage tampering, stale server
data). Forces shouldHide=false so stale nav-tab-hidden classes
on chat/settings get removed, not just skipped.
2. Add synchronous inline <script> flash-prevention after sidebar-nav
in index.html. On slow networks, defer scripts run after the
browser incrementally renders the DOM, causing hidden tabs to
flash visible before JS executes. The inline script reads
hermes-webui-hidden-tabs from localStorage and applies
nav-tab-hidden classes before first paint, mirroring the existing
theme/skin/font-size pattern. The boot.js IIFE becomes a secondary
fallback (comment updated).
3. Remove _settingsHiddenTabsOnOpen dead state. It was tracked but
never read for revert — _revertSettingsPreview is intentionally
a no-op for appearance autosave. Removing the tracking makes
the code honest about what it actually does. Also removes the
test_settings_session_tracking test that validated this dead code.