End a subagent on its own end_turn, not the parent's tool_result, and give the expander a touch-sized row

The Agent tool runs subagents in the background, so the parent's result
arrives at launch while the subagent works on for minutes; finishing on it
read a running agent as finished with a transcript cut off at launch. A
subagent now ends on its own message_delta end_turn, and a later line for a
finished one reopens it, since a background agent can be messaged again.

The card's expander row was only the chevron's height, so a tap for it
landed on the first subcard; it is the platform's 48dp minimum now.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
irisandClaude Fable 5.1 committed 2026-09-05 14:44:00 -04:00
1 parent 9fa09b0af1
commit cf10b17c5b
4 files changed
+227 -26

No files matched your search

+24 -7
View File
@@ -89,9 +89,9 @@ impl Subagent {
}
/// Whether this subagent's last recorded status is not `Exited` --
/// what decides whether a further child line still belongs in its
/// transcript. See `SUBAGENTS.md`'s lifecycle: "a child line whose
/// subagent finished already... is ignored".
/// what decides whether a further child line reopens it (see
/// `Subagents::reopen`) rather than continuing straight through. See
/// `SUBAGENTS.md`'s lifecycle.
pub fn is_open(&self) -> bool {
*self.status.lock().unwrap() != SessionStatus::Exited
}
@@ -269,10 +269,12 @@ impl Subagents {
}
}
/// The parent's `tool_result` for this Task id arrived: the subagent's
/// own `Status::Exited`. A no-op for an id that is not a subagent's, so
/// callers can call this for every `tool_result` without first checking
/// whether it belongs to one.
/// The subagent's own turn ended: its `Status::Exited`. Called from
/// `translate_child` on the subagent's own `end_turn`, never on the
/// parent's `tool_result` -- a background Task's `tool_result` arrives
/// at launch, not at completion, so it says nothing about whether this
/// is over. A no-op for an id that is not a subagent's or is already
/// closed.
pub fn finish(&self, id: &str) {
if let Some(subagent) = self.live.lock().unwrap().get(id).cloned()
&& subagent.is_open()
@@ -283,6 +285,21 @@ impl Subagents {
}
}
/// A line arrived for a subagent that had already finished: it is
/// working again, not stale -- a background Task can be sent another
/// message long after its first turn ended. Appends `Status::Running`
/// so the list stops reporting it as finished; a no-op if it was not
/// actually closed, so a caller need not check first.
pub fn reopen(&self, id: &str) {
if let Some(subagent) = self.live.lock().unwrap().get(id).cloned()
&& !subagent.is_open()
{
subagent.append(Event::Status {
state: SessionStatus::Running,
});
}
}
/// The parent session's process is gone, so nothing still open here has
/// a process behind it either -- see `SUBAGENTS.md`'s lifecycle #4.
pub fn finish_all(&self) {