Feat/desktop goal status bar conversation mode - #48285
Conversation
When the sidebar is in All-profiles mode, each session row now shows a compact, color-coded profile badge (first letter of the profile name) between the status dot and the session title. The badge uses the same deterministic color from profileColor() that the sidebar rail squares already use, so the same profile always reads the same color. Hovering the badge shows a tooltip: Profile: <name>. The default profile produces no badge (consistent with its neutral rail treatment). In single-profile or scoped-to-one-profile mode the badge is completely absent, so existing users see zero visual change. The active-status dot (SidebarRowDot) already covered the 'running', 'needs input', and 'idle' states; no changes to that component. Files changed: - session-row.tsx: ProfileBadge component + showProfileBadge prop - index.tsx: prop wired through SidebarSessionsSectionProps; passed to search-results and pinned sections when showAllProfiles is true - virtual-session-list.tsx: prop threaded to virtual and sortable rows - i18n/en.ts: sidebar.row.profileBadge key Closes #feat/sidebar-profile-badge-active-status
Adds a new subcommand to hermes curator that exposes the telemetry
already collected by tools/skill_usage.py (use_count, view_count,
patch_count, last_activity_at, provenance) in a human-readable table.
Unlike hermes curator status (which shows only curator-managed /
agent-created skills), hermes curator usage surfaces ALL skills on
disk — bundled built-ins and hub-installed included — so users can
answer 'which skills do I actually use?' without reading .usage.json
directly.
Usage:
hermes curator usage [--sort use_count|view_count|activity_count|last_activity_at]
[--provenance agent|bundled|hub]
[--limit N]
Example output:
NAME USE VIEW PATCH ACTIVITY LAST_SEEN PROVENANCE
-----------------------------------------------------------------------
spare-parts-pipeline-v8-17 120 120 123 363 2h ago agent
plan 12 88 0 100 1h ago bundled
shop 0 3 0 3 2d ago hub
Implementation notes:
- _cmd_usage() is a thin wrapper around skill_usage.usage_report();
no new data is collected — this just renders what was already there.
- Reuses _fmt_ts() already in curator.py for human-friendly timestamps.
- Registered as a subcommand of hermes curator (Footprint Ladder rung 1:
extend existing code) rather than a new top-level command.
- 16 tests covering: table structure, all four sort keys, provenance
filtering, limit, empty-set, invalid-sort (exit 2), and cli_main E2E.
…elivery
Bot API 10.1's math renderer silently drops LaTeX commands outside its
supported subset. Two common cases reported:
* \\boxed{E = mc^2} -- box wrapper dropped, inner math disappears
* \\ce{H2O} -- mhchem entirely unsupported, formula disappears
Root cause: _rich_message_payload() passed raw markdown to sendRichMessage
unchanged; Telegram's parser discards unknown commands without error.
Fix: add _normalize_rich_latex() called from _rich_message_payload() that
pre-processes content INSIDE \$\$...\$\$ math blocks only (prose is untouched):
* \\boxed{X} -> X strip the wrapper; inner math renders correctly
* \\ce{X} -> \\text{X} mhchem -> labeled text entity so formula is visible
\\boxed uses a balanced-brace depth scanner (_extract_brace_arg + _strip_boxed)
so \\boxed{\\frac{a}{b}} and deeply nested forms are handled correctly.
\\ce uses a simple [^{}]* pattern (chemistry formulas have no nested braces).
New entries can be added via _CE_LATEX_RE or _strip_boxed as further gaps
in Bot API's LaTeX support are discovered.
Tests: 17 new cases covering fast-path (no \$\$), simple and nested \\boxed,
double-nested \\boxed, \\ce, prose-between-blocks guard, multiple \$\$ blocks
in one message, and _rich_message_payload integration.
Add store/goal.ts: per-session goal atom, parseGoalResponse, refreshSessionGoal, sendGoalCommand. Add store/conversation-mode.ts: guidance/queue mode atom with localStorage persistence. Add composer/goal-status-bar.tsx: compact strip above status stack showing goal state (active/paused/waiting/completed/failed). Add composer/conversation-mode-toggle.tsx: toolbar pill to switch between Guidance and Queue modes. Modify composer/controls.tsx: add sessionId prop, render ConversationModeToggle. Modify composer/index.tsx: wire GoalStatusBar + mode toggle, queue-mode submit branch. Modify use-message-stream.ts: refreshSessionGoal after message.complete; read goal from session.info. Modify i18n/types.ts + en.ts: add goalBar and conversationMode string tables. Tests: 34 unit tests for all store helpers. Closes NousResearch#48236
teknium1
left a comment
There was a problem hiding this comment.
Thanks for the Desktop goal-status concept. Current main supports /goal, but this branch needs contract and scope work before the Desktop feature can be salvaged.
Problems
apps/desktop/src/store/goal.ts:166readstext/response, while current/goal statusreturns{type: "exec", output: ...}attui_gateway/server.py:12129-12130; the bar receives no status.apps/desktop/src/store/goal.ts:186sends/goal end; current main only clears onclear,stop, ordone(tui_gateway/server.py:12149-12158).endinstead reachesGoalManager.set()(tui_gateway/server.py:12160-12162).- The PR also bundles unrelated prompt-builder, Telegram, curator, and sidebar work across five commits.
Suggested changes
- Rebuild the goal state around a typed/versioned backend payload, or consume the current
outputcontract and cover active/paused/cleared states end to end. - Send
/goal clearfor the End action and add a regression test. - Split the unrelated commits, then salvage the focused Desktop change onto current main's TypeScript architecture.
Automated hermes-sweeper review.
| { command: '/goal status', session_id: sid } | ||
| ) | ||
|
|
||
| const raw = result?.text ?? result?.response ?? '' |
There was a problem hiding this comment.
Blocking: current slash.exec routes /goal status to command.dispatch, which returns {type: "exec", output: mgr.status_line()} (tui_gateway/server.py:12129-12130). This discards output, so raw is always empty and the goal bar cannot populate. Consume the actual response contract or add a typed goal-status RPC.
|
|
||
| try { | ||
| await gateway.request('slash.exec', { | ||
| command: `/goal ${subcommand}`, |
There was a problem hiding this comment.
Blocking: /goal end is not a supported clearing command. Current main accepts clear, stop, and done; any other text is treated as a new goal (tui_gateway/server.py:12149-12162). The End button therefore replaces the goal with end. Send /goal clear and cover that behavior.
|
The desktop goal status bar work was unified into #72244 (now merged) with credit. Thanks! |
What does this PR do?
Related Issue
Fixes #
Type of Change
Changes Made
How to Test
Checklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests passDocumentation & Housekeeping
docs/, docstrings) — or N/Acli-config.yaml.exampleif I added/changed config keys — or N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — or N/AFor New Skills
hermes --toolsets skills -q "Use the X skill to do Y"Screenshots / Logs