From 905b3eba5eea940daa8e63bfa250e9610dcc6ce0 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Thu, 21 May 2026 17:43:56 +0000 Subject: [PATCH] =?UTF-8?q?Stage=20398:=20PR=20#2700=20=E2=80=94=20feat:?= =?UTF-8?q?=20make=20pinned=20session=20limit=20configurable=20(builds=20o?= =?UTF-8?q?n=20shipped=20#2614=203-cap)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: ai-ag2026 --- api/config.py | 13 +++ api/routes.py | 5 +- static/boot.js | 2 + static/index.html | 5 + static/panels.js | 12 +++ static/sessions.js | 14 ++- ...test_configurable_pinned_sessions_limit.py | 100 ++++++++++++++++++ tests/test_issue2508_session_pin_cap.py | 10 +- 8 files changed, 152 insertions(+), 9 deletions(-) create mode 100644 tests/test_configurable_pinned_sessions_limit.py diff --git a/api/config.py b/api/config.py index 77127bf7..24ef46a8 100644 --- a/api/config.py +++ b/api/config.py @@ -4324,6 +4324,7 @@ _SETTINGS_DEFAULTS = { "font_size": "default", # small | default | large | xlarge "session_jump_buttons": False, # show Start/End transcript jump pills "session_endless_scroll": False, # auto-load older transcript pages while scrolling upward + "pinned_sessions_limit": 3, # maximum active pinned sessions shown in the sidebar "hidden_tabs": [], # sidebar tab panel names hidden by user (e.g. ["tasks","kanban"]); chat and settings are always visible "language": "en", # UI locale code; must match a key in static/i18n.js LOCALES "bot_name": os.getenv( @@ -4452,6 +4453,9 @@ _SETTINGS_ENUM_VALUES = { "auto_title_refresh_every": {"0", "5", "10", "20"}, "busy_input_mode": {"queue", "interrupt", "steer"}, } +_SETTINGS_INT_RANGES = { + "pinned_sessions_limit": (1, 99), +} _SETTINGS_BOOL_KEYS = { "onboarding_completed", "show_token_usage", @@ -4512,6 +4516,15 @@ def save_settings(settings: dict) -> dict: # Validate enum-constrained keys if k in _SETTINGS_ENUM_VALUES and v not in _SETTINGS_ENUM_VALUES[k]: continue + # Validate bounded integer settings. + if k in _SETTINGS_INT_RANGES: + try: + v = int(v) + except (TypeError, ValueError): + continue + min_value, max_value = _SETTINGS_INT_RANGES[k] + if v < min_value or v > max_value: + continue # Validate language codes (BCP-47-like: 'en', 'zh', 'fr', 'zh-CN') if k == "language" and ( not isinstance(v, str) or not _SETTINGS_LANG_RE.match(v) diff --git a/api/routes.py b/api/routes.py index c7f287ac..54c6261b 100644 --- a/api/routes.py +++ b/api/routes.py @@ -5767,8 +5767,9 @@ def handle_post(handler, parsed) -> bool: if getattr(existing, "pinned", False) and not getattr(existing, "archived", False) ) pinned_ids.discard(body["session_id"]) - if len(pinned_ids) >= 3: - return bad(handler, "Up to 3 sessions can be pinned. Unpin one before pinning another.", 400) + pinned_sessions_limit = int(load_settings().get("pinned_sessions_limit", 3) or 3) + if len(pinned_ids) >= pinned_sessions_limit: + return bad(handler, f"Up to {pinned_sessions_limit} sessions can be pinned. Unpin one before pinning another.", 400) # Mark in-memory pin state under LOCK so concurrent pin # requests see the increment immediately, even before # save() finishes flushing to disk. diff --git a/static/boot.js b/static/boot.js index 61688c49..9b48b910 100644 --- a/static/boot.js +++ b/static/boot.js @@ -1451,6 +1451,7 @@ function applyBotName(){ window._showThinking=s.show_thinking!==false; window._simplifiedToolCalling=s.simplified_tool_calling!==false; window._sidebarDensity=(s.sidebar_density==='detailed'?'detailed':'compact'); + window._pinnedSessionsLimit=parseInt(s.pinned_sessions_limit||3,10)||3; window._busyInputMode=(s.busy_input_mode||'queue'); window._sessionEndlessScrollEnabled=!!s.session_endless_scroll; window._botName=s.bot_name||'Hermes'; @@ -1539,6 +1540,7 @@ function applyBotName(){ window._simplifiedToolCalling=true; window._sessionJumpButtonsEnabled=false; window._sidebarDensity='compact'; + window._pinnedSessionsLimit=3; window._busyInputMode='queue'; window._sessionEndlessScrollEnabled=false; window._botName='Hermes'; diff --git a/static/index.html b/static/index.html index bb8e86c4..1659189f 100644 --- a/static/index.html +++ b/static/index.html @@ -1126,6 +1126,11 @@
Controls how much metadata the session list shows in the left sidebar.
+
+ + +
Maximum number of active conversations that can be pinned in the sidebar. Default is 3.
+