Skip to content

fix(desktop): session-scope fast mode, surface profile ownership + pinned model override - #66449

Merged
OutThisLife merged 2 commits into
mainfrom
audit/desktop-model-picker
Jul 17, 2026
Merged

fix(desktop): session-scope fast mode, surface profile ownership + pinned model override#66449
OutThisLife merged 2 commits into
mainfrom
audit/desktop-model-picker

Conversation

@OutThisLife

@OutThisLife OutThisLife commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

Audit of the desktop model picker / session-state complaints ("model switch applies everywhere; new message appears in all sessions; can't tell if a session belongs to Agent A or B; switching is slow"). Two of the three bug classes were already fixed on main in July (ce5c1f9f7, 659d1123c, #47709/#47743/#48281) but are not in the v0.18.2 release users are running. This PR closes everything that remained on main, in one change:

  • config.set key=fast is now session-scoped (tui_gateway/server.py) — the last "switch one session, switches everywhere" hole. It wrote agent.service_tier to config.yaml globally even when targeting a session, and the desktop's per-model presets hit it on every model pick, flipping fast mode for every other session, profile, the CLI, and future gateway builds. Now it pins create_service_tier_override ("priority" / "" for explicit normal, so lazy builds and rebuilds keep the choice) and never touches config.yaml; the no-session path persists globally as before. Sibling of the earlier reasoning scoping fix — same pattern, same test shape. config.get key=fast reads the pre-build pin, and fast-support validation checks a draft's picked model instead of the global default.
  • Owning-profile tags (closes Desktop: show owning profile for pinned sessions in All Profiles view #66003) — new ProfileTag chip (profile initial, identity color with user override, tooltip + aria Profile: <name>) on pinned rows and search results in the All-profiles sidebar view (the flat cross-profile lists with no group header), and on the chat header whenever more than one profile exists. Default profile renders neutral; status dots keep their own semantics; single-profile users see no change.
  • Pinned-model override indicator (addresses Desktop: composer model picker silently overrides Settings default — unexpected model & billing #62055) — the composer model pill shows an accent dot + tooltip ("pinned by you; new chats use this instead of the Settings default") when a draft is running a manual sticky pick. Live sessions show their own model in the footer, so no badge there. The sticky-pick design itself is unchanged — only its invisibility was the bug (it has cost users real billing).
  • Profile-switch latency: pre-open the gateway socket on hover intent — extends the e0390c0f7 hover pre-warm from spawn-only to the full spawn + connect chain (openGatewayForProfile: same path as the real switch, minus activation). By click time ensureGatewayForProfile finds an open socket and just activates it, instead of paying the WS connect + descriptor fetch after the pre-warmed spawn. Speculative hovers never start reconnect loops — retry/error UX stays with the real switch.

Also verified during the audit: both the desktop and TUI halves of #61190 are already implemented on main (--session flag reaches parse_model_flags), so that issue can be closed as implemented; and #65743 reproduces only on the v0.18.2 bundle, not main — the model-picker complaint set mostly needs a desktop release.

Test plan

  • New tests/tui_gateway/test_fast_session_scope.py (9 tests, mirrors the reasoning-scope suite) — session-scoped set skips config write, pins survive for lazy sessions, explicit normal pins "", toggle honors the pre-build pin, no-session persists globally, config.get reads the pin
  • Updated tests/test_tui_gateway_server.py::test_config_set_fast_updates_live_agent_session_scoped — asserts NO global write + override pin (was asserting the global write)
  • New profile-tag.test.tsx (chip initial, accessible label, default-neutral, color override) and model-pill.test.tsx (pin dot on manual draft, quiet on default source, quiet on live session, both render paths)
  • Updated profile.test.ts pre-warm suite — pre-warm opens (never activates) the gateway, skips the active profile, throttles, swallows failures
  • Full runs green: desktop vitest 216 files / 1788 tests, tsc --noEmit (app + electron), eslint (0 errors), scripts/run_tests.sh tests/tui_gateway/ tests/test_tui_gateway_server.py (727 tests)

Closes #66003.

A cold profile switch pays the full pool-backend spawn — Python boot,
port announcement, readiness probe, token adoption — before the
profile's gateway can even open. Measured with the new CDP harness
(scripts/measure-profile-switch.mjs, same family as
profile-session-switch.mjs): click → WS open is ~2.5-2.9s on a cold
profile, ~3-3.6s to a settled sidebar; a warm profile settles in
~0.5-0.8s. The pointer entering a profile square telegraphs the switch
hundreds of ms before the click lands, so start the spawn then.

- store/profile: prewarmProfileBackend(name) — fires the existing
  hermesDesktop.getConnection IPC, which is idempotent (ensureBackend
  returns the pooled connectionPromise), so the real switch joins the
  in-flight spawn instead of starting it. Skips the active gateway
  profile, throttles per profile (60s) so drive-by hovers can't spam
  spawn attempts, and swallows failures — error UX belongs to the real
  switch. No new IPC surface; the pool's existing LRU cap + idle reaper
  still bound resource use, and the LRU guard never evicts a
  keepalive-fresh backend for a hover spawn.
- sidebar/use-profile-prewarm: pointerenter/pointerleave handlers with
  a 120ms dwell so sweeping the pointer across the rail or a
  mixed-profile session list doesn't spawn a backend per element
  crossed.
- Wired at the three switch surfaces: rail ProfileSquare, the condensed
  ProfileDropdown items (extracted ProfileDropdownItem so each row owns
  its dwell timer), and SidebarSessionRow (covers cross-profile resumes
  from the all-profiles view; same-profile rows no-op inside the guard).

Measured E2E over CDP: synthetic hover on a cold profile square spawns
its backend in the background; the subsequent click settles in ~519ms
vs ~3.0-3.6s unhovered — and any hover shorter than the spawn still
shaves its dwell off the click's wait.

Verification: apps/desktop `npx tsc --noEmit` clean; full
`npx vitest run` 212 files / 1777 passed (new prewarm guard/throttle
tests in store/profile.test.ts); eslint + prettier clean.
…nned model override

Model-picker audit follow-through — closes the remaining pieces of the
"switch one session, switches everywhere / can't tell whose session this
is" report class:

- tui_gateway: `config.set key=fast` with a session no longer writes the
  global agent.service_tier to config.yaml (sibling of the earlier
  `reasoning` scoping fix). It pins create_service_tier_override
  ("priority" / "" for explicit normal) so lazy builds and rebuilds keep
  the choice; the desktop's per-model presets were rewriting the global
  tier on every model pick. Fast-support validation now checks a draft's
  picked model, and `config.get key=fast` reads the pre-build pin.
- desktop: owning-profile tag (initial chip + tooltip/aria label) on
  pinned rows and search results in the All-profiles sidebar, and on the
  chat header once a second profile exists (#66003).
- desktop: composer model pill shows a pin dot + tooltip when a manual
  sticky pick is overriding the Settings default for new chats (#62055).

Closes #66003. Addresses #62055.
@OutThisLife
OutThisLife enabled auto-merge July 17, 2026 18:33
@OutThisLife
OutThisLife merged commit 3e7c563 into main Jul 17, 2026
38 checks passed
@OutThisLife
OutThisLife deleted the audit/desktop-model-picker branch July 17, 2026 18:36
Gravezzz pushed a commit to Gravezzz/hermes-agent that referenced this pull request Jul 21, 2026
…-model-picker

fix(desktop): session-scope fast mode, surface profile ownership + pinned model override
randlee pushed a commit to randlee/hermes-agent that referenced this pull request Aug 11, 2026
…-model-picker

fix(desktop): session-scope fast mode, surface profile ownership + pinned model override
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Desktop: show owning profile for pinned sessions in All Profiles view

1 participant