Skip to content

fix(desktop): reseed stale composer model from profile default - #48946

Closed
marsmensch wants to merge 1 commit into
NousResearch:mainfrom
marsmensch:fix/desktop-openrouter-fusion-default
Closed

fix(desktop): reseed stale composer model from profile default#48946
marsmensch wants to merge 1 commit into
NousResearch:mainfrom
marsmensch:fix/desktop-openrouter-fusion-default

Conversation

@marsmensch

@marsmensch marsmensch commented Jun 19, 2026

Copy link
Copy Markdown

Summary

A Desktop profile configured as openrouter / openrouter/fusion could still create brand-new chats with stale gpt-5.5 / openai-codex composer state. Fresh no-session composer state now reseeds from the active profile default when it still matches the last default (or the legacy GPT-5.5 default), while deliberate same-profile model picks continue to survive routine refreshes.

This also gives the router slug a readable Desktop label: openrouter/fusion renders as OpenRouter Fusion instead of the generic Fusion.

Root cause

Desktop has two model-selection layers:

Layer Scope Persistence Used by
Profile default per profile config.yaml new non-Desktop sessions, settings defaults, backend resolution
Composer pick Desktop UI state localStorage Desktop session.create per-session overrides

The composer layer is intentionally sticky, but the refresh path only seeded when the current model was empty (or on a forced profile swap). If a user changed a profile default outside that composer state — for example to openrouter / openrouter/fusion — the stale persisted composer value (gpt-5.5 / openai-codex) kept winning and was sent with session.create for brand-new Desktop chats.

Changes

  • apps/desktop/src/app/session/hooks/use-model-controls.ts
    • records the last profile default used to seed the no-session composer
    • reseeds when the composer still matches that baseline and the profile default changes
    • explicitly migrates legacy Desktop defaults (gpt-5.5 and openai/gpt-5.5 with openai-codex) when no baseline exists
    • still skips active runtime sessions, so live chats are not mutated mid-turn
    • still preserves deliberate same-profile user picks on routine refresh
  • apps/desktop/src/lib/model-status-label.ts
    • maps openrouter/fusion to OpenRouter Fusion
  • tests cover stale default reseeding, both legacy GPT-5.5 spellings, user-pick preservation, and the display label

Related work

This PR is intentionally Desktop-only. The OpenRouter catalog/router-alias picker side is already covered by:

Those PRs make the slug selectable; this PR makes Desktop honor the active profile default instead of letting stale composer state override it.

Validation

Check Result
npm --workspace apps/desktop run test:ui -- src/app/session/hooks/use-model-controls.test.tsx src/lib/model-status-label.test.ts src/store/model-visibility.test.ts 3 files / 26 tests passed
npm --workspace apps/desktop run build passed (vite build + assert-dist-built)
git diff --check clean
Rebased on current origin/main yes; PR head 8a17d824f7433375995cae38dbd603660695b232

Draft until source-launched Desktop manual QA is exercised from apps/desktop (not /Applications/Hermes.app).

@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/tui Terminal UI (ui-tui/ + tui_gateway/) provider/openrouter OpenRouter aggregator labels Jun 19, 2026
@marsmensch
marsmensch force-pushed the fix/desktop-openrouter-fusion-default branch from ff8f1a3 to 8a17d82 Compare June 19, 2026 10:27
@marsmensch marsmensch changed the title fix(desktop): honor OpenRouter Fusion profile default fix(desktop): reseed stale composer model from profile default Jun 19, 2026
@alt-glitch alt-glitch added comp/desktop Electron desktop app (apps/desktop/*) and removed comp/tui Terminal UI (ui-tui/ + tui_gateway/) labels Jun 26, 2026

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tracing the stale-composer override through the Desktop path. The underlying bug remains on current main: refreshCurrentModel() exits on a non-empty composer value at apps/desktop/src/app/session/hooks/use-model-controls.ts:49, while new chats ship that value as a session.create override at apps/desktop/src/app/session/hooks/use-session-actions/index.ts:213-225.

Problems

  • The legacy heuristic at PR head apps/desktop/src/app/session/hooks/use-model-controls.ts:121 overwrites an unmarked gpt-5.5 / openai-codex selection merely because it matches a historic default. Current persisted state has only model/provider (apps/desktop/src/store/session.ts:264-265), so that value may also be a deliberate user pick. The new tests currently require the destructive case at PR head use-model-controls.test.tsx:230-271.

Suggested changes

  • Track explicit composer provenance (manual versus default) rather than inferring provenance from a slug; related PR #62486 / commit b74009e7e65e4fa1c73c749334b8216694eb94e6 demonstrates that shape.
  • Preserve current main's session-scoped picker behavior at apps/desktop/src/app/session/hooks/use-model-controls.ts:96-100 when salvaging this older branch.

Automated hermes-sweeper review.

!current.model ||
sameSelection(current, nextDefault) ||
(baseline != null && sameSelection(current, baseline) && !sameSelection(baseline, nextDefault)) ||
(baseline == null && isLegacyDesktopDefault(current) && !sameSelection(current, nextDefault))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This treats an unmarked historic gpt-5.5 / openai-codex picker value as default-derived even if the user deliberately selected it. Model/provider alone cannot encode that provenance; persist an explicit manual/default source marker instead, then add a regression test that preserves a manual legacy selection after the profile default changes.

@teknium1 teknium1 added sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 14, 2026
@OutThisLife

Copy link
Copy Markdown
Collaborator

Closing as superseded by #65896 (merged). Same bug class — fresh Desktop chats reseeding/sticking a stale composer model/provider instead of following the active profile default from config.yaml.

The readable OpenRouter Fusion label bit isn't covered by that merge; if you still want that UX polish, a focused follow-up PR just for the label would be welcome.

@teknium1 teknium1 added the area/profiles Multi-profile isolation, HERMES_HOME scoping label Jul 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/profiles Multi-profile isolation, HERMES_HOME scoping comp/desktop Electron desktop app (apps/desktop/*) P3 Low — cosmetic, nice to have provider/openrouter OpenRouter aggregator sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants