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

@@ -233,6 +233,24 @@ impl ChatWidget {
self.request_redraw();
}
pub(crate) fn selected_index_for_present_view(&self, view_id: &'static str) -> Option<usize> {
self.bottom_pane.selected_index_for_present_view(view_id)
}
pub(crate) fn replace_selection_view_if_present(
&mut self,
view_id: &'static str,
params: SelectionViewParams,
) -> bool {
let replaced = self
.bottom_pane
.replace_selection_view_if_present(view_id, params);
if replaced {
self.refresh_plan_mode_nudge();
}
replaced
}
pub(crate) fn no_modal_or_popup_active(&self) -> bool {
self.bottom_pane.no_modal_or_popup_active()
}