Accounts Connected now follows token validity instead of access-token
presence. Windows removal uses unambiguous PowerShell, a clear that
removes nothing is an error instead of a success toast, and the
connected-row terminal control runs disconnect.
PR #49037 (projects paradigm) broke the folder->session->sidebar flow (#53004):
- The right sidebar's Files pane gated the tree on $currentCwd, which is ''
for global/detached sessions, so those sessions hit a dead-end 'No project
open' pane with no way back into a folder. Restore an affordance in the
empty state: 'Open folder' runs the existing open-folder-as-project flow
(upsert + enter project + fresh session anchored at the picked folder),
decoupled from $currentCwd.
- The sidebar overview listed every auto-promoted repo on disk, session or
not. Auto projects with zero sessions now stay out of the sidebar until
they own a session (they reappear the moment work lands there); explicit
projects and the Home bucket always render.
UI-affecting: coordinator should hold auto-merge for visual review.
Fixes#53004
NVIDIA driver 580+ breaks ANGLE's EGL probing (Invalid visual ID requested),
killing the GPU process at startup on Ubuntu 24.04 and similar hosts. Detect
the driver major from /proc/driver/nvidia/version and, on Linux (not WSLg,
not a remote display), pre-launch appendSwitch('use-angle', 'swiftshader').
Deliberately avoids app.disableHardwareAcceleration(): on 580.173.02 +
Electron 40 that path SIGKILLs the renderer, so the closed#40119 approach
is unsafe here.
Overrides: HERMES_DESKTOP_NVIDIA_SWIFTSHADER=1 forces the fallback on,
=0 opts out; HERMES_DESKTOP_DISABLE_GPU=0 keeps the GPU untouched.
Fixes#40077
Every desktop window boots $queuedPromptsBySession from the same
localStorage key and then never syncs again: no storage-event listener,
and every save writes the window's whole snapshot back, so windows
clobber and resurrect each other's queued prompts (#46732).
- listen for storage events on the queue key (and key===null full clear)
and reload the atom; the event only fires in non-writer windows, so
there is no self-echo
- writeSession/migrateQueuedPrompts merge over the live persisted map
instead of the in-memory atom, closing the same-frame race where a
save reverts a write that landed between sync events
The session-switch half of #46194 (drain routing to the wrong session)
is already covered on main by the per-session queue keys, the
isBackgroundQueueDrain guard and the stored/runtime binding verification
in submit.ts; this PR removes the remaining shared-storage leak in the
same state cluster.
Based on #57516 by @furancis (closed unmerged) — the storage-listener and
merge-over-live-storage approach is reused and reworked onto current main.
Fixes#46732Fixes#46194
project_create (desktop_project tool / CLI) persists to the per-profile
projects.db but writes nothing to state.db, so sessions.changed never fires
and the desktop Projects sidebar goes stale until a manual refresh.
The gateway change watcher now watches projects.db and broadcasts
projects.changed when it moves; the desktop live-sync routes the event to a
new $projectsChangeTick that refetches the project list + tree.
Fixes#56757
read_preview followed the global right-rail tab. follow() copied the
interacted zone into that id, so one group overwrote an explicit open
in another. Resolve from the hovered or focused zone, and skip that
copy when the explicit open lives in a different group. When more
than one preview is mounted, include active_tab_id and the open tabs.
hermes serve --isolated (the backend another machine's Desktop spawns
over SSH) opts out of the host singleton on the CLI side, but its spawn
ledger row carried no structured marker, so the local Desktop's
attach-first discovery adopted it. Nothing on this host owns that
process, so a Desktop-driven update left the local app on stale code.
Record isolated=True in the ledger row and skip such rows in
parseSpawnLedger. An ordinary serve is still attached even when an
isolated record is newer.
Chat catalogs and the session switch treated image and video generation
models as chat. Exclude them by the capability type and name shape the
catalog already publishes, reject selecting one as the session model, and
do not restore a primary already known to be non-chat. Desktop shows the
fallback switch in the transcript.
After a Windows/macOS sleep, the desktop's WS connections drop and the
auto-reconnect reopens the gateway. The route-resume hook treated every
closed->open transition on a new-chat route as 'the user navigated to
/new' and called startFreshSessionDraft, discarding the active runtime
session and parking the previous chat in the sidebar as a detached
session. Keep the active chat when the gateway reopens mid-chat: no
navigation happened, only the transport came back.
Co-authored-by: 0disoft <rodisoft1@gmail.com>
There is no /interrupt slash command on any surface: Desktop has a Stop
button plus Esc, the terminal TUI uses Ctrl+C. The desktop busy guards
still demanded /interrupt — a dead end for users mid-turn, and on Desktop
there is no Enter-to-interrupt fallback either.
The Python gateway side already speaks plainly (user_messages.busy_message);
this fixes the two desktop copy sites left: the slash busy refusal in
use-prompt-actions and the goal-continuationBusy string across all nine
locales.
Fixes#42093Fixes#51576
Copy fix for the slash.ts site salvaged from #51569 (David Metcalfe).
Co-authored-by: David Metcalfe <80915+DavidMetcalfe@users.noreply.github.com>
Several settings suites imported their component inside each test body.
The first in-test `await import` paid the whole jsdom env init + module
transform + import graph inside test one's 15s budget, which times out
under CI runner load; the late-finishing import could also leak its first
render into the next test ("multiple Honcho settings buttons").
Load each panel once at module scope (same pattern as
profile-scope.test.tsx / config-settings.test.tsx), where the cost is
outside every test timeout. No behavior change: hoisted vi.mock factories
still intercept these imports identically.
Seen on #122304, #122269, #122368, #121523.
A composer model pick is sticky: every new chat ships it as a session.create
override instead of the Settings → Model default, and the only way back was
re-applying the main model in Settings. The menu now shows a "Use Settings
default" row while a draft carries a manual pick (the same condition as the
pill's pin dot). It resets the model source to default and reseeds the
composer from the profile model. Strings added in all locales.
Desktop opened /events with no since, and a missing cursor was read as
0, so every open replayed task_events history. Seed the socket from the
snapshot or this connection's last frame, and start a cursorless stream
at MAX(id). An explicit since still replays from there.
Fixes#81537
Signed, hardened-runtime builds without an NSContactsUsageDescription /
NSAppleEventsUsageDescription in mac.extendInfo are denied Contacts and
Apple Events access by TCC without ever showing a prompt — macOS requires
the usage string before it will even ask (#59482). Declare both next to
the sibling mic/camera/calendar strings, with a packaging test that holds
the contract at the config seam so the strings can't be lost in the next
config move.
Fixes#59482
Co-authored-by: Shashwat Gokhe <shashwatgokhe2@gmail.com>
The REST hydration on activate and the prefetched resume both graft a
newest-tail page onto the previously rendered transcript. A long turn can
push every rendered row off that page, so read older pages with
extendRefreshPageToOverlap first, as the tile delegate and background sync
already do. Re-check ownership / resume currency after the extra reads.
The overlap extension had the same older-page closure pasted at every
refresh site. It ignored pagination.offset and fetched again after a
short page. olderPageReader follows tailStateFromPage instead. The
extension also returns early when the rendered transcript has no stored
ids, and stops once a page reaches below the oldest rendered id. The
background-sync refreshes hoist their stale-read guard into stale().
A page that starts mid-turn opens with a tool fold that has no stored
id. The stored-id merge dropped it and moved the window's unstored rows
to the end. Carry each unstored row with the next stored row after it,
and reuse sharesDurableRow/durableRowIds for the overlap test.
A post-turn refresh that overlaps the window was splicing at the first
shared row, so an older row glued after the live tail stayed there.
When that splice is wrong, merge by stored id, keep the fresh page's
copy of a shared id, and leave a row with no stored id at the end. A
page that already covers the window replaces it. A page that shares no
stored id still replaces the window, which is how a compaction rewrite
reaches the screen.
[Used Grok Build 🤖]
(cherry picked from commit 484cb2c1b0595f8df9bf19c5d1a698b8bb5a60f8)
An SSH auth-failed boot error carried none of the local, host-key or
reauth latch tags, so it stayed retryable: every getConnection/api call
re-ran startHermes, re-emitted running: true and hid the boot-failure
overlay before its Gateway settings button could be clicked. Classify
the rejection (kind/sshError tag, or the message once stringified),
latch it like a host-key change, and keep it out of the renderer's
auto-retry loop. reset/repair/apply-config still release the latch.
Co-authored-by: x7peeps <xtpeeps@qq.com>
After the crash-loop budget trips on STATUS_STACK_BUFFER_OVERRUN, relaunch
once with GPU off instead of leaving a blank window. Sandbox stays intact.
Co-authored-by: Cursor <cursoragent@cursor.com>
(cherry picked from commit 02c096d5b2f0e0872b7dc35f408b70e1e51ead2e)
windowsHide on a GUI-subsystem Electron parent does not stop git.exe from
allocating a console. Route those spawns through a console-subsystem
python.exe host that starts git with CREATE_NO_WINDOW (0x08000000) and
forwards the git argv unchanged, including simple-git review probes.
The Windows tree-kill check ran at the top of backendShutdown and threw
before the graceful teardown, pool stop and straggler reap. It now takes
the owned child handles up front, runs after the reap on the children that
are still running, and surfaces a failure only once cleanup is done. A lock
whose delete fails is kept and logged instead of throwing out of close, and
exitAfterBackendShutdown still exits when shutdown reports a failure.
finish_reason=length with empty visible content and a non-empty reasoning
or reasoning_content field uses the existing thinking-budget abort. No
model id is consulted. Empty content with no side channel still continues.
Desktop close/stop no longer discards Windows taskkill failures. After the
same tree-kill, owned PIDs are inventoried and only unheld gateway locks
are cleared.
The recovery modal rendered with no close control and ignored Escape, so a
latched boot error trapped the user. Add a Close button to both the recovery
card and the embedded Gateway settings view, and route Escape through the
same onOpenChange path. Dismissal hides the modal only: the boot error stays
latched, and the overlay comes back when the error changes, clears and
recurs, or a retry starts and fails again.
Co-authored-by: giggling-ginger <110955495+giggling-ginger@users.noreply.github.com>
The setup launcher had no LSUIElement, and its already-installed hand-off
ran after Tauri/AppKit, whose default activation policy is Regular. That
registered the setup bundle as a second Dock app beside the real desktop.
Hand off before constructing Tauri, and restore Regular activation only
when the installer UI is actually shown.
On macOS the Electron single-instance lock also ran before deep-link
registration. setAsDefaultProtocolClient relaunches the app through Launch
Services, so the loser already had a Dock icon by the time the lock failed
and app.exit(0) ran. Register the protocol first. The lock-losing instance
still hard-exits before ready.
Fixes#73151