agent-ui: collapse streamed thinking_tokens ticks into one live row
claude streams a running 'estimated_tokens' counter as system/thinking_tokens events — many per turn (thousands in a long turn). renderStream rendered each as a '⚙ thinking_tokens' note, flooding the terminal scrollback. Coalesce consecutive ticks into a single '🧠 thinking … ~N tokens' note row that updates in place; reuse the row only while it's still the last one rendered (nextElementSibling == null) so any other event starts a fresh one. Closes #1818.
This commit is contained in:
parent
6bb976ec72
commit
63a79d76ba
2 changed files with 31 additions and 0 deletions
|
|
@ -71,6 +71,13 @@ suffix, so the terminal degrades cleanly against older event shapes.
|
||||||
|
|
||||||
1. Drops `system/init`, `rate_limit_event`, `result` (noise /
|
1. Drops `system/init`, `rate_limit_event`, `result` (noise /
|
||||||
handled elsewhere — `result` powers the `cost` badge).
|
handled elsewhere — `result` powers the `cost` badge).
|
||||||
|
1a. `system/thinking_tokens` (claude streams a running
|
||||||
|
`estimated_tokens` counter while thinking — many per turn) →
|
||||||
|
collapses into a **single** `🧠 thinking … ~N tokens` `.note`
|
||||||
|
row that updates in place. Consecutive ticks reuse the row only
|
||||||
|
while it's still the last one rendered (`nextElementSibling ==
|
||||||
|
null`); any other event after it makes the next tick start a
|
||||||
|
fresh row. Avoids a note-per-tick scrollback flood.
|
||||||
2. `subtype == "task_started" | "task_notification"` →
|
2. `subtype == "task_started" | "task_notification"` →
|
||||||
`renderTaskEvent` (subagent activity gets the `⌁` glyph).
|
`renderTaskEvent` (subagent activity gets the `⌁` glyph).
|
||||||
3. `type == "assistant"` → walk `message.content[]`:
|
3. `type == "assistant"` → walk `message.content[]`:
|
||||||
|
|
|
||||||
|
|
@ -1695,6 +1695,12 @@ window.marked = marked;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// Coalescing target for the live `thinking_tokens` counter.
|
||||||
|
// Holds the row currently showing the running estimate so consecutive
|
||||||
|
// ticks update in place instead of appending a note each. Reset
|
||||||
|
// implicitly: we only reuse it while it's still the last row rendered
|
||||||
|
// (see the nextElementSibling check), so any other event starts fresh.
|
||||||
|
let thinkingTokensRow = null;
|
||||||
function renderStream(v, api) {
|
function renderStream(v, api) {
|
||||||
// Drop claude's result line and rate-limit — noise. TurnEnd
|
// Drop claude's result line and rate-limit — noise. TurnEnd
|
||||||
// communicates pass/fail; rate-limit events are noisy status chatter.
|
// communicates pass/fail; rate-limit events are noisy status chatter.
|
||||||
|
|
@ -1722,6 +1728,24 @@ window.marked = marked;
|
||||||
api.row('note stderr', '✗ api error · ' + msg);
|
api.row('note stderr', '✗ api error · ' + msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Live thinking-token counter — claude streams many of these per
|
||||||
|
// turn (a running `estimated_tokens` total while it thinks). Collapse
|
||||||
|
// consecutive ticks into ONE in-place-updating row instead of a note
|
||||||
|
// per tick. Reuse our row only while it's still the last one
|
||||||
|
// in the scrollback; once any other event renders after it, the next
|
||||||
|
// tick starts a fresh row.
|
||||||
|
if (v.subtype === 'thinking_tokens') {
|
||||||
|
const n = v.estimated_tokens;
|
||||||
|
const label = '🧠 thinking … '
|
||||||
|
+ (n != null ? '~' + Number(n).toLocaleString() + ' tokens' : '');
|
||||||
|
if (thinkingTokensRow && thinkingTokensRow.isConnected
|
||||||
|
&& thinkingTokensRow.nextElementSibling === null) {
|
||||||
|
thinkingTokensRow.textContent = label;
|
||||||
|
} else {
|
||||||
|
thinkingTokensRow = api.row('note', label);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Other system subtypes (context_window_exceeded, etc.) — render a
|
// Other system subtypes (context_window_exceeded, etc.) — render a
|
||||||
// muted note with the subtype label; reserve the loud orange `sys`
|
// muted note with the subtype label; reserve the loud orange `sys`
|
||||||
// catch-all for truly unrecognised top-level types.
|
// catch-all for truly unrecognised top-level types.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue