Refresh the subagent picker in the background (#35693)

## Why

Opening the subagent picker could wait on thread metadata and live event-store locks, delaying terminal input. Its cached entries could also omit descendants that were not observed in the current TUI session.

## What changed

- Render the picker immediately from cached navigation state and refresh root descendants asynchronously with `thread/list`.
- Merge discovered descendants into an open picker while preserving its selection and live status, and coalesce concurrent refresh requests.
- Ignore refresh responses from a previous session and avoid blocking on busy event stores.

## Testing

- Extend session lifecycle coverage for nonblocking picker opens, refresh coalescing, descendant discovery, selection preservation, and status updates.
- Add coverage for rejecting a refresh response after navigation state is cleared.

GitOrigin-RevId: 7eae7f696f8d2d323a47b3fd2800ea1c1cdbf2a0
This commit is contained in:
Eric Traut authored and copyberry committed 2026-07-28 00:38:45 +00:00
1 parent 63682c4e1a
commit f029bb795c
10 files changed
+415 -37

No files matched your search

@@ -486,6 +486,11 @@ impl ListSelectionView {
let previously_selected = self
.selected_actual_idx()
.filter(|actual_idx| self.enabled_actual_idx(*actual_idx).is_some())
.or_else(|| {
self.initial_selected_idx
.take()
.filter(|actual_idx| self.enabled_actual_idx(*actual_idx).is_some())
})
.or_else(|| {
(!self.is_searchable)
.then(|| {
@@ -494,11 +499,6 @@ impl ListSelectionView {
.position(|item| item.is_current && Self::item_is_enabled(item))
})
.flatten()
})
.or_else(|| {
self.initial_selected_idx
.take()
.filter(|actual_idx| self.enabled_actual_idx(*actual_idx).is_some())
});
if self.is_searchable && !self.search_query.is_empty() {
+7
View File
@@ -1236,6 +1236,13 @@ impl BottomPane {
.and_then(|view| view.selected_index())
}
pub(crate) fn selected_index_for_present_view(&self, view_id: &'static str) -> Option<usize> {
self.view_stack
.iter()
.rfind(|view| view.view_id() == Some(view_id))
.and_then(|view| view.selected_index())
}
pub(crate) fn active_tab_id_for_active_view(&self, view_id: &'static str) -> Option<&str> {
self.view_stack
.last()