fix(model): default /model switches to session scope everywhere

Flip the resolve_persist_behavior() fallback from persist-to-config to
session-only. A plain /model <name> (typed or via any picker — CLI,
TUI/Desktop, gateway) now affects only the current session; --global
persists explicitly, and model.persist_switch_by_default: true restores
the old opt-out behavior for users who want switches to stick.

This is the root cause behind the recurring 'session switch applied
globally' bug class (#61458, #63083, #58290, #61190): every surface
funnels its no-flag default through this one function, so per-surface
patches kept missing paths. Fixing the default fixes all surfaces at
once: CLI typed + picker, TUI/Desktop config.set + slash, gateway typed
+ inline picker.

Builds on liuhao1024's #58371 (--provider session scoping, cherry-picked
as the previous commit) and supersedes the per-surface #61488.
This commit is contained in:
Teknium 2026-07-20 01:10:45 -07:00
parent 0d6d73525d
commit 8b6fde3a35
8 changed files with 75 additions and 43 deletions

24
cli.py
View file

@ -8189,16 +8189,16 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin):
Supports:
/model show current model + usage hints
/model <name> switch model (persists by default)
/model <name> switch model (this session only)
/model <name> --once switch for the next turn only
/model <name> --session switch for this session only
/model <name> --global switch and persist (explicit)
/model <name> --session switch for this session only (explicit)
/model <name> --global switch and persist to config.yaml
/model <name> --provider <provider> switch provider + model
/model --provider <provider> switch to provider, auto-detect model
Persistence defaults to on (``model.persist_switch_by_default`` in
config.yaml, default True). Use ``--session`` for this CLI session or
``--once`` for the next turn only.
Persistence defaults to off (``model.persist_switch_by_default`` in
config.yaml, default False switches are session-scoped). Use
``--global`` to persist, or ``--once`` for the next turn only.
"""
from hermes_cli.model_switch import (
switch_model,
@ -8225,10 +8225,10 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin):
if one_turn and not model_input and not explicit_provider:
_cprint(" ✗ /model --once requires a model or provider.")
return
# Resolve the effective persistence once: --session overrides the
# config-gated default, --global forces persist, otherwise defer to
# model.persist_switch_by_default (defaults to True so /model survives
# across sessions).
# Resolve the effective persistence once: --global forces persist,
# --session/--once force session-scope, otherwise defer to
# model.persist_switch_by_default (defaults to False so /model is
# session-scoped unless the user opts in).
persist_global = resolve_persist_behavior(
is_global_flag, is_session, is_once=one_turn,
explicit_provider=explicit_provider,
@ -13160,8 +13160,8 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin):
# --- /model picker modal ---
if self._model_picker_state:
try:
# Picker selections persist by default (same default as
# /model <name>); honour model.persist_switch_by_default.
# Picker selections follow the same session-scoped default
# as /model <name>; honour model.persist_switch_by_default.
from hermes_cli.model_switch import resolve_persist_behavior
self._handle_model_picker_selection(