Skip to content

Control UI / WebChat: assistant text-between-tools blocks render as cumulative duplicates instead of fresh cards #72341

@jason-alvarez-git

Description

@jason-alvarez-git

Summary

In Control UI WebChat, when an agent loop produces multiple text → tool_call → tool_result → text → tool_call → ... cycles, each new assistant text block is rendered as a separate visible message card containing the cumulative text from all prior assistant text blocks in the same turn. The gateway-side persistence is correct (separate, non-cumulative blocks); only the WebChat renderer is at fault.

Reproduced on 2026.4.24. Likely older too — possibly older than 2026.4.20. Survives the 4.24 fix #71371 and the 4.23 fix #70921.

Evidence

Single user turn, agent loop with 3 narration-before-tool blocks. JSONL entries from the session transcript (timestamps and content trimmed for clarity; sequential, no other assistant entries in between):

ts (HH:MM:SS) role content length content preview
T+00.000 assistant 184 chars "Now I need to create an updated …configuration:"
T+05.229 assistant 87 chars "Now let me upload this configuration to …"
T+27.040 assistant 95 chars "Let me try a different approach - I'll use the … CLI to …"

Note each persisted block is only the new chunk — 184/87/95 chars respectively, not 184/271/366. So the gateway is correct.

What WebChat shows for the same turn (paraphrased; three distinct message cards at three distinct timestamps):

  • Card 1: block 1 text (184 chars)
  • Card 2: block 1 text + block 2 text (cumulative ~271 chars, no separator between them — note the missing space at the join)
  • Card 3: block 1 + 2 + 3 text (cumulative ~366 chars)

The cumulative-without-separator pattern (e.g. "...configuration:Now let me upload...") suggests the renderer is not doing per-card delta tracking — it treats each new assistant block as another append to the current card's text accumulator, but also inserts a new card boundary, so old text leaks into each new bubble.

Where (suspected)

Control UI WebChat client bundle under dist/control-ui/assets/. The assistant-card append/reset logic is the surface — specifically whichever code listens for the gateway's stream events and decides when to close the current card and open a new one. A tool_call arriving after a text block should reset the text accumulator so the next text block lands in a fresh card.

Reproduction

  1. From WebChat, send a prompt that requires multiple sequential tool calls with narration between them (e.g. "do a multi-step task that needs you to ssh somewhere, then upload a file, then verify").
  2. Observe the visible cards while the run is in progress (this is not a history-refresh artifact — duplicates appear during live streaming and persist).
  3. Hard-refresh the browser tab — the duplicates disappear and the correct sequence loads from the JSONL. Confirms the bug is purely client-side render state.

Severity

Low (correctness) / High (UX). Transcript and memory are both unaffected, but the visible chat becomes very noisy during long-running tool loops.

Note

This is adjacent to but distinct from #71371 (optimistic tail message flicker on history refresh). That fix kept tails visible; this bug is about resetting them on tool-block boundaries.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions