refactor(tui_gateway): W4 wave2 pass 1 — config.get table, config.set guard decorator, voice/profile helper folds

This commit is contained in:
Teknium
2026-09-03 02:15:41 -07:00
parent e1dc1ff22d
commit f334f61523
4 changed files with 447 additions and 526 deletions
+199 -201
View File
@@ -44,24 +44,19 @@ def _emit_all_session_info() -> None:
_emit_session_info(sid, sess)
def _toggle_display_bool(rid, key, value, *, cfg_key, on_words, off_words):
"""Shared body of the on/off/toggle display booleans (``density``, ``battery``)."""
raw = _word(value)
cur_b = bool(_display_cfg().get(cfg_key, False))
if raw in {"", "toggle"}:
nv_b = not cur_b
elif raw in on_words or raw in off_words:
nv_b = raw in on_words
else:
return _err(rid, 4002, f"unknown {key} value: {value}")
_write_config_key(f"display.{cfg_key}", nv_b)
return _ok(rid, {"key": key, "value": "on" if nv_b else "off"})
def _word(value) -> str:
return str(value or "").strip().lower()
def _raw_word(value) -> str:
"""Like ``_word`` but only None is blank: falsy non-strings (0, False, []) keep their text."""
return ("" if value is None else str(value)).strip().lower()
def _kv(rid, key, value, **extra):
return _ok(rid, {"key": key, "value": value, **extra})
def _cfgset_await_agent(session, rid):
"""Wait for an in-progress agent build; the error envelope if it failed, else None."""
init_err = _wait_agent(session, rid)
@@ -71,81 +66,85 @@ def _cfgset_await_agent(session, rid):
def _cfgset_model_ok(rid, key, value, warning, confirm_required, confirm_message, scope, **extra):
return _ok(rid, {"key": key, "value": value, "warning": warning, "confirm_required": confirm_required,
"confirm_message": confirm_message, "scope": scope, **extra})
return _kv(rid, key, value, warning=warning, confirm_required=confirm_required,
confirm_message=confirm_message, scope=scope, **extra)
def _cfgset_guarded(fn):
"""Setter whose uncaught exception becomes ``_err(rid, 5001, str(e))``."""
def setter(rid, params, key, value, session):
try:
return fn(rid, params, key, value, session)
except Exception as e:
return _err(rid, 5001, str(e))
return setter
# ── per-key handlers
@_cfgset_guarded
def _set_model(rid, params, key, value, session):
"""Live/deferred model switch; see _apply_model_switch and _apply_pending_model_switch."""
try:
if not value:
return _err(rid, 4002, "model value required")
confirmed = bool(params.get("confirm_expensive_model", False))
if session:
from hermes_cli.model_switch import parse_model_switch_args
sid = params.get("session_id", "")
# No live swap while a turn streams (agent.switch_model() mutates fields the worker
# thread reads every iteration): stash the pick for the NEXT turn start.
if session.get("running"):
parsed = parse_model_switch_args(value)
try:
pending_model = parsed.model_input
except Exception:
pending_model = str(value)
pending_provider = (getattr(parsed, "explicit_provider", "") or "").strip()
# Selection guards run HERE (the only moment a confirm round-trip is possible);
# otherwise an unconfirmed stashed pick is dropped at turn start.
if not confirmed:
pending_warning = _pending_switch_selection_warning(pending_model, pending_provider)
if pending_warning is not None:
# Nothing stashed; the client re-sends with confirm_expensive_model.
# `confirm_message` is canonical, `warning` its legacy alias.
return _cfgset_model_ok(
rid, key, pending_model, pending_warning, True, pending_warning, "session", deferred=False
)
session["pending_model_switch"] = {
"raw": value,
"confirm_expensive_model": confirmed,
# _session_info reports these while pending so the end-of-turn settle keeps
# showing the user's pick, not the still-live old model.
"display_model": pending_model,
"display_provider": pending_provider}
return _cfgset_model_ok(rid, key, pending_model, "", False, "", "session", deferred=True)
parsed_flags = parse_model_switch_args(value)
explicit_provider = parsed_flags.explicit_provider
failed_agent_init = session.get("agent") is None and session.get("agent_error") is not None
failed_ready = session.get("agent_ready") if failed_agent_init else None
if failed_agent_init:
if failed_ready is None:
return _err(rid, 5032, session.get("agent_error") or "agent initialization failed")
if not failed_ready.wait(timeout=30.0):
return _err(rid, 5032, "agent initialization timed out")
failed_agent_init = (
failed_agent_init and session.get("agent") is None and session.get("agent_error") is not None
and session.get("agent_ready") is failed_ready and failed_ready.is_set())
if session.get("agent") is None and not explicit_provider.strip() and not failed_agent_init:
_start_agent_build(sid, session)
if init_err := _cfgset_await_agent(session, rid):
return init_err
if not value:
return _err(rid, 4002, "model value required")
confirmed = bool(params.get("confirm_expensive_model", False))
if session:
from hermes_cli.model_switch import parse_model_switch_args
sid = params.get("session_id", "")
# No live swap while a turn streams (agent.switch_model() mutates fields the worker
# thread reads every iteration): stash the pick for the NEXT turn start.
if session.get("running"):
parsed = parse_model_switch_args(value)
try:
pending_model = parsed.model_input
except Exception:
pending_model = str(value)
pending_provider = (getattr(parsed, "explicit_provider", "") or "").strip()
# Selection guards run HERE (the only moment a confirm round-trip is possible);
# otherwise an unconfirmed stashed pick is dropped at turn start. On a warning
# nothing is stashed; the client re-sends with confirm_expensive_model.
# `confirm_message` is canonical, `warning` its legacy alias.
if not confirmed:
pending_warning = _pending_switch_selection_warning(pending_model, pending_provider)
if pending_warning is not None:
return _cfgset_model_ok(rid, key, pending_model, pending_warning, True,
pending_warning, "session", deferred=False)
session["pending_model_switch"] = {
"raw": value, "confirm_expensive_model": confirmed,
# _session_info reports these while pending so the end-of-turn settle keeps
# showing the user's pick, not the still-live old model.
"display_model": pending_model, "display_provider": pending_provider}
return _cfgset_model_ok(rid, key, pending_model, "", False, "", "session", deferred=True)
parsed_flags = parse_model_switch_args(value)
explicit_provider = parsed_flags.explicit_provider
failed_agent_init = session.get("agent") is None and session.get("agent_error") is not None
failed_ready = session.get("agent_ready") if failed_agent_init else None
if failed_agent_init:
if failed_ready is None:
return _err(rid, 5032, session.get("agent_error") or "agent initialization failed")
if not failed_ready.wait(timeout=30.0):
return _err(rid, 5032, "agent initialization timed out")
failed_agent_init = (
failed_agent_init and session.get("agent") is None and session.get("agent_error") is not None
and session.get("agent_ready") is failed_ready and failed_ready.is_set())
if session.get("agent") is None and not explicit_provider.strip() and not failed_agent_init:
_start_agent_build(sid, session)
if init_err := _cfgset_await_agent(session, rid):
return init_err
with _session_profile_runtime_scope(session):
result = _apply_model_switch(sid, session, value, confirm_expensive_model=confirmed,
parsed_flags=parsed_flags)
if failed_agent_init and not result.get("confirm_required"):
_restart_completed_failed_agent_build(sid, session, failed_ready)
if init_err := _cfgset_await_agent(session, rid):
return init_err
with _session_profile_runtime_scope(session):
result = _apply_model_switch(
sid, session, value, confirm_expensive_model=confirmed, parsed_flags=parsed_flags
)
if failed_agent_init and not result.get("confirm_required"):
_restart_completed_failed_agent_build(sid, session, failed_ready)
if init_err := _cfgset_await_agent(session, rid):
return init_err
with _session_profile_runtime_scope(session):
_persist_live_session_runtime(session)
else:
result = _apply_model_switch("", {"agent": None}, value, confirm_expensive_model=confirmed)
return _cfgset_model_ok(
rid, key, result["value"], result["warning"], result.get("confirm_required", False),
result.get("confirm_message", ""), result.get("scope", "session"))
except Exception as e:
return _err(rid, 5001, str(e))
_persist_live_session_runtime(session)
else:
result = _apply_model_switch("", {"agent": None}, value, confirm_expensive_model=confirmed)
return _cfgset_model_ok(
rid, key, result["value"], result["warning"], result.get("confirm_required", False),
result.get("confirm_message", ""), result.get("scope", "session"))
_FAST_WORDS = {"fast": "fast", "on": "fast", "normal": "normal", "off": "normal",
@@ -162,10 +161,9 @@ def _set_fast(rid, params, key, value, session):
current_tier = session["create_service_tier_override"] or None
else:
current_tier = _load_service_tier()
current_fast = current_tier == "priority"
if raw == "status":
return _ok(rid, {"key": key, "value": {"priority": "fast", None: "normal"}.get(current_tier, current_tier)})
toggled = ("normal" if current_fast else "fast") if raw in {"", "toggle"} else None
return _kv(rid, key, {"priority": "fast", None: "normal"}.get(current_tier, current_tier))
toggled = ("normal" if current_tier == "priority" else "fast") if raw in {"", "toggle"} else None
nv = _FAST_WORDS.get(raw, toggled)
if nv is None:
return _err(rid, 4002, f"unknown fast mode: {value}")
@@ -201,17 +199,17 @@ def _set_fast(rid, params, key, value, session):
agent.request_overrides = current_overrides
_persist_live_session_runtime(session)
_emit_session_info(params.get("session_id", ""), session)
return _ok(rid, {"key": key, "value": nv})
return _kv(rid, key, nv)
def _set_busy(rid, params, key, value, session):
raw = _word(value)
if raw in {"", "status"}:
return _ok(rid, {"key": key, "value": _load_busy_input_mode()})
return _kv(rid, key, _load_busy_input_mode())
if raw not in {"queue", "steer", "interrupt"}:
return _err(rid, 4002, f"unknown busy mode: {value}")
_write_config_key("display.busy_input_mode", raw)
return _ok(rid, {"key": key, "value": raw})
return _kv(rid, key, raw)
def _set_verbose(rid, params, key, value, session):
@@ -229,7 +227,7 @@ def _set_verbose(rid, params, key, value, session):
session["tool_progress_mode"] = nv
if session.get("agent") is not None:
session["agent"].verbose_logging = nv == "verbose"
return _ok(rid, {"key": key, "value": nv})
return _kv(rid, key, nv)
def _set_focus(rid, params, key, value, session):
@@ -242,15 +240,14 @@ def _set_focus(rid, params, key, value, session):
if action == "usage":
return _err(rid, 4002, f"unknown focus value: {value} (use on|off|status)")
if action == "status" or target is None:
return _ok(rid, {"key": key, "value": "on" if cur_focus else "off", "tool_progress": _load_tool_progress_mode()})
return _kv(rid, key, "on" if cur_focus else "off", tool_progress=_load_tool_progress_mode())
if target:
saved = (cur_focus and d_f.get("focus_saved_tool_progress")) or _load_tool_progress_mode()
_write_config_key("display.focus_saved_tool_progress", normalize_tool_progress_mode(saved))
_write_config_key("display.tool_progress", FOCUS_TOOL_PROGRESS_MODE)
effective = FOCUS_TOOL_PROGRESS_MODE
else:
effective = normalize_tool_progress_mode(d_f.get("focus_saved_tool_progress") or "all")
_write_config_key("display.tool_progress", effective)
_write_config_key("display.tool_progress", effective)
_write_config_key("display.focus_view", bool(target))
if session:
session["focus_view"] = bool(target)
@@ -258,7 +255,7 @@ def _set_focus(rid, params, key, value, session):
if session.get("agent") is not None:
with contextlib.suppress(Exception):
session["agent"].tool_progress_mode = effective
return _ok(rid, {"key": key, "value": "on" if target else "off", "tool_progress": effective})
return _kv(rid, key, "on" if target else "off", tool_progress=effective)
def _set_approval_mode(rid, params, key, value, session):
@@ -267,42 +264,40 @@ def _set_approval_mode(rid, params, key, value, session):
return _err(rid, 4002, f"unknown approval mode: {value}; pick one of manual|smart|off")
_write_config_key("approvals.mode", raw)
_emit_all_session_info()
return _ok(rid, {"key": "approvals.mode", "value": raw})
return _kv(rid, "approvals.mode", raw)
@_cfgset_guarded
def _set_yolo(rid, params, key, value, session):
# scope="session" (default; Shift+Tab) toggles ONLY this session's flag. scope="global"
# (Shift+click the zap) flips persistent approvals.mode between "off" and "manual".
scope = _word(params.get("scope") or "session")
try:
from tools.approval import disable_session_yolo, enable_session_yolo, is_session_yolo_enabled
raw = _word(value)
from tools.approval import disable_session_yolo, enable_session_yolo, is_session_yolo_enabled
raw = _word(value)
def _resolve_toggle(current: bool) -> bool:
return _BOOL_WORDS.get(raw, not current)
if scope == "global":
from tools.approval import _normalize_approval_mode
appr = _load_cfg().get("approvals")
appr = appr if isinstance(appr, dict) else {}
enable = _resolve_toggle(_normalize_approval_mode(appr.get("mode", "manual")) == "off")
# Binary affordance: no restore of a prior "smart"/custom mode (those live in config.yaml).
_write_config_key("approvals.mode", "off" if enable else "manual")
_emit_all_session_info() # reflect the flip in every live indicator
return _ok(rid, {"key": key, "value": "1" if enable else "0", "scope": "global"})
if session:
skey = session["session_key"]
enable = _resolve_toggle(is_session_yolo_enabled(skey))
(enable_session_yolo if enable else disable_session_yolo)(skey)
_emit_session_info(params.get("session_id", ""), session)
def _resolve_toggle(current: bool) -> bool:
return _BOOL_WORDS.get(raw, not current)
if scope == "global":
from tools.approval import _normalize_approval_mode
appr = _load_cfg().get("approvals")
appr = appr if isinstance(appr, dict) else {}
enable = _resolve_toggle(_normalize_approval_mode(appr.get("mode", "manual")) == "off")
# Binary affordance: no restore of a prior "smart"/custom mode (those live in config.yaml).
_write_config_key("approvals.mode", "off" if enable else "manual")
_emit_all_session_info() # reflect the flip in every live indicator
return _kv(rid, key, "1" if enable else "0", scope="global")
if session:
skey = session["session_key"]
enable = _resolve_toggle(is_session_yolo_enabled(skey))
(enable_session_yolo if enable else disable_session_yolo)(skey)
_emit_session_info(params.get("session_id", ""), session)
else:
enable = _resolve_toggle(is_truthy_value(os.environ.get("HERMES_YOLO_MODE")))
if enable:
os.environ["HERMES_YOLO_MODE"] = "1"
else:
enable = _resolve_toggle(is_truthy_value(os.environ.get("HERMES_YOLO_MODE")))
if enable:
os.environ["HERMES_YOLO_MODE"] = "1"
else:
os.environ.pop("HERMES_YOLO_MODE", None)
return _ok(rid, {"key": key, "value": "1" if enable else "0", "scope": "session"})
except Exception as e:
return _err(rid, 5001, str(e))
os.environ.pop("HERMES_YOLO_MODE", None)
return _kv(rid, key, "1" if enable else "0", scope="session")
# /reasoning display words: (accepted inputs, reported value, display field, sections.thinking,
@@ -314,35 +309,33 @@ _REASONING_DISPLAY_WORDS = (
({"clamp", "collapse", "short"}, "clamp", {"reasoning_full": False}, "collapsed", None))
@_cfgset_guarded
def _set_reasoning(rid, params, key, value, session):
try:
from hermes_constants import parse_reasoning_effort
arg = _word(value)
scope = _word(params.get("scope"))
for words, reported, fields, thinking, show in _REASONING_DISPLAY_WORDS:
if arg in words:
_write_display_sections(sections={"thinking": thinking}, **fields)
if show is not None and session:
session["show_reasoning"] = show
return _ok(rid, {"key": key, "value": reported})
parsed = parse_reasoning_effort(arg)
if parsed is None:
return _err(rid, 4002, f"unknown reasoning value: {value}")
if scope == "global" or session is None:
_write_config_key("agent.reasoning_effort", arg)
if session is not None:
session.pop("create_reasoning_override", None)
else:
# Session-scoped like the gateway's `/reasoning <level>`; otherwise every desktop
# model-menu pick rewrote the global default.
session["create_reasoning_override"] = parsed
if session and session.get("agent") is not None:
session["agent"].reasoning_config = parsed
_persist_live_session_runtime(session)
_emit_session_info(params.get("session_id", ""), session)
return _ok(rid, {"key": key, "value": arg})
except Exception as e:
return _err(rid, 5001, str(e))
from hermes_constants import parse_reasoning_effort
arg = _word(value)
scope = _word(params.get("scope"))
for words, reported, fields, thinking, show in _REASONING_DISPLAY_WORDS:
if arg in words:
_write_display_sections(sections={"thinking": thinking}, **fields)
if show is not None and session:
session["show_reasoning"] = show
return _kv(rid, key, reported)
parsed = parse_reasoning_effort(arg)
if parsed is None:
return _err(rid, 4002, f"unknown reasoning value: {value}")
if scope == "global" or session is None:
_write_config_key("agent.reasoning_effort", arg)
if session is not None:
session.pop("create_reasoning_override", None)
else:
# Session-scoped like the gateway's `/reasoning <level>`; otherwise every desktop
# model-menu pick rewrote the global default.
session["create_reasoning_override"] = parsed
if session and session.get("agent") is not None:
session["agent"].reasoning_config = parsed
_persist_live_session_runtime(session)
_emit_session_info(params.get("session_id", ""), session)
return _kv(rid, key, arg)
def _set_details_mode(rid, params, key, value, session):
@@ -350,7 +343,7 @@ def _set_details_mode(rid, params, key, value, session):
if nv not in _DETAIL_MODES:
return _err(rid, 4002, f"unknown details_mode: {value}")
_write_display_sections(sections={section: nv for section in _DETAIL_SECTION_NAMES}, details_mode=nv)
return _ok(rid, {"key": key, "value": nv})
return _kv(rid, key, nv)
def _set_details_section(rid, params, key, value, session):
@@ -366,7 +359,7 @@ def _set_details_section(rid, params, key, value, session):
return _err(rid, 4002, f"unknown details_mode: {value}")
else:
_write_display_sections(sections={section: nv})
return _ok(rid, {"key": key, "value": nv})
return _kv(rid, key, nv)
def _set_thinking_mode(rid, params, key, value, session):
@@ -376,17 +369,27 @@ def _set_thinking_mode(rid, params, key, value, session):
_write_config_key("display.thinking_mode", nv)
# Backward compatibility bridge: keep details_mode aligned.
_write_config_key("display.details_mode", "expanded" if nv == "full" else "collapsed")
return _ok(rid, {"key": key, "value": nv})
return _kv(rid, key, nv)
def _set_density(rid, params, key, value, session):
return _toggle_display_bool(rid, key, value, cfg_key="tui_compact", on_words={"on"}, off_words={"off"})
# on/off/toggle display booleans: key -> (display field, on words, off words).
_DISPLAY_BOOLS = {
"density": ("tui_compact", {"on"}, {"off"}),
"battery": ("battery", {"on", "true", "yes"}, {"off", "false", "no"})}
def _set_battery(rid, params, key, value, session):
return _toggle_display_bool(
rid, key, value, cfg_key="battery", on_words={"on", "true", "yes"}, off_words={"off", "false", "no"}
)
def _set_display_bool(rid, params, key, value, session):
cfg_key, on_words, off_words = _DISPLAY_BOOLS[key]
raw = _word(value)
cur_b = bool(_display_cfg().get(cfg_key, False))
if raw in {"", "toggle"}:
nv_b = not cur_b
elif raw in on_words or raw in off_words:
nv_b = raw in on_words
else:
return _err(rid, 4002, f"unknown {key} value: {value}")
_write_config_key(f"display.{cfg_key}", nv_b)
return _kv(rid, key, "on" if nv_b else "off")
def _set_theme(rid, params, key, value, session):
@@ -395,7 +398,7 @@ def _set_theme(rid, params, key, value, session):
if raw not in {"auto", "light", "dark"}:
return _err(rid, 4002, f"unknown theme value: {value} (use auto|light|dark)")
_write_config_key("display.tui_theme", raw)
return _ok(rid, {"key": key, "value": raw})
return _kv(rid, key, raw)
def _set_statusbar(rid, params, key, value, session):
@@ -408,13 +411,11 @@ def _set_statusbar(rid, params, key, value, session):
else:
return _err(rid, 4002, f"unknown statusbar value: {value}")
_write_config_key("display.tui_statusbar", nv)
return _ok(rid, {"key": key, "value": nv})
return _kv(rid, key, nv)
def _set_mouse(rid, params, key, value, session):
# Explicit None check so falsy non-string inputs (0, False) reach the alias map as
# themselves (-> 'off') instead of toggling.
raw = ("" if value is None else str(value)).strip().lower()
raw = _raw_word(value) # 0/False reach the alias map as themselves (-> 'off'), not toggle
current = _display_mouse_tracking(_display_cfg())
if raw in {"", "toggle"}:
nv = "all" if current == "off" else "off"
@@ -423,16 +424,15 @@ def _set_mouse(rid, params, key, value, session):
else:
return _err(rid, 4002, f"unknown mouse value: {value}")
_write_config_key("display.mouse_tracking", nv)
return _ok(rid, {"key": key, "value": nv})
return _kv(rid, key, nv)
def _set_indicator(rid, params, key, value, session):
# Explicit None check so falsy non-string inputs (0, False, []) surface in the error message.
raw = ("" if value is None else str(value)).strip().lower()
raw = _raw_word(value) # 0/False/[] surface in the error message
if raw not in INDICATOR_STYLES:
return _err(rid, 4002, f"unknown indicator: {raw!r}; pick one of {'|'.join(INDICATOR_STYLES)}")
_write_config_key("display.tui_status_indicator", raw)
return _ok(rid, {"key": key, "value": raw})
return _kv(rid, key, raw)
def _set_cwd(rid, params, key, value, session):
@@ -444,41 +444,39 @@ def _set_cwd(rid, params, key, value, session):
return _err(rid, 4002, f"working directory does not exist: {raw}")
_write_config_key("terminal.cwd", cwd)
os.environ["TERMINAL_CWD"] = cwd
return _ok(rid, {"key": "terminal.cwd", "value": cwd, "cwd": cwd, "branch": _git_branch_for_cwd(cwd)})
return _kv(rid, "terminal.cwd", cwd, cwd=cwd, branch=_git_branch_for_cwd(cwd))
@_cfgset_guarded
def _set_prompt_like(rid, params, key, value, session):
try:
cfg = _load_cfg_raw() # write-back round-trip ("prompt" saves cfg)
resp = {"key": key, "value": value}
if key == "prompt":
if value == "clear":
cfg.pop("custom_prompt", None)
resp["value"] = ""
else:
cfg["custom_prompt"] = value
_save_cfg(cfg)
elif key == "personality":
pname, new_prompt = _validate_personality(str(value or ""), cfg)
# Personality persists through hermes_cli.personality (single owner), never the
# user-owned global system prompt.
from hermes_cli.personality import persist_personality
persist_personality(pname)
resp["value"] = str(value or "none")
history_reset, info = _apply_personality_to_session(params.get("session_id", ""), session, new_prompt, pname)
resp["history_reset"] = history_reset
if info is not None:
resp["info"] = info
cfg = _load_cfg_raw() # write-back round-trip ("prompt" saves cfg)
resp = {"key": key, "value": value}
if key == "prompt":
if value == "clear":
cfg.pop("custom_prompt", None)
resp["value"] = ""
else:
_write_config_key(f"display.{key}", value)
if key == "skin":
# Every surface repaints; sync the watcher baseline so the poll loop doesn't
# re-broadcast the skin this RPC just applied.
_broadcast_global_event("skin.changed", resolve_skin())
_note_skin_broadcast()
return _ok(rid, resp)
except Exception as e:
return _err(rid, 5001, str(e))
cfg["custom_prompt"] = value
_save_cfg(cfg)
elif key == "personality":
pname, new_prompt = _validate_personality(str(value or ""), cfg)
# Personality persists through hermes_cli.personality (single owner), never the
# user-owned global system prompt.
from hermes_cli.personality import persist_personality
persist_personality(pname)
resp["value"] = str(value or "none")
history_reset, info = _apply_personality_to_session(params.get("session_id", ""), session, new_prompt, pname)
resp["history_reset"] = history_reset
if info is not None:
resp["info"] = info
else:
_write_config_key(f"display.{key}", value)
if key == "skin":
# Every surface repaints; sync the watcher baseline so the poll loop doesn't
# re-broadcast the skin this RPC just applied.
_broadcast_global_event("skin.changed", resolve_skin())
_note_skin_broadcast()
return _ok(rid, resp)
def _set_display_toggle(rid, params, key, value, session):
@@ -486,7 +484,7 @@ def _set_display_toggle(rid, params, key, value, session):
if on is None:
return _err(rid, 4002, f"{key} takes true or false")
_write_config_key(key, on)
return _ok(rid, {"key": key, "value": on})
return _kv(rid, key, on)
# ── dispatch
@@ -495,8 +493,8 @@ _CONFIG_SETTERS = {
"model": _set_model, "fast": _set_fast, "busy": _set_busy, "verbose": _set_verbose, "focus": _set_focus,
"approval_mode": _set_approval_mode, "approvals.mode": _set_approval_mode, "yolo": _set_yolo,
"reasoning": _set_reasoning, "details_mode": _set_details_mode, "thinking_mode": _set_thinking_mode,
"density": _set_density, "battery": _set_battery, "theme": _set_theme, "statusbar": _set_statusbar,
"mouse": _set_mouse, "indicator": _set_indicator,
"density": _set_display_bool, "battery": _set_display_bool, "theme": _set_theme,
"statusbar": _set_statusbar, "mouse": _set_mouse, "indicator": _set_indicator,
"cwd": _set_cwd, "terminal.cwd": _set_cwd, "workdir": _set_cwd,
"prompt": _set_prompt_like, "personality": _set_prompt_like, "skin": _set_prompt_like}