Render background task completions as their own panel, not user prompts - #109
Open
dchaudhari7177 wants to merge 1 commit into
Open
Render background task completions as their own panel, not user prompts#109dchaudhari7177 wants to merge 1 commit into
dchaudhari7177 wants to merge 1 commit into
Conversation
A tool started with run_in_background=true reports back as a `type: user` entry whose message.content is a plain XML string starting with <task-notification> -- not the usual list of tool_result blocks. Since is_tool_result_message() only recognises the list form, these fell through to the "User" branch, and a long session read as though the human had been typing system-generated XML at intervals. They now classify as a "Background task" panel showing the status, the summary, and the task and tool-use ids that tie the entry back to the tool_use that started it. Parsed with a regex rather than an XML parser: <summary> carries free text which may itself contain markup, so the payload is not guaranteed well-formed. A payload that yields no fields still renders as a notification with its raw text -- falling back to the user branch would reintroduce exactly the bug being fixed. Fields are escaped on the way in. The four snapshot updates are the added CSS only; no rendered message changed. Closes simonw#99
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #99.
The problem
is_tool_result_message()only recognises the list oftool_resultblocks form. A background task reports back as atype: userentry whosemessage.contentis a plain string of XML, so it fell through to theUserbranch — and a long session read as though the human had been typing system XML at intervals.Result
Running your repro JSONL verbatim, the fourth entry now renders:
No
class="message user"anywhere in the output. The<tool-use-id>is surfaced because it is what ties the notification back to thetool_usethat started it, as @Necmttn suggested.Its own class rather than reusing
tool-reply: this is a lifecycle event with no tool output attached, and slate grey reads as quieter than the orange, which seemed right for something the human did not ask for at that moment. Easy to change if you'd rather it share the tool-reply styling.Regex, not an XML parser
<summary>carries free text, which can itself contain markup, so the payload is not guaranteed well-formed — anElementTreeparse would raise on exactly the inputs most worth displaying.More importantly the fallback still renders as a notification: a payload yielding no fields shows its raw text under the same panel. Falling back to the user branch would reintroduce the bug for precisely the malformed cases.
test_unparseable_xml_still_renders_as_a_notificationpins that.Fields are escaped on the way in, so
<summary><script>alert(1)</script></summary>is inert.Detection
Only a string content whose stripped form starts with
<task-notification>.test_a_real_user_prompt_is_notpins the negative side, including a prompt that merely mentions the tag in passing — a human writing "I asked <task-notification> about it" must still be a user message.Tests
8 tests in
TestTaskNotifications, written first and watched fail (collection error on the missing import, then the behaviour).uv run pytest: 130 passed, 18 snapshots passed.uv run black .clean.Four snapshots are updated. The diff is 8 added CSS lines each and nothing removed — no rendered message changed. I checked the diff before regenerating rather than after.
Also verified end to end by running
claude-code-transcripts json repro.jsonl -o /tmp/outon your repro and reading the HTML, which is the output quoted above.AI disclosure
Written with Claude Code (Claude Opus 5), following AGENTS.md: failing tests first, then implementation, then Black. The end-to-end repro run and the snapshot-diff review were done by hand.