Report a backgrounded command into the card that launched it

A backgrounded command has no subagent, so there is no second transcript for
its report to live in and its own tool card is the only record of it anywhere
-- and until the task notification arrives that card is showing the launch
result, which says the command is running. It was left saying that for ever.

The report now updates the call's own row (`Event::ToolUpdate` against its
tool_use id), so the card ends up holding what became of the command instead
of a claim nothing was ever going to correct. That includes the endings that
carry no summary: those are exactly the ones that went wrong, and a stale
"running in background" reads worst on them, so they say the status word
rather than nothing. A task with a subagent behind it is untouched and its
report stays where it was, in that subagent's own transcript.

Echo grew `/background [seconds]` for the shape end to end: the Bash call, the
launch result, a turn that ends `waiting`, and the completion arriving later
to correct the card and start a second turn.

Verified on the emulator: the card reads `Background command "sleep 5 && echo
done" completed (exit code 0)` where it had said "Command running in background
with ID: ...". 172 server tests, ktfmt, clippy, rustfmt, Android lint and the
JVM unit tests clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
irisandClaude Opus 5 committed 2026-09-06 21:47:54 -04:00
1 parent 1bbb642973
commit 9cc52beb09
5 files changed
+199 -15

No files matched your search

+17 -8
View File
@@ -676,14 +676,23 @@ them: a hairline rule, no words, no colour. It is made by the fold rather than
sent by the server because it is not something that happened — it is the
boundary between two things that did.
**Nothing else about a background task goes in the session's transcript.** That
was tried and was wrong: a row per finished subagent is a screenful of dividers
about work the reader was not asking after, and one of them turned out to be a
whole shell command drawn as centred prose, because its words came from
somewhere with no reason to keep them short. A subagent's closing report is
recorded as that subagent's own transcript's closing text and is read there.
The parent's transcript gets a row for a message a subagent genuinely *sends*
it, which arrives by the peer path and already has one.
**Nothing else about a background task gets a row of its own.** That was tried
and was wrong: a row per finished subagent is a screenful of dividers about work
the reader was not asking after, and one of them turned out to be a whole shell
command drawn as centred prose, because its words came from somewhere with no
reason to keep them short. The parent's transcript gets a row for a message a
subagent genuinely *sends* it, which arrives by the peer path and already has
one.
**The report goes to whichever record is the only one of it**, and the two cases
are different places. A subagent has a transcript of its own, and its closing
words are that transcript's last line. A backgrounded *command* has none: its
own tool card is the only record of it anywhere, and until the notification
arrives that card is showing the launch result, which says the command is
running. So the card is updated (`Event::ToolUpdate` against the call's own id)
rather than left making a claim nothing will ever correct — including for the
endings that carry no summary, which are exactly the ones that went wrong and
the ones a stale "running in background" reads worst on.
What the notification is still used for is the status: it is what closes a task
in `Status::Waiting`'s bookkeeping. Handled once, however many of the two