Skip to content

feat(tui): enriched switchable agent list — status/elapsed/tokens + arrow-open (agent switcher PR1)#392

Merged
andrei-hasna merged 1 commit into
mainfrom
feat/tui-agent-switcher-list
Jul 23, 2026
Merged

feat(tui): enriched switchable agent list — status/elapsed/tokens + arrow-open (agent switcher PR1)#392
andrei-hasna merged 1 commit into
mainfrom
feat/tui-agent-switcher-list

Conversation

@andrei-hasna

Copy link
Copy Markdown
Contributor

Agent switcher PR1 — enriched, switchable live agent list (in-thread "FleetView")

Codewith already has the agent switching engine (the /agent · /session picker in
multi_agents.rs / agent_navigation.rs, session_lifecycle::select_agent_thread,
AppEvent::SelectAgentThread, and the app-server attach/detach). This PR only enriches the
picker rows, adds arrow-open at an empty composer, and introduces a live per-thread metrics
cache.
No new protocol / RPC.

Target UX (in-thread FleetView)

Picker rows now render:

<status-dot> <agent-name>  <task>   <elapsed> · ↓ <tokens> tokens
  • filled for the selected/active thread, hollow otherwise; terminal states use a distinct
    glyph, all colored from the existing CollabAgentStatus vocabulary:
    running ●/○ green · completed green · interrupted ("wrapped") yellow ·
    stopped dim · errored red.
  • First row is always main; remaining rows keep earliest-launched-first spawn order.
  • At an empty composer (no overlay/modal), a bare or opens the picker. / select
    (existing ListSelectionView), Enter switches into that agent's live window (existing
    select_agent_thread), Esc returns to the prompt, and main + Enter goes back to main.
  • Alt+Left / Alt+Right quick-cycle is unchanged.
  • Footer hint copy updated (Enter now "to switch"; subtitle mentions switching into the live window).
  • ↓ tokens is the thread's cumulative total_tokens.

Why the metrics cache lives in AgentNavigationState (not ChatWidget)

Switching into an agent rebuilds the ChatWidget from scratch (select_agent_thread
replace_chat_widget). If elapsed/tokens/status lived on the widget they would reset every time
you glanced at a different agent. So a new AgentLiveMetrics { status, started_at, last_task_message, token_total } is kept per ThreadId in AgentNavigationState, in a separate map from the
picker-entry metadata so it survives upsert liveness refreshes and thread switches (only clear
and remove drop it). Mutators: note_turn_started / note_turn_completed / note_error /
note_status / note_token_usage, plus a metrics() getter.

One shared feed for main + every sub-agent

app_server_events::handle_server_notification_event folds each per-thread notification into the
cache before it forks to primary-vs-non-primary enqueue, so main and every sub-agent update
uniformly. The mapping mirrors core/src/agent/status.rs::agent_status_from_event, adapted to the
v2 app-server stream: TurnStarted → Running (+ started_at, + task from the turn's first user
input) · TurnCompleted → {Completed|Interrupted|Errored} (by turn status) · non-retrying
Error → Errored · ThreadClosed → Shutdown · ThreadStatusChanged → status ·
ThreadTokenUsageUpdated → token_total.

Rendering

  • multi_agents::agent_picker_status_dot_spans(status, is_active) — status-driven ●/○/✓/✗/■ + color.
  • multi_agents::agent_picker_row_status(is_closed, live_status) — resolves the row's status (live
    telemetry authoritative; a closed entry with a non-terminal live status downgrades to Shutdown).
  • multi_agents::format_agent_picker_metrics(task, elapsed_secs, token_total) — builds the
    description column, reusing status_indicator_widget::fmt_elapsed_compact and
    status::format_tokens_compact.
  • session_lifecycle::agent_picker_selection_items — Main hoisted first, status-driven dot,
    metrics description replacing the raw-UUID description (the UUID stays in search_value).

Files changed

  • codex-rs/tui/src/app/agent_navigation.rsAgentLiveMetrics + cache + mutators/getter + tests.
  • codex-rs/tui/src/app/app_server_events.rs — shared metrics-feed hook + status mappers.
  • codex-rs/tui/src/multi_agents.rs — status-dot / row-status / metrics formatters + tests + snapshot.
  • codex-rs/tui/src/app/session_lifecycle.rs — enriched picker rows, Main-first, footer copy.
  • codex-rs/tui/src/app/input.rs — empty-composer Down/LeftOpenAgentPicker (gated).
  • codex-rs/tui/src/app.rs — new multi_agents fn imports.
  • snapshots: new agent_picker_rows; updated agent_picker_selection_items_with_new_agent.

Verification (Blacksmith, warm box)

  • cargo nextest run -p codex-tui: 3485 passed, 4 skipped, 0 failed (exit 0) — includes the
    new reducer / formatter / row-status tests and both picker snapshots.
  • cargo clippy -p codex-tui --tests --all-targets -- -D warnings: clean (exit 0). The only
    note is a pre-existing transitive proc-macro-error2 future-incompat warning, unrelated.
  • cargo fmt --all -- --config imports_granularity=Item: clean.
  • No *.md changed, so prettier is a no-op here.

Out of scope — follow-up increments (PR2–6)

  1. PR2 — persistent agent rail (always-visible side list, not just the modal picker).
  2. PR3 — live-transcript fidelity when watching a sub-agent (streaming deltas parity).
  3. PR4 — richer per-agent widget (model/effort, progress, last message preview).
  4. PR5 — inline steering / stop from the list (send input, interrupt an agent).
  5. PR6 — snapshot RPC (authoritative server-side agent fleet snapshot instead of client-folded
    metrics; also freezes elapsed at completion and sources the task more precisely).

Do not merge/tag — handing back for review.

@andrei-hasna
andrei-hasna merged commit 7273c31 into main Jul 23, 2026
27 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 23, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant