feat: clarify profiles and workspaces

This commit is contained in:
Michael Lam
2026-05-15 19:41:02 -07:00
committed by Hermes Agent
parent dbd2c79891
commit b373f090bd
3 changed files with 71 additions and 1 deletions
+2
View File
@@ -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
View File
@@ -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