feat: unify local + external agents in Running agents sidebar tree - #283
feat: unify local + external agents in Running agents sidebar tree#283NovakPAai wants to merge 3 commits into
Conversation
The tree only ever showed agents in external native terminals — an agent running inside codbash's own Workspace pane silently disappeared from it, so a project with both an in-app Claude session and an iTerm one only showed half the picture. That asymmetry (plus dimmed "idle" rows reading as ambiguous "maybe gone") is what read as messy/hard-to-trust grouping. - _wsRunningGroups(mode) replaces _wsRunningByProject(): still built from activeSessions (a live ps-scan re-polled every 5s, so an exited process is simply absent next tick — no separate "ghost" filtering needed), but now includes local (in-codbash) agents alongside external ones instead of dropping them. - Grouping is now a per-browser toggle (project → agent, or agent → project), persisted to localStorage['codedash-running-group'], exposed as a compact 2-button segmented control in the tree header. - Rows are color-coded by where they run: blue dot = inside codbash, orange dot = external native terminal (iTerm/Terminal.app/Warp/cmux). Idle still dims to muted gray, on top of either color. - jumpToRunningAgent now dispatches on the `local` tag: local agents jump straight to their Workspace tab/pane (found by matching cwd against the live pane list); external agents keep the existing /api/focus path. Updates the design doc and CLAUDE.md (which documented "external only" as intentional) and the existing test suite for the new function names/shape.
The first pass grouped project/agent as a flat 2-level list — one header,
then every session underneath as a same-labeled row ("Claude", "Claude",
…). That read as alternating noise (session, agent, session, agent) rather
than a real hierarchy, especially once a project had 2+ live sessions of
the same agent.
_wsRunningTree(mode) replaces _wsRunningGroups(mode): a real 3-level tree
— outer group -> inner group -> individual sessions — built via a small
generic _wsGroupBy() applied twice. mode:'project' nests project -> agent
-> sessions; mode:'agent' nests the mirror, agent -> project -> sessions
(re-parenting the whole tree, not just relabeling a flat list).
A project+agent pair with exactly one live session collapses its leaf row
into the subgroup row itself (.ws-run-leaf) — no redundant single-child
row. Once it holds 2+ sessions, the subgroup renders as a real subheader
and each session gets its own leaf row, labeled via _wsSessionLeafLabel
(a live pane's name if local and matched, else the session-id prefix —
same convention as the session cards' "Resume last session (id12345)" —
else a bare pid) so same-project/same-agent sessions read as genuinely
distinct instead of the same label repeated.
|
Follow-up commit: turned this into an actual 3-level tree instead of a flat project→(agent rows) / agent→(project rows) list. The flat version still read as "session, agent, session, agent" once a project had 2+ live sessions of the same tool — every session showed up as a same-labeled row directly under the project header, no real hierarchy. Now:
Verified locally with a real 2-session case ( |
Top-level (L1) groups — project in project-mode, agent kind in agent-mode — now start collapsed. Clicking a header expands it in place to reveal its agent/project subgroups and individual sessions; the same accordion logic applies to both grouping directions. - _wsRunExpanded (in-memory, keyed `mode|groupKey`) tracks open/closed per group so switching the Project/Agent toggle doesn't share expand state across the two different hierarchies. - _wsToggleRunGroup flips a `collapsed` class directly on the group's wrapper DOM node instead of forcing a full tree rebuild — cheap, and a later rebuild (triggered by a real activeSessions change) re-reads _wsRunExpanded so the user's open/closed choices survive it. - The L1 header's click now means "toggle" rather than "jump to the first session" — with a header disclosing multiple children there's no single unambiguous default target, so the jump action lives entirely on leaf rows (.ws-run-l2.ws-run-leaf / .ws-run-l3), unchanged from before. - Basic keyboard support: role="button", aria-expanded, Enter/Space to toggle.
|
Added the accordion: top-level groups (project or agent, depending on the toggle) now render collapsed by default. Clicking a header expands it in place to reveal its agent/project subgroups and sessions — same logic in both directions.
Verified locally: collapsed by default in both Project and Agent mode; expanding |
Summary
The "Running agents" sidebar tree only ever listed agents running in external native terminals (a deliberate 7.15.0/#268 change) — an agent running inside codbash's own in-app Workspace pane silently disappeared from it. A project with both an in-app Claude session and an iTerm one only showed half the picture, which read as confusing/inconsistent grouping (see conversation screenshot:
market-analytics/~/codbashrows with some dimmed "idle" dots that read as ambiguous "maybe gone")._wsRunningGroups(mode)replaces_wsRunningByProject(): still built fromactiveSessions(a liveps-scan re-polled every 5s — an exited process is simply absent on the next tick, so there's no separate "ghost" filtering to get right), but now includes local (in-codbash) agents alongside external ones instead of dropping them.localStorage['codedash-running-group'](per-browser display preference, not a synced setting).jumpToRunningAgentnow dispatches on thelocaltag: local agents jump straight to their Workspace tab/pane (found by matchingcwdagainst the live pane list, so a tab opened after the tree last rendered still resolves); external agents keep the existing/api/focus-by-PID path (never spawns a blank terminal, per the existing design doc's rule).Updates
docs/design/running-agents-external.md(amended, not rewritten — the external-focus mechanics section is unchanged) andCLAUDE.md, both of which documented "external only" as intentional. Updatestest/running-agents-external.test.jsfor the new function name/shape.Test plan
node --test "test/**/*.test.js"— 260 passed, 1 skipped (win32-only)🤖 Generated with Claude Code