fix: hide attachment path markers in chat UI

This commit is contained in:
AJV20
2026-05-31 20:52:33 -04:00
committed by nesquena-hermes
parent 36fac9d0a3
commit ec704356ac
5 changed files with 50 additions and 4 deletions

View File

@@ -2858,6 +2858,10 @@ def all_sessions(diag=None):
return result
def _strip_attached_files_marker(text: str) -> str:
return re.sub(r"\n\n\[Attached files: [^\]]+\]$", "", str(text or "")).strip()
def title_from(messages, fallback: str='Untitled'):
"""Derive a session title from the first user message."""
for m in messages:
@@ -2865,7 +2869,7 @@ def title_from(messages, fallback: str='Untitled'):
c = m.get('content', '')
if isinstance(c, list):
c = ' '.join(p.get('text', '') for p in c if isinstance(p, dict) and p.get('type') == 'text')
text = str(c).strip()
text = _strip_attached_files_marker(str(c))
if text:
return text[:64]
return fallback

View File

@@ -3508,7 +3508,11 @@ function _collapseSessionLineageForSidebar(sessions){
}
function _sessionDisplayTitle(s){
const title=String((s&&(s.display_title||s._state_db_title||s.title))||'Untitled').trim();
const rawTitle=String((s&&(s.display_title||s._state_db_title||s.title))||'Untitled').trim();
const strip=(typeof _stripAttachedFilesMarker==='function')
? _stripAttachedFilesMarker
: (text)=>String(text||'').replace(/\n\n\[Attached files: [^\]]+\]$/,'').trim();
const title=strip(rawTitle);
return title||'Untitled';
}

View File

@@ -3564,6 +3564,10 @@ function renderMd(raw){
return s;
}
function _stripAttachedFilesMarkerForDisplay(text){
return String(text||'').replace(/\n\n\[Attached files: [^\]]+\]$/,'').trim();
}
function setStatus(t){
if(!t)return;
showToast(t, 4000);
@@ -6530,7 +6534,7 @@ function renderMessages(options){
if(!isUser&&_isMarkerOnlyAssistantCompressionMessage(m)){
content='**Error:** No response received after context compression. Please retry.';
}
const displayContent=isUser?_stripWorkspaceDisplayPrefix(content):content;
const displayContent=isUser?_stripAttachedFilesMarkerForDisplay(_stripWorkspaceDisplayPrefix(content)):content;
if(thinkingText&&!isUser){
thinkingText=_stripVisibleAssistantEchoFromThinking(thinkingText, displayContent);
}

View File

@@ -22,6 +22,40 @@ def test_image_uploads_use_server_path_in_attached_files_context():
assert "uploadedPaths=uploaded.map(u=>u&&u.path?u.path" in src
def test_attached_files_context_is_hidden_from_user_message_display():
"""Persist full attachment paths for the agent without showing them in chat."""
ui_src = (ROOT / "static" / "ui.js").read_text(encoding="utf-8")
assert "function _stripAttachedFilesMarkerForDisplay" in ui_src
assert "_stripAttachedFilesMarkerForDisplay(_stripWorkspaceDisplayPrefix(content))" in ui_src
assert "dataset.rawText=String(displayContent).trim()" in ui_src
def test_attached_files_context_is_hidden_from_sidebar_titles():
"""Sidebar rows should not expose absolute uploaded image paths in titles."""
sessions_src = (ROOT / "static" / "sessions.js").read_text(encoding="utf-8")
assert "function _stripAttachedFilesMarker" in sessions_src
assert "? _stripAttachedFilesMarker" in sessions_src
assert "replace(/\\n\\n\\[Attached files: [^\\]]+\\]$/" in sessions_src
def test_server_provisional_titles_strip_attached_files_context():
"""Server-generated provisional titles must not include the path suffix."""
from api.models import title_from
title = title_from([
{
"role": "user",
"content": "why is llm wiki not working?\n\n[Attached files: /tmp/private/Screenshot.png]",
}
])
assert title == "why is llm wiki not working?"
assert "Attached files" not in title
assert "/tmp/private" not in title
def test_duplicate_upload_response_reports_actual_stored_filename(tmp_path, monkeypatch):
"""Duplicate upload names should report the suffixed stored basename."""
monkeypatch.setenv("HERMES_WEBUI_ATTACHMENT_DIR", str(tmp_path))

View File

@@ -35,7 +35,7 @@ def test_user_render_uses_stripped_display_content_without_preempting_context_ca
assert loop_end != -1, "assistant render branch not found after user branch"
render_prefix = src[loop_start:loop_end]
display_idx = render_prefix.find("const displayContent=isUser?_stripWorkspaceDisplayPrefix(content):content;")
display_idx = render_prefix.find("const displayContent=isUser?_stripAttachedFilesMarkerForDisplay(_stripWorkspaceDisplayPrefix(content)):content;")
context_idx = render_prefix.find("if(_isContextCompactionMessage(m))")
user_idx = render_prefix.find("if(isUser)")
assert display_idx != -1, "display content stripper not used in render loop"