Fix profile-aware assistant display names

This commit is contained in:
dobby-d-elf committed 2026-05-19 07:17:11 -06:00
1 parent 71c70352c1
commit 2a95c1e482
12 files changed
+120 -62

No files matched your search

+15 -4
View File
@@ -247,6 +247,13 @@ def _preferred_agent_display_name() -> str:
return name or 'Hermes'
def _preferred_agent_display_name_for_session(session) -> str:
profile = str(getattr(session, 'profile', '') or '').strip()
if profile and profile != 'default':
return profile[:1].upper() + profile[1:]
return _preferred_agent_display_name()
def _cancelled_turn_hint(agent_name: str | None = None) -> str:
name = str(agent_name or _preferred_agent_display_name()).strip() or 'Hermes'
return f'The run was cancelled by the user before {name} finished. No provider failure occurred.'
@@ -398,14 +405,14 @@ def _session_has_cancel_marker(session) -> bool:
return False
def _cancelled_turn_content(message: str = 'Task cancelled.') -> str:
def _cancelled_turn_content(message: str = 'Task cancelled.', agent_name: str | None = None) -> str:
"""Return cancelled-turn copy matching the verbose provider-error layout."""
_message = str(message or 'Task cancelled.').strip()
if not _message.endswith('.'):
_message += '.'
return (
f"**Task cancelled:** {_message}\n\n"
f"*{_cancelled_turn_hint()}*"
f"*{_cancelled_turn_hint(agent_name)}*"
)
@@ -422,9 +429,10 @@ def _persist_cancelled_turn(session, *, message: str = 'Task cancelled.') -> Non
session.pending_attachments = []
session.pending_started_at = None
if not _session_has_cancel_marker(session):
agent_name = _preferred_agent_display_name_for_session(session)
session.messages.append({
'role': 'assistant',
'content': _cancelled_turn_content(message),
'content': _cancelled_turn_content(message, agent_name),
'_error': True,
'provider_details': str(message or 'Task cancelled.').strip(),
'provider_details_label': 'Cancellation details',
@@ -5555,7 +5563,10 @@ def cancel_stream(stream_id: str) -> bool:
if not _cancel_marker_exists:
_cs.messages.append({
'role': 'assistant',
'content': _cancelled_turn_content('Task cancelled.'),
'content': _cancelled_turn_content(
'Task cancelled.',
_preferred_agent_display_name_for_session(_cs),
),
'_error': True,
'provider_details': 'Task cancelled.',
'provider_details_label': 'Cancellation details',
+4 -11
View File
@@ -1380,15 +1380,9 @@ function _buildSkinPicker(activeSkin){
}
function applyBotName(){
// Prefer profile name over global bot_name for personalised placeholder.
// If activeProfile is set and not 'default', use it (capitalised).
// Falls back to window._botName (global bot_name setting) or 'Hermes'.
let name;
if(S.activeProfile && S.activeProfile!=='default'){
name=S.activeProfile.charAt(0).toUpperCase()+S.activeProfile.slice(1);
}else{
name=window._botName||'Hermes';
}
// The saved assistant name applies to the default profile only.
// Non-default profiles use their own profile names.
const name=assistantDisplayName();
document.title=name;
const sidebarH1=document.querySelector('.sidebar-header h1');
if(sidebarH1) sidebarH1.textContent=name;
@@ -1469,7 +1463,6 @@ function applyBotName(){
setLocale(_lang);
if(typeof applyLocaleToDOM==='function')applyLocaleToDOM();
}
applyBotName();
// TTS: apply enabled state on boot so buttons show/hide correctly (#499)
if(typeof _applyTtsEnabled==='function') _applyTtsEnabled(localStorage.getItem('hermes-tts-enabled')==='true');
}catch(e){
@@ -1497,7 +1490,6 @@ function applyBotName(){
setLocale(_lang);
if(typeof applyLocaleToDOM==='function')applyLocaleToDOM();
}
applyBotName();
if(typeof _applyTtsEnabled==='function') _applyTtsEnabled(localStorage.getItem('hermes-tts-enabled')==='true');
}
// Non-blocking update check (fire-and-forget, once per tab session)
@@ -1509,6 +1501,7 @@ function applyBotName(){
}
// Fetch active profile
try{const p=await api('/api/profile/active');S.activeProfile=p.name||'default';}catch(e){S.activeProfile='default';}
applyBotName();
// Update profile chip label immediately
const profileLabel=$('profileChipLabel');
if(profileLabel) profileLabel.textContent=S.activeProfile||'default';
+22 -22
View File
@@ -554,7 +554,7 @@ const LOCALES = {
settings_label_sync_insights: 'Sync to insights',
settings_label_check_updates: 'Check for updates',
settings_label_whats_new_summary: "Summarize What's New with AI",
settings_label_bot_name: 'Assistant Name',
settings_label_bot_name: 'Default assistant name',
settings_label_password: 'Access Password',
settings_saved: 'Settings saved',
settings_save_failed: 'Save failed: ',
@@ -793,7 +793,7 @@ const LOCALES = {
settings_desc_sync_insights: 'Mirrors WebUI token usage to state.db so hermes /insights includes browser session data. Off by default.',
settings_desc_check_updates: 'Show a banner when newer versions of the WebUI or Agent are available. Runs a background git fetch periodically.',
settings_desc_whats_new_summary: "Changes the What's New action from opening the raw diff first to generating a short, human-readable summary. The regular diff comparison stays available after the summary.",
settings_desc_bot_name: 'Display name for the assistant throughout the UI. Defaults to Hermes.',
settings_desc_bot_name: 'Used for the default profile only. Other profiles use their own profile names.',
settings_desc_password: 'Enter a new password to set or change it. Leave blank to keep current setting.',
password_placeholder: 'Enter new password…',
password_env_var_locked: 'The HERMES_WEBUI_PASSWORD environment variable is currently set and takes precedence. Unset it and restart the server to manage the password from here.',
@@ -1773,7 +1773,7 @@ const LOCALES = {
settings_label_sync_insights: 'Sincronizza con insights',
settings_label_check_updates: 'Verifica aggiornamenti',
settings_label_whats_new_summary: "Summarize What's New with AI",
settings_label_bot_name: 'Nome Assistente',
settings_label_bot_name: 'Nome assistente predefinito',
settings_label_password: 'Password di Accesso',
settings_saved: 'Impostazioni salvate',
settings_save_failed: 'Salvataggio fallito: ',
@@ -2004,7 +2004,7 @@ const LOCALES = {
settings_desc_sync_insights: 'Rispecchia l\'uso token WebUI su state.db così hermes /insights include i dati delle sessioni browser. Disattivato per impostazione predefinita.',
settings_desc_check_updates: 'Mostra un banner quando sono disponibili versioni più recenti della WebUI o dell\'Agente. Esegue un git fetch in background periodicamente.',
settings_desc_whats_new_summary: "Changes the What's New action from opening the raw diff first to generating a short, human-readable summary. The regular diff comparison stays available after the summary.",
settings_desc_bot_name: 'Nome visualizzato per l\'assistente in tutta l\'interfaccia. Predefinito: Hermes.',
settings_desc_bot_name: 'Usato solo per il profilo predefinito. Gli altri profili usano i propri nomi.',
settings_desc_password: 'Inserisci una nuova password per impostarla o cambiarla. Lascia vuoto per mantenere l\'impostazione attuale.',
password_placeholder: 'Inserisci nuova password…',
password_env_var_locked: 'La variabile d\'ambiente HERMES_WEBUI_PASSWORD è attualmente impostata e ha la precedenza. Rimuovila e riavvia il server per gestire la password da qui.',
@@ -2984,7 +2984,7 @@ const LOCALES = {
settings_label_sync_insights: 'インサイトに同期',
settings_label_check_updates: 'アップデートを確認',
settings_label_whats_new_summary: "Summarize What's New with AI",
settings_label_bot_name: 'アシスタント名',
settings_label_bot_name: 'デフォルトのアシスタント名',
settings_label_password: 'アクセスパスワード',
settings_saved: '設定を保存しました',
settings_save_failed: '保存失敗: ',
@@ -3220,7 +3220,7 @@ const LOCALES = {
settings_desc_sync_insights: 'WebUI のトークン使用量を state.db にミラーし、hermes /insights にブラウザセッションのデータを含めます。デフォルトはオフ。',
settings_desc_check_updates: 'WebUI または Agent の新しいバージョンが利用可能な時にバナーを表示します。バックグラウンドで定期的に git fetch を実行します。',
settings_desc_whats_new_summary: "Changes the What's New action from opening the raw diff first to generating a short, human-readable summary. The regular diff comparison stays available after the summary.",
settings_desc_bot_name: 'UI 全体で表示されるアシスタントの名前。デフォルトは Hermes。',
settings_desc_bot_name: 'デフォルトプロファイルでのみ使用されます。他のプロファイルはそれぞれのプロファイル名を使用します。',
settings_desc_password: '新しいパスワードを入力すると設定または変更します。空欄なら現在の設定を維持。',
password_placeholder: '新しいパスワードを入力…',
password_env_var_locked: '現在 HERMES_WEBUI_PASSWORD 環境変数が設定されており優先されます。ここで管理するには変数を解除してサーバーを再起動してください。',
@@ -4006,7 +4006,7 @@ const LOCALES = {
settings_label_sync_insights: 'Синхронизировать с Insights',
settings_label_check_updates: 'Проверять обновления',
settings_label_whats_new_summary: "Summarize What's New with AI",
settings_label_bot_name: 'Имя помощника',
settings_label_bot_name: 'Имя помощника по умолчанию',
settings_label_password: 'Пароль доступа',
settings_saved: 'Настройки сохранены',
settings_save_failed: 'Не удалось сохранить: ',
@@ -4191,7 +4191,7 @@ const LOCALES = {
settings_desc_sync_insights: 'Синхронизирует использование токенов WebUI в state.db, чтобы Hermes /insights включал данные браузерных сеансов. Выключено по умолчанию.',
settings_desc_check_updates: 'Показывает баннер, когда доступны более новые версии WebUI или Agent. Периодически выполняет git fetch в фоне.',
settings_desc_whats_new_summary: "Changes the What's New action from opening the raw diff first to generating a short, human-readable summary. The regular diff comparison stays available after the summary.",
settings_desc_bot_name: 'Отображаемое имя помощника во всём интерфейсе. По умолчанию Hermes.',
settings_desc_bot_name: 'Используется только для профиля по умолчанию. Другие профили используют свои имена.',
settings_desc_password: 'Введите новый пароль, чтобы задать или изменить его. Оставьте пустым, чтобы сохранить текущую настройку.',
password_placeholder: 'Введите новый пароль…',
password_env_var_locked: 'Переменная окружения HERMES_WEBUI_PASSWORD сейчас задана и имеет приоритет. Сбросьте её и перезапустите сервер, чтобы управлять паролем отсюда.',
@@ -5146,7 +5146,7 @@ const LOCALES = {
settings_label_sync_insights: 'Sincronizar con insights',
settings_label_check_updates: 'Buscar actualizaciones',
settings_label_whats_new_summary: "Summarize What's New with AI",
settings_label_bot_name: 'Nombre del asistente',
settings_label_bot_name: 'Nombre predeterminado del asistente',
settings_label_password: 'Contraseña de acceso',
settings_saved: 'Configuración guardada',
settings_save_failed: 'Error al guardar: ',
@@ -5342,7 +5342,7 @@ const LOCALES = {
settings_desc_sync_insights: 'Refleja el uso de tokens de la WebUI en state.db para que hermes /insights incluya datos de sesiones del navegador. Desactivado por defecto.',
settings_desc_check_updates: 'Muestra un banner cuando haya versiones más nuevas de la WebUI o del Agent. Ejecuta periódicamente un git fetch en segundo plano.',
settings_desc_whats_new_summary: "Changes the What's New action from opening the raw diff first to generating a short, human-readable summary. The regular diff comparison stays available after the summary.",
settings_desc_bot_name: 'Nombre visible del asistente en toda la UI. Por defecto es Hermes.',
settings_desc_bot_name: 'Solo se usa para el perfil predeterminado. Los otros perfiles usan sus propios nombres.',
settings_desc_password: 'Introduce una nueva contraseña para establecerla o cambiarla. Déjalo en blanco para mantener la configuración actual.',
password_placeholder: 'Introduce una contraseña nueva…',
password_env_var_locked: 'La variable de entorno HERMES_WEBUI_PASSWORD está definida y tiene prioridad. Quítala y reinicia el servidor para gestionar la contraseña desde aquí.',
@@ -6279,7 +6279,7 @@ const LOCALES = {
settings_label_sync_insights: 'Mit Insights synchronisieren',
settings_label_check_updates: 'Nach Updates suchen',
settings_label_whats_new_summary: "Summarize What's New with AI",
settings_label_bot_name: 'Assistenten-Name',
settings_label_bot_name: 'Standard-Assistentenname',
settings_label_password: 'Zugangspasswort',
settings_saved: 'Einstellungen gespeichert',
settings_save_failed: 'Speichern fehlgeschlagen: ',
@@ -6465,7 +6465,7 @@ const LOCALES = {
settings_desc_sync_insights: 'Spiegelt den WebUI-Token-Verbrauch in die state.db, sodass hermes /insights Browser-Sitzungsdaten enthält. Standardmäßig aus.',
settings_desc_check_updates: 'Zeigt ein Banner an, wenn neuere Versionen der WebUI oder des Agenten verfügbar sind. Führt regelmäßig einen Git-Fetch im Hintergrund aus.',
settings_desc_whats_new_summary: "Changes the What's New action from opening the raw diff first to generating a short, human-readable summary. The regular diff comparison stays available after the summary.",
settings_desc_bot_name: 'Anzeigename für den Assistenten in der UI. Standardmäßig Hermes.',
settings_desc_bot_name: 'Wird nur für das Standardprofil verwendet. Andere Profile verwenden ihre eigenen Namen.',
settings_desc_password: 'Geben Sie ein neues Passwort ein, um es zu setzen oder zu ändern. Leer lassen, um die aktuelle Einstellung beizubehalten.',
password_placeholder: 'Neues Passwort eingeben…',
password_env_var_locked: 'Die Umgebungsvariable HERMES_WEBUI_PASSWORD ist gesetzt und hat Vorrang. Entferne sie und starte den Server neu, um das Passwort hier zu verwalten.',
@@ -7462,7 +7462,7 @@ const LOCALES = {
settings_label_sync_insights: '同步到 insights',
settings_label_check_updates: '检查更新',
settings_label_whats_new_summary: "Summarize What's New with AI",
settings_label_bot_name: '助手名称',
settings_label_bot_name: '默认助手名称',
settings_label_password: '访问密码',
settings_saved: '设置已保存',
settings_save_failed: '保存失败:',
@@ -7721,7 +7721,7 @@ const LOCALES = {
settings_desc_sync_insights: '将 WebUI token 使用情况同步到 state.db,使 hermes /insights 包含浏览器会话数据。默认关闭。',
settings_desc_check_updates: '当有更新的 WebUI 或助手版本时显示横幅。会在后台定期执行 git fetch。',
settings_desc_whats_new_summary: "Changes the What's New action from opening the raw diff first to generating a short, human-readable summary. The regular diff comparison stays available after the summary.",
settings_desc_bot_name: '助手在 UI 中的显示名称。默认为 Hermes。',
settings_desc_bot_name: '仅用于默认个人资料。其他个人资料会使用各自的名称。',
settings_desc_password: '输入新密码以设置或更改。留空保持当前设置。',
// onboarding
onboarding_badge: '首次运行',
@@ -8623,7 +8623,7 @@ const LOCALES = {
settings_label_sync_insights: '\u540c\u6b65\u5230 insights',
settings_label_check_updates: '\u6aa2\u67e5\u66f4\u65b0',
settings_label_whats_new_summary: "Summarize What's New with AI",
settings_label_bot_name: '\u52a9\u624b\u540d\u7a31',
settings_label_bot_name: '預設助手名稱',
settings_label_password: '\u8a2a\u554f\u5bc6\u78bc',
settings_saved: '\u8a2d\u5b9a\u5df2\u5132\u5b58',
settings_save_failed: '\u5132\u5b58\u5931\u6557\uff1a',
@@ -8806,7 +8806,7 @@ const LOCALES = {
settings_desc_sync_insights: '將 WebUI token 使用情況同步到 state.db,使 hermes /insights 包含瀏覽器會話數據。預設未啟用。',
settings_desc_check_updates: '當有更新的 WebUI 或助手版本時顯示標記。將在後台正常執行 Git-Fetch。',
settings_desc_whats_new_summary: "Changes the What's New action from opening the raw diff first to generating a short, human-readable summary. The regular diff comparison stays available after the summary.",
settings_desc_bot_name: '助手在 UI 中的顯示名稱。預設未更改。',
settings_desc_bot_name: '僅用於預設個人檔案。其他個人檔案會使用各自的名稱。',
settings_desc_password: '\u8a2d\u5b9a WebUI \u767b\u5165\u5bc6\u78bc\u3002\u5047\u5982\u5df2\u8a2d\u7f6e\uff0c\u6bcf\u6b21\u52a0\u8f09\u90fd\u9700\u8981\u767b\u5165\u3002',
onboarding_password_will_enable: '\u5c07\u6703\u555f\u7528',
onboarding_password_will_replace: '\u5c07\u6703\u53d6\u4ee3',
@@ -9922,7 +9922,7 @@ const LOCALES = {
settings_label_sync_insights: 'Sincronizar para insights',
settings_label_check_updates: 'Verificar atualizações',
settings_label_whats_new_summary: "Summarize What's New with AI",
settings_label_bot_name: 'Nome do Assistente',
settings_label_bot_name: 'Nome padrão do assistente',
settings_label_password: 'Senha de Acesso',
settings_saved: 'Configurações salvas',
settings_save_failed: 'Falha ao salvar: ',
@@ -10111,7 +10111,7 @@ const LOCALES = {
settings_desc_sync_insights: 'Espelha uso de tokens para state.db.',
settings_desc_check_updates: 'Mostrar banner quando versões mais novas estiverem disponíveis.',
settings_desc_whats_new_summary: "Changes the What's New action from opening the raw diff first to generating a short, human-readable summary. The regular diff comparison stays available after the summary.",
settings_desc_bot_name: 'Nome de exibição do assistente. Padrão: Hermes.',
settings_desc_bot_name: 'Usado apenas para o perfil padrão. Outros perfis usam seus próprios nomes.',
settings_desc_password: 'Digite nova senha para definir ou trocar. Deixe em branco para manter.',
password_placeholder: 'Digite nova senha…',
password_env_var_locked: 'A variável de ambiente HERMES_WEBUI_PASSWORD está definida e tem prioridade. Remova-a e reinicie o servidor para gerenciar a senha aqui.',
@@ -11036,7 +11036,7 @@ const LOCALES = {
settings_label_sync_insights: 'Insights에 동기화',
settings_label_check_updates: '업데이트 확인',
settings_label_whats_new_summary: "Summarize What's New with AI",
settings_label_bot_name: 'Assistant 이름',
settings_label_bot_name: '기본 Assistant 이름',
settings_label_password: '접근 비밀번호',
settings_saved: '설정 저장됨',
settings_save_failed: '설정 저장 실패: ',
@@ -11224,7 +11224,7 @@ const LOCALES = {
settings_desc_sync_insights: 'WebUI 토큰 사용량을 state.db에 반영하여 hermes /insights에 브라우저 세션 데이터가 포함되도록 합니다. 기본값은 꺼짐입니다.',
settings_desc_check_updates: 'WebUI 또는 Agent의 새 버전이 있으면 배너를 표시합니다. 백그라운드에서 주기적으로 git fetch를 실행합니다.',
settings_desc_whats_new_summary: "Changes the What's New action from opening the raw diff first to generating a short, human-readable summary. The regular diff comparison stays available after the summary.",
settings_desc_bot_name: 'UI 전체에 표시되는 Assistant 이름입니다. 기본값은 Hermes입니다.',
settings_desc_bot_name: '기본 프로필에만 사용됩니다. 다른 프로필은 각 프로필 이름을 사용합니다.',
settings_desc_password: '새 비밀번호를 설정하거나 변경하려면 입력하세요. 현재 설정을 유지하려면 비워 두세요.',
password_placeholder: '새 비밀번호 입력…',
password_env_var_locked: '현재 HERMES_WEBUI_PASSWORD 환경 변수가 설정되어 있어 우선 적용됩니다. 변수를 해제하고 서버를 재시작해야 여기에서 비밀번호를 관리할 수 있습니다.',
@@ -12167,7 +12167,7 @@ const LOCALES = {
settings_label_sync_insights: 'Synchroniser avec les insights',
settings_label_check_updates: 'Vérifier les mises à jour',
settings_label_whats_new_summary: "Summarize What's New with AI",
settings_label_bot_name: 'Nom de l\'assistant',
settings_label_bot_name: 'Nom par défaut de l\'assistant',
settings_label_password: 'Mot de passe d\'accès',
settings_saved: 'Paramètres enregistrés',
settings_save_failed: 'Échec de l\'enregistrement :',
@@ -12365,7 +12365,7 @@ const LOCALES = {
settings_desc_sync_insights: 'Met en miroir l\'utilisation du jeton WebUI dans state.db afin que Hermes /insights inclut les données de session du navigateur. Désactivé par défaut.',
settings_desc_check_updates: 'Afficher une bannière lorsque des versions plus récentes de WebUI ou de l\'agent sont disponibles. Exécute périodiquement une récupération git en arrière-plan.',
settings_desc_whats_new_summary: "Changes the What's New action from opening the raw diff first to generating a short, human-readable summary. The regular diff comparison stays available after the summary.",
settings_desc_bot_name: 'Nom d’affichage de l’assistant dans l’interface utilisateur. Par défaut, Hermès.',
settings_desc_bot_name: 'Utilisé uniquement pour le profil par défaut. Les autres profils utilisent leurs propres noms.',
settings_desc_password: 'Saisissez un nouveau mot de passe pour le définir ou le modifier. Laissez vide pour conserver le paramètre actuel.',
password_placeholder: 'Entrez le nouveau mot de passe…',
password_env_var_locked: 'La variable d\'environnement HERMES_WEBUI_PASSWORD est actuellement définie et est prioritaire. Désactivez-le et redémarrez le serveur pour gérer le mot de passe à partir d\'ici.',
+2 -2
View File
@@ -1164,8 +1164,8 @@
<div style="font-size:11px;color:var(--muted);margin-top:4px" data-i18n="settings_desc_whats_new_summary">Changes the What's New action from opening the raw diff first to generating a short, human-readable summary. The regular diff comparison stays available after the summary.</div>
</div>
<div class="settings-field">
<label for="settingsBotName" data-i18n="settings_label_bot_name">Assistant Name</label>
<div style="font-size:11px;color:var(--muted);margin-bottom:6px" data-i18n="settings_desc_bot_name">Display name for the assistant throughout the UI. Defaults to Hermes.</div>
<label for="settingsBotName" data-i18n="settings_label_bot_name">Default assistant name</label>
<div style="font-size:11px;color:var(--muted);margin-bottom:6px" data-i18n="settings_desc_bot_name">Used for the default profile only. Other profiles use their own profile names.</div>
<input type="text" id="settingsBotName" placeholder="Hermes" maxlength="64" style="width:100%;padding:8px;background:var(--code-bg);color:var(--text);border:1px solid var(--border2);border-radius:6px;font-size:13px">
</div>
<button class="sm-btn" onclick="saveSettings()" style="margin-top:12px;width:100%;padding:8px;font-weight:600" data-i18n="settings_save_btn">Save Settings</button>
+2 -2
View File
@@ -1989,7 +1989,7 @@ function attachLiveStream(activeSid, streamId, uploaded=[], options={}){
// Fallback to local cancel message if API fails
if(S.session&&S.session.session_id===activeSid){
clearLiveToolCards();if(!assistantText)removeThinking();
const cancelAgentName=((window._botName||'Hermes')+'').trim()||'Hermes';
const cancelAgentName=(assistantDisplayName()+'').trim()||'Hermes';
S.messages.push({role:'assistant',content:`**Task cancelled:** Task cancelled.\n\n*The run was cancelled by the user before ${cancelAgentName} finished. No provider failure occurred.*`,provider_details:'Task cancelled.',provider_details_label:'Cancellation details',_error:true});renderMessages({preserveScroll:true});
_markSessionViewed(activeSid, S.messages.length);
}
@@ -2855,7 +2855,7 @@ function playNotificationSound(){
function sendBrowserNotification(title,body){
if(!window._notificationsEnabled||!document.hidden) return;
if(!('Notification' in window)) return;
const botName=window._botName||'Hermes';
const botName=assistantDisplayName();
if(Notification.permission==='granted'){
new Notification(title||botName,{body:body});
}else if(Notification.permission!=='denied'){
+1 -1
View File
@@ -3539,7 +3539,7 @@ async function deleteSession(sid){
if(remaining.sessions&&remaining.sessions.length){
await loadSession(remaining.sessions[0].session_id);
}else{
const _tt=$('topbarTitle');if(_tt)_tt.textContent=window._botName||'Hermes';
const _tt=$('topbarTitle');if(_tt)_tt.textContent=assistantDisplayName();
const _tm=$('topbarMeta');if(_tm)_tm.textContent='Start a new conversation';
$('msgInner').innerHTML='';
$('emptyState').style.display='';
+8 -3
View File
@@ -1,4 +1,9 @@
const S={session:null,messages:[],entries:[],busy:false,pendingFiles:[],toolCalls:[],activeStreamId:null,currentDir:'.',activeProfile:'default',showHiddenWorkspaceFiles:false};
function assistantDisplayName(){
if(S.activeProfile&&S.activeProfile!=='default') return S.activeProfile.charAt(0).toUpperCase()+S.activeProfile.slice(1);
return window._botName||'Hermes';
}
const INFLIGHT={}; // keyed by session_id while request in-flight
const SESSION_QUEUES={}; // keyed by session_id for queued follow-up turns
const MAX_UPLOAD_BYTES=(window.__HERMES_CONFIG__&&window.__HERMES_CONFIG__.maxUploadBytes)||20*1024*1024;
@@ -4663,7 +4668,7 @@ async function checkInflightOnBoot(sid) {
function syncTopbar(){
if(!S.session){
document.title=window._botName||'Hermes';
document.title=assistantDisplayName();
if(typeof syncWorkspaceDisplays==='function') syncWorkspaceDisplays();
if(typeof _syncWorkspaceHeadingState==='function') _syncWorkspaceHeadingState();
if(typeof syncModelChip==='function') syncModelChip();
@@ -4683,7 +4688,7 @@ function syncTopbar(){
}
const sessionTitle=S.session.title||t('untitled');
const _topbarTitle=$('topbarTitle');if(_topbarTitle)_topbarTitle.textContent=sessionTitle;
document.title=sessionTitle+' \u2014 '+(window._botName||'Hermes');
document.title=sessionTitle+' \u2014 '+assistantDisplayName();
const vis=S.messages.filter(m=>m&&m.role&&m.role!=='tool');
const _topbarMeta=$('topbarMeta');
if(_topbarMeta){
@@ -4815,7 +4820,7 @@ function isTpsDisplayEnabled(){
return window._showTps===true;
}
function _assistantRoleHtml(tsTitle='', tpsText=''){
const _bn=window._botName||'Hermes';
const _bn=assistantDisplayName();
const tps=(isTpsDisplayEnabled()&&tpsText)?`<span class="msg-tps-inline" title="Tokens per second">${esc(tpsText)}</span>`:'';
return `<div class="msg-role assistant" ${tsTitle?`title="${esc(tsTitle)}"`:''}><div class="role-icon assistant">${esc(_bn.charAt(0).toUpperCase())}</div><span style="font-size:12px">${esc(_bn)}</span>${tps}</div>`;
}
+51 -15
View File
@@ -11,31 +11,67 @@ class TestComposerPlaceholderProfile:
"""applyBotName() should use the profile name when activeProfile is set."""
def test_applyBotName_uses_profile_name(self):
"""applyBotName must check S.activeProfile and prefer it over global bot_name."""
"""Non-default profiles must use the profile name instead of bot_name."""
src = _src("boot.js")
assert "S.activeProfile" in src, \
"applyBotName must reference S.activeProfile"
# Should fall back to _botName when activeProfile is 'default'
assert "S.activeProfile!=='default'" in src, \
"applyBotName must skip 'default' profile (use bot_name instead)"
ui_src = _src("ui.js")
assert "function assistantDisplayName()" in ui_src, \
"assistant display name resolution should be shared"
assert "S.activeProfile&&S.activeProfile!=='default'" in ui_src, \
"assistantDisplayName must only treat the literal default profile as renamed by bot_name"
assert "assistantDisplayName()" in src, \
"applyBotName must use the shared profile-aware display name"
def test_applyBotName_capitalises_profile_name(self):
"""Profile name should be capitalised (first letter uppercase)."""
src = _src("boot.js")
m = re.search(r'function applyBotName\(\)\{.*?\n\}', src, re.DOTALL)
assert m, "applyBotName function must exist"
src = _src("ui.js")
m = re.search(r'function assistantDisplayName\(\)\{.*?\n\}', src, re.DOTALL)
assert m, "assistantDisplayName function must exist"
body = m.group(0)
assert "charAt(0).toUpperCase()" in body, \
"applyBotName must capitalise first letter of profile name"
"assistantDisplayName must capitalise first letter of profile name"
def test_applyBotName_falls_back_to_bot_name(self):
"""When no active profile, must fall back to window._botName."""
src = _src("boot.js")
m = re.search(r'function applyBotName\(\)\{.*?\n\}', src, re.DOTALL)
assert m, "applyBotName function must exist"
"""The saved assistant name applies to the default profile."""
src = _src("ui.js")
m = re.search(r'function assistantDisplayName\(\)\{.*?\n\}', src, re.DOTALL)
assert m, "assistantDisplayName function must exist"
body = m.group(0)
assert "window._botName||'Hermes'" in body, \
"applyBotName must fall back to window._botName or 'Hermes'"
"assistantDisplayName must use window._botName or 'Hermes' for the default profile"
def test_chat_surfaces_use_shared_assistant_display_name(self):
"""Chat rows, titles, notifications, and cancel copy must honor profile overrides."""
ui_src = _src("ui.js")
messages_src = _src("messages.js")
sessions_src = _src("sessions.js")
assert "document.title=assistantDisplayName();" in ui_src
assert "document.title=sessionTitle+' \\u2014 '+assistantDisplayName();" in ui_src
assert "const _bn=assistantDisplayName();" in ui_src
assert "assistantDisplayName()" in messages_src
assert "assistantDisplayName()" in sessions_src
def test_boot_applies_placeholder_after_active_profile_loads(self):
"""Boot must set the composer placeholder after S.activeProfile is known."""
src = _src("boot.js")
fetch_idx = src.find("api('/api/profile/active')")
assert fetch_idx >= 0, "boot.js should fetch the active profile during boot"
label_idx = src.find("const profileLabel=$('profileChipLabel');", fetch_idx)
assert label_idx >= 0, "profile chip sync should follow active profile fetch"
assert "applyBotName();" in src[fetch_idx:label_idx], (
"boot should apply the profile-aware assistant name after active profile resolution"
)
def test_settings_copy_names_default_assistant_scope(self):
"""The preference copy must say that only the default profile is renamed."""
index_src = _src("index.html")
i18n_src = _src("i18n.js")
assert "Default assistant name" in index_src
assert "Used for the default profile only. Other profiles use their own profile names." in index_src
assert "settings_label_bot_name: 'Default assistant name'" in i18n_src
assert (
"settings_desc_bot_name: 'Used for the default profile only. "
"Other profiles use their own profile names.'"
) in i18n_src
def test_switchToProfile_calls_applyBotName(self):
"""switchToProfile() must call applyBotName() after switching."""
+12
View File
@@ -509,6 +509,18 @@ def test_cancel_copy_uses_configured_bot_name(monkeypatch):
)
def test_cancel_copy_uses_profile_name_for_non_default_profile(monkeypatch):
"""Persisted cancellation copy should use profile names outside literal default."""
import api.streaming as streaming
monkeypatch.setattr(streaming, 'load_settings', lambda: {'bot_name': 'Obryn'})
session = type('Session', (), {'profile': 'research'})()
name = streaming._preferred_agent_display_name_for_session(session)
assert name == 'Research'
assert 'before Research finished' in streaming._cancelled_turn_content(agent_name=name)
def test_cancel_copy_falls_back_to_hermes_for_blank_bot_name(monkeypatch):
"""Blank or missing bot_name should not leak old persona copy."""
import api.streaming as streaming
@@ -98,6 +98,7 @@ const window = { _botName: 'Hermes', _defaultModel: null, _activeProvider: null
function fetch(url, opts) { calls.fetches.push({url: String(url), body: opts && opts.body || ''}); return Promise.resolve({ok: true}); }
for (const name of [
'assistantDisplayName',
'_getOptionProviderId', '_providerFromModelValue', '_modelStateForSelect',
'_findModelInDropdown', '_refreshOpenModelDropdown', '_applyModelToDropdown',
'_modelStateFromAppliedDropdown', '_persistSessionModelCorrection',
+1 -1
View File
@@ -113,7 +113,7 @@ def test_korean_settings_detail_descriptions_are_translated():
"settings_desc_external_sessions: 'CLI, Telegram, Discord, Slack 및 기타 채널의 대화를 세션 목록에 표시합니다. 클릭하여 가져오고 계속하세요.'",
"settings_desc_sync_insights: 'WebUI 토큰 사용량을 state.db에 반영하여 hermes /insights에 브라우저 세션 데이터가 포함되도록 합니다. 기본값은 꺼짐입니다.'",
"settings_desc_check_updates: 'WebUI 또는 Agent의 새 버전이 있으면 배너를 표시합니다. 백그라운드에서 주기적으로 git fetch를 실행합니다.'",
"settings_desc_bot_name: 'UI 전체에 표시되는 Assistant 이름입니다. 기본값은 Hermes입니다.'",
"settings_desc_bot_name: '기본 프로필에만 사용됩니다. 다른 프로필은 각 프로필 이름을 사용합니다.'",
"settings_desc_password: '새 비밀번호를 설정하거나 변경하려면 입력하세요. 현재 설정을 유지하려면 비워 두세요.'",
]
for entry in expected:
+1 -1
View File
@@ -218,7 +218,7 @@ def test_cancel_marker_flagged_as_error_to_skip_in_api_history():
persisting the marker to the session.
"""
src = read("api/streaming.py")
idx = src.find("'content': _cancelled_turn_content(message)")
idx = src.find("'content': _cancelled_turn_content(message")
assert idx != -1, "cancel marker content writer not found in cancel_stream()"
# Walk back to the start of the dict literal (opening brace)