mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-05-24 02:36:27 +00:00
feat: clarify profiles and workspaces
This commit is contained in:
committed by
Hermes Agent
parent
dbd2c79891
commit
b373f090bd
@@ -20,6 +20,8 @@
|
||||
|
||||
### Added
|
||||
|
||||
- **PR #2343** by @Michaelyklam (refs #2147) — The Profiles panel now includes an inline "Profiles vs workspaces" explainer. The copy clarifies that profiles control how the agent works — identity, memory, skills, model/provider config, and tools — while workspaces control what project/files a session operates on, making the OpenClaw-style role/profile mental model easier to map onto Hermes WebUI.
|
||||
|
||||
- **PR #2332** by @Michaelyklam (refs #2290) — Cron run history/output cards now surface token/cost metadata when the underlying cron output markdown includes it. The backend parses optional model/token/cost/duration frontmatter from cron output files and returns it from `/api/crons/history` and `/api/crons/run`; the Tasks panel renders a compact usage strip beside run rows and below expanded output without affecting older outputs that lack usage metadata.
|
||||
|
||||
### Fixed
|
||||
|
||||
+37
-1
@@ -4465,8 +4465,22 @@ async function loadProfilesPanel() {
|
||||
const data = await api('/api/profiles');
|
||||
_profilesCache = data;
|
||||
panel.innerHTML = '';
|
||||
const explainer = document.createElement('div');
|
||||
explainer.className = 'profile-card profile-help-card';
|
||||
explainer.innerHTML = `
|
||||
<div class="profile-card-header">
|
||||
<div style="min-width:0;flex:1">
|
||||
<div class="profile-card-name">Profiles vs workspaces</div>
|
||||
<div class="profile-card-meta">Use profiles for how the agent works; use workspaces for what files it works on.</div>
|
||||
</div>
|
||||
</div>`;
|
||||
explainer.onclick = () => _renderProfileConceptHelp(data.active || 'default');
|
||||
panel.appendChild(explainer);
|
||||
if (!data.profiles || !data.profiles.length) {
|
||||
panel.innerHTML = `<div style="padding:16px;color:var(--muted);font-size:12px">${esc(t('profiles_no_profiles'))}</div>`;
|
||||
const emptyMsg = document.createElement('div');
|
||||
emptyMsg.style.cssText = 'padding:16px;color:var(--muted);font-size:12px';
|
||||
emptyMsg.textContent = t('profiles_no_profiles');
|
||||
panel.appendChild(emptyMsg);
|
||||
if (_profileMode !== 'create') _clearProfileDetail();
|
||||
return;
|
||||
}
|
||||
@@ -4509,6 +4523,28 @@ async function loadProfilesPanel() {
|
||||
}
|
||||
}
|
||||
|
||||
function _renderProfileConceptHelp(activeName){
|
||||
const title = $('profileDetailTitle');
|
||||
const body = $('profileDetailBody');
|
||||
const empty = $('profileDetailEmpty');
|
||||
if (!title || !body) return;
|
||||
title.textContent = 'Profiles vs workspaces';
|
||||
body.innerHTML = `
|
||||
<div class="main-view-content">
|
||||
<div class="detail-card">
|
||||
<div class="detail-card-title">Use profiles for how; workspaces for what</div>
|
||||
<div class="detail-row"><div class="detail-row-label">Profiles</div><div class="detail-row-value">Agent identity, memory, skills, model/provider config, and connected tools. Create profiles for roles like researcher, writer, marketer, or developer when those roles should carry different context or capabilities.</div></div>
|
||||
<div class="detail-row"><div class="detail-row-label">Workspaces</div><div class="detail-row-value">Project or product folders on disk. Use one workspace per repo/product so chat, terminal, and file browsing point at the right files.</div></div>
|
||||
<div class="detail-row"><div class="detail-row-label">Together</div><div class="detail-row-value">A profile can have a default workspace, but you can still switch workspaces for a session. Profiles answer “who is working?”; workspaces answer “where are they working?”</div></div>
|
||||
</div>
|
||||
</div>`;
|
||||
body.style.display = '';
|
||||
if (empty) empty.style.display = 'none';
|
||||
_profileMode = 'read';
|
||||
_currentProfileDetail = null;
|
||||
_setProfileHeaderButtons('empty');
|
||||
}
|
||||
|
||||
function _renderProfileDetail(p, activeName){
|
||||
_currentProfileDetail = p;
|
||||
const title = $('profileDetailTitle');
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
"""Regression tests for issue #2147 profile/workspace mental-model copy."""
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
def read(rel: str) -> str:
|
||||
return (REPO / rel).read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def test_profiles_panel_surfaces_profiles_vs_workspaces_help_card():
|
||||
src = read("static/panels.js")
|
||||
assert "Profiles vs workspaces" in src
|
||||
assert "Use profiles for how the agent works; use workspaces for what files it works on." in src
|
||||
assert "_renderProfileConceptHelp" in src
|
||||
assert "explainer.onclick = () => _renderProfileConceptHelp" in src
|
||||
|
||||
|
||||
def test_profile_concept_help_distinguishes_how_from_where():
|
||||
src = read("static/panels.js")
|
||||
assert "Agent identity, memory, skills, model/provider config, and connected tools" in src
|
||||
assert "Create profiles for roles like researcher, writer, marketer, or developer" in src
|
||||
assert "Project or product folders on disk" in src
|
||||
assert "Profiles answer “who is working?”; workspaces answer “where are they working?”" in src
|
||||
|
||||
|
||||
def test_empty_profiles_state_keeps_help_card_visible():
|
||||
src = read("static/panels.js")
|
||||
assert "panel.innerHTML = ''" in src
|
||||
assert "panel.appendChild(explainer)" in src
|
||||
assert "emptyMsg.textContent = t('profiles_no_profiles')" in src
|
||||
assert "panel.appendChild(emptyMsg)" in src
|
||||
Reference in New Issue
Block a user