mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-07-16 20:50:18 +00:00
Merge pull request #4199 from nesquena/stage-od3
Release OD (v0.51.417): preserve sidebar issue numbers (#4193) + guard external-refresh poll (#4195)
This commit is contained in:
@@ -3,6 +3,13 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v0.51.417] — 2026-06-14 — Release OD (sidebar issue-number + external-refresh fixes, #4154/#3916)
|
||||
|
||||
### Fixed
|
||||
|
||||
- **GitHub issue numbers in session titles are no longer stripped from the sidebar (#4154).** The sidebar tag extractor treated any `#word` as an attention tag and removed it from the displayed title, so a title like "Fix #1234" lost its issue reference. Purely-numeric `#NNNN` patterns are now preserved while real attention tags (`#approval`, `#clarify`, …) still extract. (#4154)
|
||||
- **The 30-second external-refresh poll no longer fires for non-external sessions (#3916).** `refreshActiveSessionIfExternallyUpdated` now early-returns for WebUI-native sessions, which don't need the external-update reconciliation, avoiding needless reload churn. (#3916)
|
||||
|
||||
## [v0.51.416] — 2026-06-14 — Release OC (cache + single-flight /api/sessions payloads, #3791)
|
||||
|
||||
### Fixed
|
||||
|
||||
+3
-2
@@ -3650,6 +3650,7 @@ async function refreshActiveSessionIfExternallyUpdated(reason){
|
||||
if(_activeSessionExternalRefreshInFlight) return;
|
||||
if(!S.session || !S.session.session_id) return;
|
||||
if(S.busy || S.activeStreamId) return;
|
||||
if(!_isExternalSession(S.session)) return;
|
||||
// Cooldown: don't force-reload immediately after streaming ends — the
|
||||
// "done" event already delivered the final messages. Reloading here would
|
||||
// clear S.toolCalls and lose Activity.
|
||||
@@ -4726,7 +4727,7 @@ function _sessionTitleIsDefaultWebUI(rawTitle){
|
||||
|
||||
function _sessionTitleTags(rawTitle){
|
||||
if(_sessionTitleIsDefaultWebUI(rawTitle)) return [];
|
||||
return String(rawTitle||'').match(/#[\w-]+/g)||[];
|
||||
return String(rawTitle||'').match(/#(?!\d+\b)[\w-]+/g)||[];
|
||||
}
|
||||
|
||||
function _activeSessionIdForSidebar(){
|
||||
@@ -5427,7 +5428,7 @@ function renderSessionListFromCache(){
|
||||
if(isActive&&S.session&&S.session._flash)delete S.session._flash;
|
||||
const rawTitle=_sessionDisplayTitle(s);
|
||||
const tags=_sessionTitleTags(rawTitle);
|
||||
let cleanTitle=tags.length?rawTitle.replace(/#[\w-]+/g,'').trim():rawTitle;
|
||||
let cleanTitle=tags.length?rawTitle.replace(/#(?!\d+\b)[\w-]+/g,'').trim():rawTitle;
|
||||
// Guard: system prompt content must never surface as a visible session title
|
||||
if(cleanTitle.startsWith('[SYSTEM:')){
|
||||
cleanTitle='Session';
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
SESSIONS_JS = Path(__file__).resolve().parent.parent / "static" / "sessions.js"
|
||||
|
||||
|
||||
def test_refreshActiveSessionIfExternallyUpdated_exists():
|
||||
src = SESSIONS_JS.read_text(encoding="utf-8")
|
||||
assert "async function refreshActiveSessionIfExternallyUpdated" in src
|
||||
|
||||
|
||||
def test_isExternalSession_guard_present():
|
||||
src = SESSIONS_JS.read_text(encoding="utf-8")
|
||||
m = re.search(
|
||||
r"async function refreshActiveSessionIfExternallyUpdated\b.*?^}",
|
||||
src,
|
||||
re.DOTALL | re.MULTILINE,
|
||||
)
|
||||
assert m, "refreshActiveSessionIfExternallyUpdated function not found"
|
||||
body = m.group(0)
|
||||
assert re.search(r"if\s*\(\s*!\s*_isExternalSession\s*\(", body), (
|
||||
"refreshActiveSessionIfExternallyUpdated must have an early-return "
|
||||
"guard: if(!_isExternalSession(...))"
|
||||
)
|
||||
@@ -0,0 +1,67 @@
|
||||
"""Regression tests for #4154: sidebar strips GitHub issue numbers from titles.
|
||||
|
||||
The _sessionTitleTags regex must NOT treat purely numeric #NNNN patterns as
|
||||
tags. Only known attention/session-control tags like #approval, #clarify,
|
||||
#attention should be extracted.
|
||||
"""
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SESSIONS_JS = ROOT / "static" / "sessions.js"
|
||||
|
||||
|
||||
def _read_sessions_js():
|
||||
return SESSIONS_JS.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def test_sessionTitleTags_function_exists():
|
||||
"""The _sessionTitleTags function must be present in sessions.js."""
|
||||
src = _read_sessions_js()
|
||||
assert "function _sessionTitleTags" in src
|
||||
|
||||
|
||||
def test_regex_excludes_numeric_issue_numbers():
|
||||
"""The regex in _sessionTitleTags must use a negative lookahead to skip
|
||||
purely numeric #NNNN patterns (GitHub issue references)."""
|
||||
src = _read_sessions_js()
|
||||
# The fix replaces /#[\w-]+/g with /#(?!\d+\b)[\w-]+/g
|
||||
assert r"#(?!\d+\b)[\w-]+" in src, (
|
||||
"Expected negative lookahead regex /#(??!\\d+\\b)[\\w-]+/ in _sessionTitleTags"
|
||||
)
|
||||
# The old broad pattern must NOT be the only pattern present
|
||||
# (the negative lookahead variant must exist)
|
||||
|
||||
|
||||
def test_old_broad_regex_not_still_present():
|
||||
r"""The old /#[\w-]+/g pattern (without negative lookahead) must not appear
|
||||
in _sessionTitleTags or _renderOneSession tag-stripping code."""
|
||||
src = _read_sessions_js()
|
||||
# Find _sessionTitleTags function body
|
||||
match = re.search(
|
||||
r"function _sessionTitleTags\(rawTitle\)\{(.*?)\n\}",
|
||||
src,
|
||||
re.DOTALL,
|
||||
)
|
||||
assert match, "Could not locate _sessionTitleTags function body"
|
||||
body = match.group(1)
|
||||
# The function body must not contain the old broad regex
|
||||
assert "/#[\\w-]+/g" not in body.replace(" ", ""), (
|
||||
"_sessionTitleTags still uses the old broad /#[\\w-]+/g regex"
|
||||
)
|
||||
|
||||
|
||||
def test_regex_applied_in_renderOneSession_too():
|
||||
"""The tag-stripping regex in _renderOneSession must also use the
|
||||
negative lookahead so visible titles keep issue numbers."""
|
||||
src = _read_sessions_js()
|
||||
# Find the cleanTitle line
|
||||
assert r"#(?!\d+\b)[\w-]+" in src
|
||||
# Confirm it appears at least twice (once in _sessionTitleTags, once in
|
||||
# _renderOneSession)
|
||||
count = src.count(r"#(?!\d+\b)[\w-]+")
|
||||
assert count >= 2, (
|
||||
f"Expected negative lookahead regex in at least 2 locations "
|
||||
f"(_sessionTitleTags + _renderOneSession), found {count}"
|
||||
)
|
||||
Reference in New Issue
Block a user