fix(tools): keep the tool that was running when the interrupt landed - #23
Merged
Conversation
Pressing Esc mid-tool erased the tool. The transcript kept the calls that
had already finished and the "Interrupted · …" note, but the one row that
said WHAT was cut off — the command still running — vanished with the
turn, because a tool that never completes never gets a completion event
and `activeTools` is dropped on interrupt.
Upstream keeps it. Claude Code 2.1.235, Esc four seconds into a long find:
⏺ Bash(find / -name '*.zzzznope' 2>/dev/null)
⎿ Interrupted · What should Claude do instead?
and the collapsed view folds that row into the brief like any other
settled call, which is why the note is attached as an ordinary result and
not an error — an error would break the row out of the brief and read as a
failure it was not.
Now the same, with the note printed once. It had been the reply text too,
so carrying it on the row would have printed the same sentence twice three
lines apart; the reply keeps only the model's own partial text. With no
partial text at all the shelf hangs off a bare trail message the way a
settled segment does, rather than an assistant bubble with no text, which
rendered as an empty `⏺` row.
Separately, a workflow call was identified by the script it runs:
before ⏺ Workflow(script: log("hello from the workflow") return { ok: true }, met…)
after ⏺ Workflow(gap-probe)
A script is payload like a file's content, so it joins the bulk set — and
an object argument carrying its own `name` (a workflow's `meta` block) now
renders as that name, since as JSON the identity sits behind a brace and
two quotes and the row's 64 columns lose it to the ellipsis.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ericleepi314
added a commit
that referenced
this pull request
Aug 19, 2026
…e fold (#24) The token read-outs undercounted every run that fanned out. A child's `assistant/message` usage went onto the child's own row — the agents overlay, and the `Done (3 tool uses · 1.2k tokens)` a delegation settles with — and stopped there. The session odometer behind `tokens: N in / M out`, `/status` and the context read-out never heard about it, so a turn that delegated most of its work reported the sliver the parent did itself: tokens: 1.1k in / 84 out ← parent only tokens: 42.6k in / 3.1k out ← what the session actually spent It is one bill. The two halves of the bus are already disjoint — `bindAgent` routes by session identity and `onChildSessionEvent` drops anything the lifecycle never announced as a child — so folding the child's step in counts it exactly once, and a sibling top-level session still cannot leak in. Also lands the test coverage for the fold that #23 carried in without it: `statsFromUsage` and its precedence behind a CostSnapshot, the price-free `usage` path through `message.complete`, and the rider a resume replays out of the log. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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.
What
Pressing Esc mid-tool erased the tool. The transcript kept the calls that had already finished and the "Interrupted · …" note, but the one row that said what was cut off — the command still running — vanished with the turn.
A tool that never completes never gets a completion event, and
activeToolsis dropped on interrupt.Claude Code 2.1.235, Esc four seconds into a long
find:and its collapsed view folds that row into the brief (
Searched for 1 pattern) like any other settled call — which is why the note attaches as an ordinary result and not an error. An error would break the row out of the brief and read as a failure it was not.Printed once
The note had been the reply text too, so carrying it on the row would print the same sentence twice, three lines apart. The reply now keeps only the model's own partial text. With no partial text at all, the shelf hangs off a bare trail message the way a settled segment does — an assistant bubble with no text rendered as an empty
⏺row.Also: a workflow was identified by the script it runs
A script is payload like a file's content, so it joins the bulk set. And an object argument carrying its own
name(a workflow'smetablock) now renders as that name — as JSON the identity sits behind a brace and two quotes, and the row's 64 columns lose it to the ellipsis, which is the one thing the parens exist to prevent.Test
npm run typecheck,npm test— 150 files / 1942 passingnode scripts/verify-boundary.mjscreateGatewayEventHandler.test.ts); a workflow renders as its meta name (toolArgs.test.ts)scripts/tool-gallery.py workflow🤖 Generated with Claude Code