Skip to content

feat(desktop): follow the focused bot in the profile-grouped sidebar - #91052

Open
kennysamuerto wants to merge 2 commits into
NousResearch:mainfrom
kennysamuerto:feat/sidebar-follow-active-bot
Open

feat(desktop): follow the focused bot in the profile-grouped sidebar#91052
kennysamuerto wants to merge 2 commits into
NousResearch:mainfrom
kennysamuerto:feat/sidebar-follow-active-bot

Conversation

@kennysamuerto

Copy link
Copy Markdown

What

With Grouping → Profile in the all-profiles scope, the sidebar now answers "what is THIS bot doing?" passively:

  • Follow the focused bot — the focused chat's profile group floats to the top of the list and is re-opened if it had been collapsed, so switching bots (roster click, tile focus) always reveals that bot's sessions without hunting for its section. default keeps its historical first spot when nothing else is focused; the rest stay alphabetical. A focused profile with no sessions changes nothing.
  • Session count on group headers — each profile group shows its session count next to the label, readable while collapsed (analyst · 3-style, in the quaternary text tone so it stays out of the way of the cost/token totals).

Closes #89347

How

  • $focusedSessionProfile moves from the plugin SDK (src/sdk/index.ts) into the session-states store, so the sidebar and host.state.focusedSessionProfile share one resolver — same focused-row → owner-profile ladder, behavior unchanged for SDK consumers.
  • Profile-group construction is extracted from the index.tsx memo into a pure buildProfileGroups() (sidebar/profile-groups.ts) with colocated unit tests covering ranking (focused > default > alpha), normalization, legacy rows without a profile, and color stamping.
  • A small effect calls setWorkspaceNodeOpen(focusedProfile, true) when focus moves while profile-grouped, reusing the existing collapse-state store.

Testing

  • vitest run --project ui src/app/chat/sidebar src/sdk src/store1285 passed (includes 7 new tests in profile-groups.test.ts; profile-routing.test.ts mock updated for the moved atom).
  • tsc -p . --noEmit clean; eslint clean on all touched files.

Notes

🤖 Generated with Claude Code

@Enough1122

Copy link
Copy Markdown
Contributor

AI code review — automated review for reference; please use your judgment.

Reviewed the diff. Tidy feature: the focused-profile ranking lives in a pure, well-tested helper (seven unit cases covering normalization, ghost focus, default-first preservation, color stamping), the auto-reveal effect correctly no-ops when focus doesn't change (so a manual collapse survives same-profile refocuses), and lifting `$focusedSessionProfile` into `session-states` removes a private duplicate so the SDK host state and the sidebar read one source of truth. Header session-count badge answers the collapsed-glance question nicely.

Nit: two micro-points — (a) confirm `setWorkspaceNodeOpen(profileKey, true)` writes node state namespaced to the profile-grouping mode; if workspace open/closed state is shared across grouping modes, a project node that happens to share an id string would get expanded as a side effect; (b) in buildProfileGroups the `groups.set(key, group)` inside the loop is redundant (the Map already holds the mutated object reference).

No blocking issues found.

@kennysamuerto

Copy link
Copy Markdown
Author

Thanks for the review! On the two nits:

(a) Node-state collision — confirmed not possible: workspace node ids are always path-derived (<repoRoot>::branch::<name> via branchLaneId, <repoRoot>::kanban, or absolute worktree paths — all contain / or ::), while profile keys are bare profile names, which can't take either form. Worth noting the two id families already cohabit in $sidebarWorkspaceNodeOpen before this PR: the profile pane's own useWorkspaceNodeOpen(group.id) in workspace-group.tsx reads the same bare profile key this effect writes, so the effect targets exactly the node the pane owns.

(b) groups.set in the loop — it wasn't redundant on the first insert (the ?? arm built a detached object that still needed to enter the map), but it did re-set existing references on every subsequent row. Restructured to an explicit create-then-set branch in 6570ba7 — clearer and skips the no-op sets. Tests and lint still green.

vandertoorm and others added 2 commits August 27, 2026 19:40
With Grouping → Profile in the all-profiles scope, the sidebar now answers
"what is THIS bot doing?" passively: the focused chat's profile group
floats to the top and is re-opened if it had been collapsed, so switching
bots always reveals that bot's sessions without hunting for its section.
Profile group headers also gain a session count, readable while collapsed.

- $focusedSessionProfile moves from the plugin SDK into the session-states
  store so the sidebar and host.state share one resolver (same
  focused-row → owner-profile ladder, unchanged behavior).
- Group construction extracted to buildProfileGroups() with unit tests:
  focused profile first, then default, then alphabetical — a focused
  profile with no sessions changes nothing.

Closes NousResearch#89347

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review follow-up: the unconditional groups.set() was needed only on first
insert; make that branch explicit so existing references are never re-set.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kennysamuerto
kennysamuerto force-pushed the feat/sidebar-follow-active-bot branch from 6570ba7 to b7f8071 Compare August 27, 2026 17:41
@kennysamuerto

Copy link
Copy Markdown
Author

Rebased onto current main (v2026.8.27). Main had grown its own connection-qualified $focusedSessionOwner + $focusedSessionProfile pair in sdk/index.ts, which superseded the simple profile ladder this PR originally lifted. Resolution keeps this PR's single-source-of-truth shape while adopting the richer resolution: both computeds now live in store/session-states (exported), and the SDK imports them and re-exports the owner type as PluginFocusedSessionOwner — no behavior change to either feature. The sidebar keeps reading $focusedSessionProfile from the store as before.

Typecheck, lint and the desktop suite are green (7,975 passed; the only suite my rebase had broken was profile-routing.test.ts, whose session-states mock now also stubs $focusedSessionOwner).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/profiles Multi-profile isolation, HERMES_HOME scoping comp/desktop Electron desktop app (apps/desktop/*) P3 Low — cosmetic, nice to have type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Desktop (macOS): group sessions by bot in the sidebar — see each bot's active sessions at a glance

4 participants