diff --git a/CHANGELOG.md b/CHANGELOG.md
index c86c32d2..f3e2e67e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
diff --git a/static/panels.js b/static/panels.js
index 0f9ccedb..47d38561 100644
--- a/static/panels.js
+++ b/static/panels.js
@@ -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 = `
+
`;
+ explainer.onclick = () => _renderProfileConceptHelp(data.active || 'default');
+ panel.appendChild(explainer);
if (!data.profiles || !data.profiles.length) {
- panel.innerHTML = `
+
+
Use profiles for how; workspaces for what
+
Profiles
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.
+
Workspaces
Project or product folders on disk. Use one workspace per repo/product so chat, terminal, and file browsing point at the right files.
+
Together
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?”
+
+
`;
+ body.style.display = '';
+ if (empty) empty.style.display = 'none';
+ _profileMode = 'read';
+ _currentProfileDetail = null;
+ _setProfileHeaderButtons('empty');
+}
+
function _renderProfileDetail(p, activeName){
_currentProfileDetail = p;
const title = $('profileDetailTitle');
diff --git a/tests/test_issue2147_profile_workspace_copy.py b/tests/test_issue2147_profile_workspace_copy.py
new file mode 100644
index 00000000..55203c15
--- /dev/null
+++ b/tests/test_issue2147_profile_workspace_copy.py
@@ -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