fix(agents-server-ui): hide empty reasoning blocks and interleave them with run items#4570
Conversation
…m with run items Two fixes for the reasoning stream UI: - Models that report reasoning without exposing the tokens (e.g. OpenAI codex models) open a reasoning row on thinking_start that never receives a delta. Filter contentless rows out client-side so no empty 'Thought' block renders. Encrypted (Anthropic redacted) rows are kept and still render their placeholder. - Reasoning blocks now interleave with the run's text / tool-call items by _timeline_order instead of all stacking above the whole response — in multi-step tool-using runs each block renders where the model emitted it (think, write, call tool, think, ...). ReasoningSection becomes ReasoningBlock (one block per row) with expand/collapse state lifted to AgentResponseLive so it survives remounts, same as before. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## kevin/reasoning-content #4570 +/- ##
============================================================
- Coverage 57.95% 34.03% -23.93%
============================================================
Files 369 224 -145
Lines 40622 19080 -21542
Branches 11506 6775 -4731
============================================================
- Hits 23541 6493 -17048
+ Misses 17006 12552 -4454
+ Partials 75 35 -40
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Electric Agents Mobile BuildLocal mobile checks ran for commit The EAS Android preview build was skipped because the |
Summary
Two fixes for the reasoning-stream UI added in #4508 (note: that feature is on
kevin/reasoning-content, not yet onmain, so this PR targets the feature branch):No more empty thinking blocks. Some models report that they reasoned but never expose the tokens (e.g. OpenAI codex models) —
pi-adapter.tsdeliberately opens a reasoning row onthinking_starteven when no delta ever arrives, so the UI rendered a blank live block that settled into an empty▸ Thoughtrow.AgentResponseLivenow filters out rows with no content client-side. Anthropic redacted rows (encryptedset) are kept and still render their placeholder, and a genuinely-streaming block appears as soon as its first delta lands. Persistence is untouched — empty rows are still recorded (they can carry the encrypted payload that must round-trip to the model).Reasoning blocks interleave with the response instead of stacking at the top. Previously all of a run's reasoning rows rendered in one
<ReasoningSection>above every text/tool-call item, so in multi-step tool-using runs step-3 thinking appeared above step-1 output. Reasoning rows already carry the same_timeline_orderas text/tool-call rows, soAgentResponseLivenow merges both streams into one ordered render list — each block renders at the position the model emitted it (think → write → call tool → think → …). On an order tie (legacy rows without_timeline_order), reasoning sorts before output.Implementation
ReasoningSection→ReasoningBlock: the component now renders a single entry; expand/collapse state is lifted toAgentResponseLive(keyed by row key) so it still survives the block unmounting/remounting, same as before.ReasoningEntrygains anorderfield (sameTimelineOrderspace as run items).LiveRenderEntryunion +compareLiveRenderEntriescomparator; item-vs-item ties keep delegating tocompareLiveRunItems..rootwidth wrapper inReasoningSection.module.cssis gone — blocks are now direct children of theAgentResponseroot, which applies the same width treatment, so they align with text items.lastItemby identity instead of array index (the index no longer maps 1:1 once reasoning entries are interleaved).Test plan
pnpm typecheckclean inagents-server-uipnpm testinagents-server-ui(88 passed)🤖 Generated with Claude Code