fix(desktop): the main agent's model pick persists as the profile default - #86414
Merged
Merged
Conversation
…ault
Reported: the default bot switches to the OpenAI API account instead of
the user's subscription, and doesn't retain the previous selection.
Root cause: the composer model picker always sent the switch as
--session scope, even for the PRIMARY profile's main agent. So the pick
never wrote config.yaml model.provider — and with model.provider unset,
resolve_provider('auto') falls through to a leftover OPENAI_API_KEY env
var and picks OpenAI/OpenRouter. The subscription the user selected was
only ever a per-session override that evaporated on the next session.
Fix: when the pick targets the primary profile's main agent
(touchesPrimary), send --global so it persists to config.yaml
(model.default + model.provider) via the existing model-switch persist
path. A SET model.provider already outranks the OPENAI_API_KEY env var
in resolve_provider (tier 2 vs tier 3), so the main agent now keeps the
chosen provider across restarts. Secondary chat tiles stay --session so
picking a model in one chat never rewrites the profile default (the
cross-session-contamination guard the old comment protected).
No change to resolve_provider's priority chain, so #29285 (an explicit
env key beating a STALE oauth login) is untouched — we simply make the
user's explicit main-agent selection the config default it always
should have been.
૮ >ﻌ< ა ci reviewran on ec2ebb3 — MoA presets stay session-scoped; update tests for primary-pe
|
Fix CI (ui shard 3of3): the primary main-agent pick now persists via --global, but MoA (mixture-of-agents) presets must NOT — a transient orchestration choice can't become the global gateway default. Exclude provider==='moa' from the persist path (stays --session). Update the primary-picker test to assert --global (the new intent) and keep the MoA + secondary-tile tests asserting --session (the guards that prove the narrowing). 19/19 green locally.
skappafrost
pushed a commit
to skappafrost/hermes-agent
that referenced
this pull request
Aug 15, 2026
…ault (NousResearch#86414) * fix(desktop): the main agent's model pick persists as the profile default Reported: the default bot switches to the OpenAI API account instead of the user's subscription, and doesn't retain the previous selection. Root cause: the composer model picker always sent the switch as --session scope, even for the PRIMARY profile's main agent. So the pick never wrote config.yaml model.provider — and with model.provider unset, resolve_provider('auto') falls through to a leftover OPENAI_API_KEY env var and picks OpenAI/OpenRouter. The subscription the user selected was only ever a per-session override that evaporated on the next session. Fix: when the pick targets the primary profile's main agent (touchesPrimary), send --global so it persists to config.yaml (model.default + model.provider) via the existing model-switch persist path. A SET model.provider already outranks the OPENAI_API_KEY env var in resolve_provider (tier 2 vs tier 3), so the main agent now keeps the chosen provider across restarts. Secondary chat tiles stay --session so picking a model in one chat never rewrites the profile default (the cross-session-contamination guard the old comment protected). No change to resolve_provider's priority chain, so NousResearch#29285 (an explicit env key beating a STALE oauth login) is untouched — we simply make the user's explicit main-agent selection the config default it always should have been. * MoA presets stay session-scoped; update tests for primary-persist intent Fix CI (ui shard 3of3): the primary main-agent pick now persists via --global, but MoA (mixture-of-agents) presets must NOT — a transient orchestration choice can't become the global gateway default. Exclude provider==='moa' from the persist path (stays --session). Update the primary-picker test to assert --global (the new intent) and keep the MoA + secondary-tile tests asserting --session (the guards that prove the narrowing). 19/19 green locally.
bobaba76
pushed a commit
to bobaba76/hermes-agent
that referenced
this pull request
Aug 27, 2026
…ault (NousResearch#86414) * fix(desktop): the main agent's model pick persists as the profile default Reported: the default bot switches to the OpenAI API account instead of the user's subscription, and doesn't retain the previous selection. Root cause: the composer model picker always sent the switch as --session scope, even for the PRIMARY profile's main agent. So the pick never wrote config.yaml model.provider — and with model.provider unset, resolve_provider('auto') falls through to a leftover OPENAI_API_KEY env var and picks OpenAI/OpenRouter. The subscription the user selected was only ever a per-session override that evaporated on the next session. Fix: when the pick targets the primary profile's main agent (touchesPrimary), send --global so it persists to config.yaml (model.default + model.provider) via the existing model-switch persist path. A SET model.provider already outranks the OPENAI_API_KEY env var in resolve_provider (tier 2 vs tier 3), so the main agent now keeps the chosen provider across restarts. Secondary chat tiles stay --session so picking a model in one chat never rewrites the profile default (the cross-session-contamination guard the old comment protected). No change to resolve_provider's priority chain, so NousResearch#29285 (an explicit env key beating a STALE oauth login) is untouched — we simply make the user's explicit main-agent selection the config default it always should have been. * MoA presets stay session-scoped; update tests for primary-persist intent Fix CI (ui shard 3of3): the primary main-agent pick now persists via --global, but MoA (mixture-of-agents) presets must NOT — a transient orchestration choice can't become the global gateway default. Exclude provider==='moa' from the persist path (stays --session). Update the primary-picker test to assert --global (the new intent) and keep the MoA + secondary-tile tests asserting --session (the guards that prove the narrowing). 19/19 green locally.
Codename-11
pushed a commit
to Codename-11/hermes-agent
that referenced
this pull request
Sep 2, 2026
…policy for every surface (NousResearch#90235) Symptom: picking a model in the Desktop composer for the primary chat silently rewrote config.yaml (model.default + model.provider) as the profile default, ignoring model.persist_switch_by_default. A throwaway pick that resolved to e.g. openai-api (no key) left the profile with an unusable default on the next launch (NousResearch#90235). Root cause: 7d96537 (NousResearch#86414) made use-model-controls.ts send --global for every primary-tile pick so a fresh profile would get a persisted provider instead of falling through to a leftover OPENAI_API_KEY env var. That put a persistence policy in the client, contradicting the server-side rule /model uses (resolve_persist_behavior). Fix: - resolve_persist_behavior gains one rule, ahead of the --provider session-only rule: when neither model.default nor model.provider is configured yet, persist. This preserves NousResearch#86414's first-pick motivation for CLI, gateway and Desktop alike. With a default configured, a plain pick is session-only unless --global / persist_switch_by_default. - Desktop primary-tile picks send no scope flag and let the gateway decide. Secondary tiles and MoA presets still send --session. - /model help text in cli.py said "(persists)"; it now matches reality and lists --global. - Docs: desktop.md picker note + slash-commands /model row. Tests: test_first_pick_persists_then_session_only (fails on main), and the existing use-model-controls vitest updated to assert the flag-less request.
melon-xf
added a commit
to melon-xf/hermes-agent
that referenced
this pull request
Sep 3, 2026
…ault (NousResearch#86414) * fix(desktop): the main agent's model pick persists as the profile default Reported: the default bot switches to the OpenAI API account instead of the user's subscription, and doesn't retain the previous selection. Root cause: the composer model picker always sent the switch as --session scope, even for the PRIMARY profile's main agent. So the pick never wrote config.yaml model.provider — and with model.provider unset, resolve_provider('auto') falls through to a leftover OPENAI_API_KEY env var and picks OpenAI/OpenRouter. The subscription the user selected was only ever a per-session override that evaporated on the next session. Fix: when the pick targets the primary profile's main agent (touchesPrimary), send --global so it persists to config.yaml (model.default + model.provider) via the existing model-switch persist path. A SET model.provider already outranks the OPENAI_API_KEY env var in resolve_provider (tier 2 vs tier 3), so the main agent now keeps the chosen provider across restarts. Secondary chat tiles stay --session so picking a model in one chat never rewrites the profile default (the cross-session-contamination guard the old comment protected). No change to resolve_provider's priority chain, so NousResearch#29285 (an explicit env key beating a STALE oauth login) is untouched — we simply make the user's explicit main-agent selection the config default it always should have been. * MoA presets stay session-scoped; update tests for primary-persist intent Fix CI (ui shard 3of3): the primary main-agent pick now persists via --global, but MoA (mixture-of-agents) presets must NOT — a transient orchestration choice can't become the global gateway default. Exclude provider==='moa' from the persist path (stays --session). Update the primary-picker test to assert --global (the new intent) and keep the MoA + secondary-tile tests asserting --session (the guards that prove the narrowing). 19/19 green locally.
melon-xf
added a commit
to melon-xf/hermes-agent
that referenced
this pull request
Sep 3, 2026
…policy for every surface (NousResearch#90235) Symptom: picking a model in the Desktop composer for the primary chat silently rewrote config.yaml (model.default + model.provider) as the profile default, ignoring model.persist_switch_by_default. A throwaway pick that resolved to e.g. openai-api (no key) left the profile with an unusable default on the next launch (NousResearch#90235). Root cause: 7d96537 (NousResearch#86414) made use-model-controls.ts send --global for every primary-tile pick so a fresh profile would get a persisted provider instead of falling through to a leftover OPENAI_API_KEY env var. That put a persistence policy in the client, contradicting the server-side rule /model uses (resolve_persist_behavior). Fix: - resolve_persist_behavior gains one rule, ahead of the --provider session-only rule: when neither model.default nor model.provider is configured yet, persist. This preserves NousResearch#86414's first-pick motivation for CLI, gateway and Desktop alike. With a default configured, a plain pick is session-only unless --global / persist_switch_by_default. - Desktop primary-tile picks send no scope flag and let the gateway decide. Secondary tiles and MoA presets still send --session. - /model help text in cli.py said "(persists)"; it now matches reality and lists --global. - Docs: desktop.md picker note + slash-commands /model row. Tests: test_first_pick_persists_then_session_only (fails on main), and the existing use-model-controls vitest updated to assert the flag-less request.
This was referenced Sep 20, 2026
adurham
added a commit
to adurham/hermes-agent
that referenced
this pull request
Sep 26, 2026
…wiring Bare `/reasoning` (and the `/effort` alias added for Claude Code parity) set `_reasoning_picker_state` and then painted nothing, while the app's input filter still excluded that state — a locked composer behind an invisible modal. The picker's state machine, render helper and Enter handler survived the upstream merges; the four pieces that connect it to the app did not. Lost at the v2026.7.20 sync and dropped from every sync since: `git show <tag>:cli.py | grep -c _get_reasoning_picker_display` is 2 at ff97264/a4c788a9a9, 0 from v2026.7.20 onward. Restored on the mixin that owns the layout now (hermes_cli/cli_tui_mixin.py): * `_get_reasoning_picker_display_fragments` renders through the same `_render_scroll_list_panel` as the model picker and command palette, so the picker inherits the shared viewport/wrap/chrome behaviour instead of the original commit's hand-rolled panel (whose helpers no longer exist). * `_tui_reasoning_picker_{up,down,escape}` + their `kb.add(...)` bindings. Enter is deliberately NOT bound: submission goes through `_tui_enter_overlay`, exactly like the model picker. * `reasoning_picker_widget` built in `_tui_build_layout` and passed into `_build_tui_layout_children` — a parameter that had survived the whole time with nothing to fill it. * `_reasoning_picker_state` added to both `_tui_cancel_foreground_ui` closer lists (Ctrl+C / Ctrl+Q) so the picker always has an escape hatch. Apply semantics: `_apply_reasoning_arg` gains `persist_global: bool = False`. The original helper wrote the GLOBAL `agent.reasoning_effort` unconditionally on every pick; upstream has since made the same command's typed form session-scoped with `--global` (NousResearch#86414) and the picker has no scope affordance, so restoring that write verbatim would ship one command with two persistence policies. The per-model map write (`agent.reasoning_effort_by_model`) is deliberately NOT gated — it is this fork's isolation feature, this helper is its only writer, and its reader (`_apply_reasoning_for_new_model`) runs on every model switch, so gating it would leave the documented feature write-dead. The confirmation line now names the scope it actually used (`session; default for <model>`). Verified: 24 new tests in tests/hermes_cli/test_reasoning_picker.py, fail-first proven against a pristine `git archive HEAD` export of the pre-fix tree (11 failed there, 24/24 pass now). Two standalone probes drove the real layout assembly and a real prompt_toolkit KeyBindings registry: the widget lands immediately after the model picker with its filter tracking the state, and the registry resolves Up/Down/Escape to the picker handlers while open (none while closed; Escape eager). Existing reasoning/extension-hook suites green (44 passed across 4 files). FORK.md entry added.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Report (@rashidkhan): the default Hermes bot switches to the OpenAI API account rather than the subscription, and doesn't retain the previous selection.
Why — the actual bug. The composer model picker always sent the switch as
--sessionscope, even for the primary profile's main agent. So the selection never wroteconfig.yaml model.provider. Withmodel.providerunset,resolve_provider('auto')falls through (tier 3) to a leftoverOPENAI_API_KEYin~/.hermes/.envand picks OpenAI/OpenRouter. The subscription the user picked was only a per-session override that evaporated next session — hence "doesn't retain the selection" and "switches to OpenAI." One root cause, both symptoms.Fix. When the pick targets the primary profile's main agent (
touchesPrimary), send--globalinstead of--session, so it persists toconfig.yaml(model.default+model.provider) through the existing model-switch persist path. A setmodel.provideralready outranks the env key inresolve_provider(tier 2 > tier 3, verified), so the main agent keeps the chosen provider across restarts.Deliberately narrow:
--session— picking a model in one chat must not rewrite the profile default (the cross-session-contamination guard the old comment protected). Only the primary main-agent pick persists.resolve_provider's priority chain — so Bug: auth.json active_provider silently overrides model.provider in config.yaml at runtime #29285 (an explicit env key beating a stale OAuth login) is untouched. We just make the user's explicit main-agent selection the config default it always should have been, rather than fighting the priority chain.This supersedes #86386 (which tried to reorder the priority chain and collided with #29285's tested contract). Renderer change — ships in the next desktop build.