fix: show WebChat preamble progress during tool activity#95708
Conversation
There was a problem hiding this comment.
Pull request overview
Updates the Control UI WebChat streaming pipeline so stream:"item" preamble progress events become visible to users during long tool-using turns, aligning WebChat behavior with other UIs that already surface this commentary.
Changes:
- Teach
app-tool-stream.handleAgentEventto consumestream:"item"+kind:"preamble"progress events and store/update them as keyedchatStreamSegments. - Materialize visible pre-final stream segments into assistant message blocks when a renderable final assistant message arrives, then clear the stream segment buffer.
- Add/adjust node + controller tests to cover keyed preamble progress handling and the updated finalization behavior.
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| ui/src/ui/controllers/chat.ts | Materializes visible pre-final stream segments before appending a renderable final assistant message. |
| ui/src/ui/controllers/chat.test.ts | Updates expectations to confirm pre-final segments become assistant messages and the segment buffer is cleared. |
| ui/src/ui/app-tool-stream.ts | Adds handling for stream:"item" preamble progress events, storing them as keyed stream segments (itemId). |
| ui/src/ui/app-tool-stream.node.test.ts | Adds test coverage for keyed preamble progress segment storage/update behavior. |
| host.chatStreamSegments = [ | ||
| ...host.chatStreamSegments, | ||
| { | ||
| text: progress.text, | ||
| ts: Date.now(), | ||
| ...(progress.itemId ? { itemId: progress.itemId } : {}), | ||
| }, | ||
| ]; |
|
@clawsweeper re-review Updated proof and validation are in the PR body: annotated WebChat GIF, focused local tests, UI build, and crabbox local-container fresh-PR validation for 147 focused tests. |
|
🦞🧹 I asked ClawSweeper to review this item again. |
7372bcb to
83e04b8
Compare
|
@clawsweeper re-review Updated proof and validation were added to the PR body at that point in the review loop: annotated WebChat proof GIF, focused tests, Real behavior proof parser check, and Crabbox fresh-PR validation ( |
|
🦞🧹 I asked ClawSweeper to review this item again. |
83e04b8 to
a47d405
Compare
a47d405 to
533fba2
Compare
|
Follow-up after Codex review and cleanup:
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
533fba2 to
6fe30bb
Compare
|
No-code force-push to The previous failing cron shard is outside this PR's UI diff and passes locally with:
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
CI follow-up on current head
|
|
Codex review: needs real behavior proof before merge. Reviewed June 30, 2026, 9:01 PM ET / 01:01 UTC. Summary PR surface: Source +432, Tests +552. Total +984 across 59 files. Reproducibility: yes. source inspection gives a high-confidence repro path: send a current-main Control UI agent event with stream=item and data.kind=preamble, and handleAgentEvent returns at the non-tool branch before updating chatStream or chatStreamSegments. I did not run a live browser repro in this read-only review. Review metrics: 1 noteworthy metric.
Stored data model Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Mantis proof suggestion Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Maintainers should choose one WebChat commentary policy, fold or reconcile the keyed multi-segment behavior with the chosen path, then require current-head visual proof for the final default and toggle behavior before merge. Do we have a high-confidence way to reproduce the issue? Yes, source inspection gives a high-confidence repro path: send a current-main Control UI agent event with stream=item and data.kind=preamble, and handleAgentEvent returns at the non-tool branch before updating chatStream or chatStreamSegments. I did not run a live browser repro in this read-only review. Is this the best way to solve the issue? Unclear as the best final solution: the keyed reconciliation is useful, but the saved persistence setting and overlap with #92236 make the policy choice maintainer-owned rather than settled by the patch alone. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 23527a456d11. Label changesLabel justifications:
Evidence reviewedPR surface: Source +432, Tests +552. Total +984 across 59 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
|
ClawSweeper follow-up for the current-head proof request. Updated head: What changed after the feedback:
Current-head browser proof: Validation:
Please re-review this head. @clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
ce39408 to
c57819e
Compare
|
Refreshed the branch and addressed the review findings. New head [P1] Conflicting/dirty → resolved. Rebased the 3 commits onto current upstream [P2] Keyed commentary live ordering ( Persistent-by-default vs transient (#92236). This is a maintainer UX-policy choice, not an author-side defect. Where the proof is: The rebase and the ordering fix do not change the commentary-persistence UI behavior shown in the GIF (single-tool ON/OFF toggle), so the existing capture remains valid for the current head; the ordering fix is covered by the new unit regression rather than a re-capture. Capture environment is stated honestly in the body (real built Control UI bundle + real render/reconciliation path, mock Gateway/transport). @clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
c57819e to
f61236c
Compare
|
Rebased onto current main at @clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
Render Control UI item and preamble progressText events as chat stream segments and preserve pre-final stream commentary before appending final assistant messages. Keyed preamble segments stay independent from accumulated stream snapshots, so distinct same-text commentary items render intact. Co-authored-by: Chisel <[email protected]>
Co-authored-by: Chisel <[email protected]>
Add a per-viewer 'Keep commentary' toggle (UiSettings.chatPersistCommentary, default true) that controls whether keyed Codex preamble/commentary blocks stay after the final answer or clear with it. - Persist (default): keyed commentary materializes as durable blocks, current behavior, existing proof unchanged. - Transient (toggle off): commentary stays live during streaming but is never materialized, so it disappears as the final message arrives. This is the transient-only behavior from openclaw#92236, now user-selectable instead of a maintainer-level either/or policy choice. Single gating point in materializeVisibleStreamState (skip itemId-keyed parts when persistCommentary is false); threaded from settings through the chat event handler. Adds desktop + mobile header toggles and an en.ts label (locale bundles regenerated via ui:i18n:sync, English fallback). Tests: reconciliation persist/transient coverage, final-event handler honors the setting, settings round-trip + header button assertions updated.
Keyed preamble commentary was appended after the whole tool loop, so it relied solely on the final visible-time sort for placement and lost the insertion-order tiebreaker against tool cards. Splice each keyed commentary segment into the items list before the first item with a strictly-later timestamp, so a preamble that arrived before a later tool renders above that tool while the run is live (not only after final materialization). Tools sharing the commentary timestamp that are already visible stay above it. Adds a buildChatItems regression covering a keyed preamble between two tools.
878b761 to
bd1891c
Compare
|
Landed via rebase onto main.
Thanks @ragesaq! |

What Problem This Solves
Control UI was not showing WebChat/Codex preamble and commentary progress while tools were active, so users could see an idle-looking run until the final assistant message arrived. This patch projects those progress events into the chat stream, preserves pre-final commentary, and keeps keyed preamble updates from being collapsed into unrelated final messages.
Relation To #92236
PR #92236 covers the same root signal, native Codex app-server
stream:"item"/kind:"preamble"progress, with a narrower transient-progress approach: project oneprogressTextvalue intochatStream, mark it as progress, and clear it on final without saving it to assistant history.This PR handles the larger WebChat reconciliation path:
item/preamblevisibilityitemIdupdatesNO_REPLYprogressCommentary,Tool call, andFinal messageblocksMaintainer choice: these two PRs should not land as divergent UX policies. If maintainers prefer #92236's transient-only behavior, this PR should be treated as the keyed/multi-segment reconciliation variant to fold into that landing path. If maintainers want visible pre-final commentary preserved as chat content, this PR is the more complete path.
Summary
Commentary,Tool call, andFinal messageblocks.Behavior Contract
stream:"item",kind:"preamble", keyed text updateitemId.itemIdupdates with newprogressTextitemIdupdates with emptyprogressTextitemIdvalues with the same textNO_REPLY-only progressFinal message, neverCommentary.Implementation
itemandpreambleprogress events into Control UI chat stream segments.Commentary, while live final-answer streams remainFinal message.Evidence
Animated proof: commentary-persistence toggle (
chatPersistCommentary)The static screenshot above proves the labeled
Commentary/Tool call/Final messagerender path. This animated capture proves the configurable persistence behavior added in the third commit, on the live PR head (c57819e5fd):Commentary->Tool call->Commentary->Final message, and the pre-final commentary stays pinned above the final answer.materializeVisibleStreamState+chatPersistCommentary), not destructive history loss.Capture environment: the repository's own Control UI e2e harness, which serves the real built Control UI bundle and drives it through real
agent/chatstream events from a mock Gateway. The model/transport is mocked; the changed code path (client-side render reconciliation and the persistence toggle) is the real shipped code. Built from a fresh worktree at PR headc57819e5fdafter a cleanpnpm install.Behavior or issue addressed: WebChat now surfaces native Codex app-server
stream:"item"/kind:"preamble"progress during tool activity, keeps keyed preamble segments independent, and distinguishes commentary/tool/final blocks visually.Real environment tested: local OpenClaw Control UI source checkout on Linux, exercised through the Control UI render path and a system Chromium browser for the visible proof.
Exact steps or command run after this patch: loaded the mock Control UI proof surface in system Chromium, rendered the WebChat conversation state containing commentary, tool activity, and final message blocks, and captured the browser screenshot above. After rebasing onto current
mainand adding the keyed-commentary ordering fix, reran the focused UI/runtime validation listed below on headc57819e5fdff037932735b53a442cc2d75b2d47d.Evidence after fix: the screenshot shows all three labels:
Commentary,Tool call, andFinal message. The DOM proof for the labeled render path verified the labels exactly as["Commentary", "Tool call", "Final message"].Observed result after fix: preamble/commentary progress is visible before the final assistant answer, tool cards remain distinct, and live final-answer streams are labeled as
Final messagerather thanCommentary.What was not tested: this proof does not launch a live frontier-model WebChat turn after the rebase. The visual capture was taken at the same UI feature diff; the rebase onto current
mainand the keyed-commentary ordering fix do not change the commentary-persistence UI behavior shown in the GIF, and the ordering fix is covered by a newbuildChatItemsunit regression. The current head was validated with the focused tests, typecheck, and i18n check listed below.Proof limitations or environment constraints: the rebase changed the base commit and head SHA, not the UI feature diff. A fresh live Mantis capture would be the strongest proof if maintainers want live-run evidence instead of the local browser proof plus current-head validation.
Fleet-Tool Evidence
c57819e5fdff037932735b53a442cc2d75b2d47d, 4 commits ahead of current upstreammain;git merge-treeagainst currentmainreports a clean merge (no conflicts).ragesaq <[email protected]>.vitest run ui/src/ui/chat/build-chat-items.test.ts ui/src/ui/chat/stream-reconciliation.test.ts ui/src/ui/chat/grouped-render.test.ts ui/src/ui/controllers/chat.test.ts ui/src/ui/app-tool-stream.node.test.ts ui/src/ui/storage.node.test.ts(6 files, 293 tests passed, includes the new keyed-commentary ordering regression)node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.test.ui.json(pass)node --import tsx scripts/control-ui-i18n.ts check(clean; the 2 new keys carry English fallback across all 18 locales)git merge-tree --write-tree upstream/main HEAD(clean, no conflicts)ClawSweeper Follow-Up
main;mergeableis now true andgit merge-treereports a clean merge. Headc57819e5fd.build-chat-items.ts)buildChatItemsregression covers a keyed preamble between two tools.chatPersistCommentarydefaults to keep; flipping the default to transient (opt-in persistence) is a one-line change if maintainers prefer #92236's behavior as canonical.check-lintfinish and address concrete failurescheck-lintandcheck-prod-typesfailures both pointed atsrc/config/sessions/session-accessor.ts, outside this PR diff. Current upstreammaincontainse3058efa10, which removes that unused binding.Relation To #92236; maintainers should choose one policy or fold this keyed reconciliation behavior into the preferred path.openclawStreamFallbackand keyedchatStreamSegmentsare Control UI stream metadata used for local render reconciliation.AI Disclosure
This PR was authored and submitted under ragesaq's GitHub identity. Implementation assistance was provided by Chisel, a OpenClaw agent. The evidence above lists the verification commands and review loop outputs used before requesting maintainer/bot review.