feat(tui): enriched switchable agent list — status/elapsed/tokens + arrow-open (agent switcher PR1)#392
Merged
Merged
Conversation
…rrow-open (agent switcher PR1)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Agent switcher PR1 — enriched, switchable live agent list (in-thread "FleetView")
Codewith already has the agent switching engine (the
/agent·/sessionpicker inmulti_agents.rs/agent_navigation.rs,session_lifecycle::select_agent_thread,AppEvent::SelectAgentThread, and the app-server attach/detach). This PR only enriches thepicker 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:
●filled for the selected/active thread,○hollow otherwise; terminal states use a distinctglyph, all colored from the existing
CollabAgentStatusvocabulary:running
●/○green · completed✓green · interrupted ("wrapped")✓yellow ·stopped
■dim · errored✗red.main; remaining rows keep earliest-launched-first spawn order.↓or←opens the picker.↑/↓select(existing
ListSelectionView),Enterswitches into that agent's live window (existingselect_agent_thread),Escreturns to the prompt, andmain+Entergoes back to main.↓ tokensis the thread's cumulativetotal_tokens.Why the metrics cache lives in
AgentNavigationState(notChatWidget)Switching into an agent rebuilds the
ChatWidgetfrom scratch (select_agent_thread→replace_chat_widget). If elapsed/tokens/status lived on the widget they would reset every timeyou glanced at a different agent. So a new
AgentLiveMetrics { status, started_at, last_task_message, token_total }is kept perThreadIdinAgentNavigationState, in a separate map from thepicker-entry metadata so it survives
upsertliveness refreshes and thread switches (onlyclearand
removedrop it). Mutators:note_turn_started/note_turn_completed/note_error/note_status/note_token_usage, plus ametrics()getter.One shared feed for main + every sub-agent
app_server_events::handle_server_notification_eventfolds each per-thread notification into thecache 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 thev2 app-server stream:
TurnStarted → Running(+started_at, + task from the turn's first userinput) ·
TurnCompleted → {Completed|Interrupted|Errored}(by turn status) · non-retryingError → 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 (livetelemetry 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 thedescription column, reusing
status_indicator_widget::fmt_elapsed_compactandstatus::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.rs—AgentLiveMetrics+ 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-composerDown/Left→OpenAgentPicker(gated).codex-rs/tui/src/app.rs— newmulti_agentsfn imports.agent_picker_rows; updatedagent_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 thenew reducer / formatter / row-status tests and both picker snapshots.
cargo clippy -p codex-tui --tests --all-targets -- -D warnings: clean (exit 0). The onlynote is a pre-existing transitive
proc-macro-error2future-incompat warning, unrelated.cargo fmt --all -- --config imports_granularity=Item: clean.*.mdchanged, so prettier is a no-op here.Out of scope — follow-up increments (PR2–6)
metrics; also freezes elapsed at completion and sources the task more precisely).
Do not merge/tag — handing back for review.