The picker no longer touches the profile default. Model/effort/fast live as
plain UI state persisted in localStorage, so a pick follows across Cmd+N and
restarts instead of snapping back. New chats ship that state through
session.create as per-session overrides; live chats still scope switches to the
current session. Settings -> Model remains the only surface that writes the
profile default.
The gateway now accepts those session.create overrides, builds the agent with
them directly, reflects them in the immediate session.info payload, and writes
the chat's own model_config into the lazy DB row so reconnect/resume restores
that chat instead of the global default.
Three rough edges in the remote backend apply flow:
- On success the overlay dropped to IDLE, briefly re-rendering the pre-install
'update available' view and then the generic 'you're all set' before settling.
Close the overlay outright once the backend is confirmed back instead of
bouncing through the idle view.
- If the backend never came back (a failed restart), the flow still reported
success. waitForBackendReturn now returns whether the backend answered;
finishBackendApply surfaces an error when it didn't.
- The up-to-date copy said 'you're running the latest version', conflating
client and backend. Backend target now reads 'the backend is running the
latest version' — the client's own version is a separate pill.
The backend Install path set stage:'restart' and stopped — in remote mode no
boot-progress events arrive to carry the overlay to done, so it sat on the
restarting spinner until a manual reload while the backend had already come
back. Poll the backend until it answers again, then clear the overlay and
refresh the backend status. Target-aware applying copy explains the remote
restart + auto-reconnect instead of the local-updater-window wording.
Also switch the apply poll sleeps from window.setTimeout to globalThis.setTimeout
so the flow is exercisable off the renderer.
The status bar merged both versions into one pill with a single click target,
so there was no way to tell which artifact an update acted on — and the apply
path was overloaded by connection mode. Separate them:
- store: independent client (checkUpdates/applyUpdates) and backend
(checkBackendUpdates/applyBackendUpdate) flows with their own status/apply
atoms; openUpdateOverlayFor(target) drives the overlay.
- status bar: two buttons — client vX (always) and backend vY (+N) (remote
only), each with its own behind-count, opening the overlay for its target.
- overlay: reads the active target's atoms; install/check route per target.
Removes the version-bar merge helper (no longer merging the two versions).
In remote mode, checkUpdates()/applyUpdates() branch on connection.mode and
drive the existing updates overlay from the connected backend instead of the
local Electron git bridge:
- checkUpdates -> GET /api/hermes/update/check, mapped onto DesktopUpdateStatus
(behind, commits, supported=can_apply, message). The overlay renders the
commit list as 'what's changed' and shows guidance (not Install) when the
backend install can't self-apply (docker/nix).
- applyUpdates -> POST /api/hermes/update (the proven command-center path),
polling the action to completion and handling the expected mid-update
connection drop as the restart phase.
Local mode is unchanged. Adds checkHermesUpdate() to hermes.ts and a
BackendUpdateCheckResponse type.