Never run two turns into one, and say when a session waits on its own work
A turn started by something with no row of its own -- a subagent reporting back, a peer message the CLI only owns up to at the end -- met the previous reply with nothing between it, and the fold grew that reply rather than starting a new one. Two answers were drawn as one paragraph, running together mid-sentence with not even a space between them. The fold now refuses to grow a settled reply, and `joinPages` carries the same rule across a page boundary. The other half is the row. `Event::TaskNote` records a background task reporting back -- a subagent that finished, or a backgrounded command -- with its title, how it ended and what it said; `TaskNoteRow` draws it as a card, since somebody said this, and its own row rather than an update to the Task call's, which is above everything the session has said since. Reported once however many of the CLI's two lifecycle shapes arrive. `SessionStatus::Waiting` is a session whose own turn is over while work it started is not. `Idle` means "waiting for a person" and this means the opposite, so reporting it as idle sent a "finished" notification at the one moment that was untrue. Drawn as "waiting" in `waitingColor`; the queue and the held-command boundary release on either end-of-turn status, so a message sent while a subagent runs is not held until it finishes. And a usage limit the account hits inside a subagent now reaches the session as well as the subagent's transcript. `resume.rs` can only schedule against a session, and a background Task outliving its parent's turn is the ordinary case, so auto-resume was doing nothing at all for it. The status word and its colour were two `when`s on two screens, and the second missed `waiting` silently; they are `sessionStatusWord`/`sessionStatusColour` now. Echo's `/subagent n` reproduces the whole shape, staggered a second apart. Verified on the emulator against the sandbox: 169 server tests, ktfmt, clippy, rustfmt, Android lint and the JVM unit tests all clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
74c07d687a
commit
5711c2568a
17 files changed
+891
-85
No files matched your search
@@ -67,6 +67,10 @@ pub struct SubagentInfo {
|
||||
/// session's but with no driver behind it.
|
||||
pub struct Subagent {
|
||||
dir: PathBuf,
|
||||
/// What a reader knows this subagent as -- `Meta::title`, kept here so
|
||||
/// naming one costs no file read. Never changes: a subagent is titled
|
||||
/// once, when it is created.
|
||||
title: String,
|
||||
transcript: Mutex<Transcript>,
|
||||
events: broadcast::Sender<SeqEvent>,
|
||||
/// Mirrors the transcript's last `Status` event, kept live rather than
|
||||
@@ -96,6 +100,13 @@ impl Subagent {
|
||||
*self.status.lock().unwrap() != SessionStatus::Exited
|
||||
}
|
||||
|
||||
/// What a reader knows this subagent as. Empty for one started from a
|
||||
/// child line before its Task call was seen and never renamed since --
|
||||
/// see `Subagents::get`, which passes no title on a reopen.
|
||||
pub fn title(&self) -> &str {
|
||||
&self.title
|
||||
}
|
||||
|
||||
fn append(&self, event: Event) {
|
||||
let mut transcript = self.transcript.lock().unwrap();
|
||||
match transcript.append(event, super::now()) {
|
||||
@@ -200,6 +211,7 @@ impl Subagents {
|
||||
let (events, _) = broadcast::channel(EVENT_BUFFER);
|
||||
Ok(Arc::new(Subagent {
|
||||
dir,
|
||||
title: meta.title.clone(),
|
||||
transcript: Mutex::new(transcript),
|
||||
events,
|
||||
status: Mutex::new(status),
|
||||
@@ -258,6 +270,16 @@ impl Subagents {
|
||||
}
|
||||
}
|
||||
|
||||
/// What the subagent named `id` is called, or `None` for an id that is
|
||||
/// not a subagent's at all -- a backgrounded command's tool call reaches
|
||||
/// here with the same shape, and answering it with a made-up name is
|
||||
/// worse than answering "this is not one".
|
||||
pub fn title_of(&self, id: &str) -> Option<String> {
|
||||
self.get(id)
|
||||
.map(|subagent| subagent.title().to_string())
|
||||
.filter(|title| !title.is_empty())
|
||||
}
|
||||
|
||||
/// Appends one event to a subagent's own transcript. A no-op, with a
|
||||
/// debug log, for an id nothing was started under -- a child line for a
|
||||
/// subagent this registry never opened is dropped rather than guessed
|
||||
|
||||
Reference in new issue
Block a user