[PR #12732] feat: add team TUI integration — sidebar, header, sync, and team dialog #14344

Open
opened 2026-02-16 18:19:08 -05:00 by yindo · 0 comments
Owner

Original Pull Request: https://github.com/anomalyco/opencode/pull/12732

State: open
Merged: No


Summary

Adds TUI integration for agent teams (Fixes #12711). Depends on #12730 and #12731 — merge those first.

This is PR 3/3. It surfaces the team system in the terminal UI — sidebar panels, header badges, status bars, keyboard navigation between teammates, a team dialog, and real-time event synchronization. The LLM creates and manages teams via tools (PR 2); this PR gives the user visibility and navigation.

What's included

Team dialog (component/dialog-team.tsx — 190 lines)

  • Full-screen dialog showing team members and shared tasks
  • Members listed with status icons (* active, o idle, ! interrupted, x shutdown) and agent type
  • Tasks listed with status, assignee, priority, and dependency info
  • Click a teammate → navigates to their session
  • Keybinds: m to message (shows toast hint), l to jump to lead session
  • Auto-refreshes team data from /team/by-session/:sessionID on open

Sidebar (routes/session/sidebar.tsx — +387 lines)

Two new sidebar components that appear when a team is active:

TeamLeadSidebar (for the lead session):

  • Lists all teammates with colored status indicators and model info
  • Shows plan approval state ([awaiting approval], [approved])
  • Displays current task for each active member
  • Message count per teammate
  • Click a teammate → navigate to their session

TeamMemberSidebar (for teammate sessions):

  • Shows the teammate's own status, assigned task, and team name
  • Links back to lead session
  • Shows recent activity from tools (formatted with formatToolActivity())

Helper functions:

  • memberColor() — consistent color assignment per teammate
  • teamTaskIcon() / teamTaskColor() — status-appropriate icons and colors for tasks
  • truncate() — safe string truncation for sidebar display
  • formatToolActivity() — extracts last tool call from message parts for activity display

Header (routes/session/header.tsx — +159 lines)

  • TeamBadge: Shows team name with active/idle/total member counts (lead view) or memberName @teamName (teammate view)
  • TeamStatusBar: Persistent bar below header showing all teammates with status icon, model, plan approval state, and current task. Clickable → navigate to teammate session. Shows task progress (completed/total) and delegate mode indicator
  • memberStatusIcon() — maps status to ASCII icons
  • Badge appears in both lead and subagent header layouts
  • Status bar only shown when sidebar is not visible (avoids duplication)
  • Header accepts sidebarVisible prop to coordinate display

Sync store (context/sync.tsx — +160 lines)

  • New team store field: { [sessionID: string]: TeamSyncEntry } with team name, role, members, tasks, delegate state
  • 6 team event handlers in the SSE event loop:
    • team.created — adds lead entry to store
    • team.member.spawned — adds member entry + updates lead's member list
    • team.member.status — updates member status across all sessions in the team
    • team.task.updated — reconciles task list for all sessions in the team
    • team.task.claimed — updates task assignee and status
    • team.cleaned — removes all team entries from store
  • Team context fetch on session sync (non-blocking sdk.fetch to /team/by-session/:sessionID)
  • Session deletion cleanup includes team store entries
  • Uses reconcile() from SolidJS for efficient task list updates

Session layout (routes/session/index.tsx — +181 lines)

  • selectedTeammate signal for sidebar teammate selection
  • teamInfo / teamMembers memos derived from sync store
  • Sidebar visibility logic — shows when teammates exist or a teammate is selected
  • Keyboard navigation: Shift+Up / Shift+Down to cycle through teammates + lead
  • Command palette entries: next teammate, previous teammate, go to lead, delegate to team commands
  • Teammate messaging UI (passed to Prompt component)

Prompt integration (component/prompt/index.tsx — +54 lines)

  • New props: selectedTeammate, onTeammateMessageSent
  • teamBusy memo — shows indicator when a team message is being sent
  • Team message interception in submit handler — when a teammate is selected, messages route through team_message instead of normal prompt submission
  • Team busy indicator in the prompt UI

SDK context (context/sdk.tsx)

  • Added fetch passthrough prop on SDK context — needed for team API calls in direct-RPC mode where bare fetch() can't reach the internal server

User experience

As the lead:

  1. LLM creates a team via team_create → header shows team badge, status bar appears
  2. LLM spawns teammates → sidebar populates with member list, status bar shows each member
  3. Members work in parallel → status updates in real-time (active → idle), task progress ticks up
  4. Shift+Down/Up to cycle between teammate sessions and review their work
  5. Team dialog (Ctrl+T or command palette) shows full overview with clickable navigation
  6. LLM cleans up → team UI disappears

As a teammate:

  1. Session header shows "Teammate session" with memberName @teamName badge
  2. Sidebar shows own status, assigned task, link back to lead
  3. Works normally — all team communication happens through tools

Files changed

File Change Lines
src/cli/cmd/tui/component/dialog-team.tsx New +189
src/cli/cmd/tui/routes/session/sidebar.tsx Modified +387
src/cli/cmd/tui/routes/session/index.tsx Modified +181
src/cli/cmd/tui/context/sync.tsx Modified +160
src/cli/cmd/tui/routes/session/header.tsx Modified +159
src/cli/cmd/tui/component/prompt/index.tsx Modified +54
src/cli/cmd/tui/context/sdk.tsx Modified +1
Total +1,111 (+1,111 / −21)

Stack

PR 3/3 — merge in order:

  1. #12730 — Core team primitives
  2. #12731 — Tools + routes
  3. This PR — TUI integration
**Original Pull Request:** https://github.com/anomalyco/opencode/pull/12732 **State:** open **Merged:** No --- ## Summary Adds **TUI integration** for agent teams (Fixes #12711). Depends on #12730 and #12731 — merge those first. This is PR 3/3. It surfaces the team system in the terminal UI — sidebar panels, header badges, status bars, keyboard navigation between teammates, a team dialog, and real-time event synchronization. The LLM creates and manages teams via tools (PR 2); this PR gives the *user* visibility and navigation. ## What's included ### Team dialog (`component/dialog-team.tsx` — 190 lines) - Full-screen dialog showing team members and shared tasks - Members listed with status icons (`*` active, `o` idle, `!` interrupted, `x` shutdown) and agent type - Tasks listed with status, assignee, priority, and dependency info - Click a teammate → navigates to their session - Keybinds: `m` to message (shows toast hint), `l` to jump to lead session - Auto-refreshes team data from `/team/by-session/:sessionID` on open ### Sidebar (`routes/session/sidebar.tsx` — +387 lines) Two new sidebar components that appear when a team is active: **`TeamLeadSidebar`** (for the lead session): - Lists all teammates with colored status indicators and model info - Shows plan approval state (`[awaiting approval]`, `[approved]`) - Displays current task for each active member - Message count per teammate - Click a teammate → navigate to their session **`TeamMemberSidebar`** (for teammate sessions): - Shows the teammate's own status, assigned task, and team name - Links back to lead session - Shows recent activity from tools (formatted with `formatToolActivity()`) **Helper functions:** - `memberColor()` — consistent color assignment per teammate - `teamTaskIcon()` / `teamTaskColor()` — status-appropriate icons and colors for tasks - `truncate()` — safe string truncation for sidebar display - `formatToolActivity()` — extracts last tool call from message parts for activity display ### Header (`routes/session/header.tsx` — +159 lines) - **`TeamBadge`**: Shows team name with active/idle/total member counts (lead view) or `memberName @teamName` (teammate view) - **`TeamStatusBar`**: Persistent bar below header showing all teammates with status icon, model, plan approval state, and current task. Clickable → navigate to teammate session. Shows task progress (`completed/total`) and delegate mode indicator - `memberStatusIcon()` — maps status to ASCII icons - Badge appears in both lead and subagent header layouts - Status bar only shown when sidebar is not visible (avoids duplication) - Header accepts `sidebarVisible` prop to coordinate display ### Sync store (`context/sync.tsx` — +160 lines) - New `team` store field: `{ [sessionID: string]: TeamSyncEntry }` with team name, role, members, tasks, delegate state - **6 team event handlers** in the SSE event loop: - `team.created` — adds lead entry to store - `team.member.spawned` — adds member entry + updates lead's member list - `team.member.status` — updates member status across all sessions in the team - `team.task.updated` — reconciles task list for all sessions in the team - `team.task.claimed` — updates task assignee and status - `team.cleaned` — removes all team entries from store - Team context fetch on session sync (non-blocking `sdk.fetch` to `/team/by-session/:sessionID`) - Session deletion cleanup includes team store entries - Uses `reconcile()` from SolidJS for efficient task list updates ### Session layout (`routes/session/index.tsx` — +181 lines) - `selectedTeammate` signal for sidebar teammate selection - `teamInfo` / `teamMembers` memos derived from sync store - Sidebar visibility logic — shows when teammates exist or a teammate is selected - **Keyboard navigation**: `Shift+Up` / `Shift+Down` to cycle through teammates + lead - **Command palette entries**: `next teammate`, `previous teammate`, `go to lead`, `delegate to team` commands - Teammate messaging UI (passed to Prompt component) ### Prompt integration (`component/prompt/index.tsx` — +54 lines) - New props: `selectedTeammate`, `onTeammateMessageSent` - `teamBusy` memo — shows indicator when a team message is being sent - Team message interception in submit handler — when a teammate is selected, messages route through `team_message` instead of normal prompt submission - Team busy indicator in the prompt UI ### SDK context (`context/sdk.tsx`) - Added `fetch` passthrough prop on SDK context — needed for team API calls in direct-RPC mode where bare `fetch()` can't reach the internal server ## User experience **As the lead:** 1. LLM creates a team via `team_create` → header shows team badge, status bar appears 2. LLM spawns teammates → sidebar populates with member list, status bar shows each member 3. Members work in parallel → status updates in real-time (active → idle), task progress ticks up 4. `Shift+Down/Up` to cycle between teammate sessions and review their work 5. Team dialog (`Ctrl+T` or command palette) shows full overview with clickable navigation 6. LLM cleans up → team UI disappears **As a teammate:** 1. Session header shows "Teammate session" with `memberName @teamName` badge 2. Sidebar shows own status, assigned task, link back to lead 3. Works normally — all team communication happens through tools ## Files changed | File | Change | Lines | |------|--------|-------| | `src/cli/cmd/tui/component/dialog-team.tsx` | New | +189 | | `src/cli/cmd/tui/routes/session/sidebar.tsx` | Modified | +387 | | `src/cli/cmd/tui/routes/session/index.tsx` | Modified | +181 | | `src/cli/cmd/tui/context/sync.tsx` | Modified | +160 | | `src/cli/cmd/tui/routes/session/header.tsx` | Modified | +159 | | `src/cli/cmd/tui/component/prompt/index.tsx` | Modified | +54 | | `src/cli/cmd/tui/context/sdk.tsx` | Modified | +1 | | **Total** | | **+1,111 (+1,111 / −21)** | ## Stack PR 3/3 — merge in order: 1. #12730 — Core team primitives 2. #12731 — Tools + routes 3. **This PR** — TUI integration
yindo added the pull-request label 2026-02-16 18:19:08 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#14344