After the live-to-final redesign (#3401/#3741), the execution trace of a turn is presented as a Compact Worklog / Activity abstraction: reasoning, progress text, and tool calls are grouped and summarized into rows like:
Thinking
terminal
Checked the web 6 times, ran 3 tools
For the default Web UI audience this is the intended direction — the final answer stays primary and supporting activity is quieter.
But a class of power users runs the Web UI as an agent cockpit: they want to watch the model work step by step, in real execution order. #3820 documents that the pre-#3401 transparent chronological stream is no longer reachable. The old behavior exposed concrete events directly in the transcript:
assistant progress text
Thinking / reasoning preview
tool: web_search query / compact output preview
assistant progress text
tool: memory action preview / result preview
assistant progress text
Two rounds of investigation on the issue established that this is not a
discoverability problem and cannot be fixed by the existing
Compact tool activity toggle (simplified_tool_calling):
The toggle is asymmetric. The live path branches on
isSimplifiedToolCalling() (static/ui.js — ensureLiveWorklogShell,
appendThinking, finalizeThinkingCard), but the static rebuild path does
not: the tool-call loop in renderMessages() unconditionally buckets tool
calls into an Activity group and summarizes them via _toolWorklogSummary.
So the moment a turn settles or the page reloads, the per-tool view collapses
back into the grouped Activity card regardless of the flag.
Even with the flag off, the live view is not a usable cockpit stream.
Measured in the running UI with _simplifiedToolCalling=false: individual
.tool-card-row nodes do exist in the DOM, but the running terminal card
was pushed out of the viewport (top=-24) while the Thinking card
dominated the readable area (top=55 bottom=296). “Tool cards exist in the
DOM” is not equivalent to “the concrete tool call is the primary,
readable item in the transcript flow.”
The issue discussion has converged on treating this as a real display-mode split, not another overloaded disclosure default. The maintainer has invited a PR that rebuilds the desired visual behavior on top of the refactored codebase, and this RFC records the proposed product boundary for that work.
Introduce Transparent Stream as an opt-in display mode that satisfies the following acceptance criteria. All four must hold; any one missing means #3820 is not solved.
Ran 11 commands / Checked the web 6 times.progress → Thinking → tool → result → progress → …).Plus two boundaries:
simplified_tool_calling. That flag stays only to preserve
existing stored preferences; the new mode does not read it. We do not extend
the old Compact tool activity checkbox semantics.A new preference, independent of simplified_tool_calling:
# api/config.py preferences
"chat_activity_display_mode": "compact_worklog", # | "transparent_stream"
compact_worklog — existing users see no change.Compact tool activity checkbox is marked deprecated. It is not
the seam for this feature.Mirrored to the front end as window._chatActivityDisplayMode, with a single
predicate used everywhere:
function chatActivityMode(){ return window._chatActivityDisplayMode || 'compact_worklog'; }
function isTransparentStream(){ return chatActivityMode() === 'transparent_stream'; }
The live backend emits ordered SSE events (token, interim_assistant,
reasoning, tool, tool_complete, done, …). Transparent Stream should
preserve that order instead of applying the Compact Worklog grouping strategy on
top. Settled messages and replay snapshots may not already expose the exact same
event shape, so each implementation slice must normalize the available
transcript, journal, reasoning, and tool-call metadata into an ordered activity
event sequence before rendering.
The fix should therefore be a strategy seam queried at the three render paths, all feeding a shared renderer where the data is sufficiently normalized:
SSE live ─────────┐
journal replay ───┼──► normalizeToEvents() ──► renderActivityEvent() ──► DOM
settled messages ─┘ (single source of render truth)
Using one renderActivityEvent() for all three paths is what prevents the
live-vs-static asymmetry that #3820 keeps hitting.
static/ui.js)These are the three regions that currently fail the criteria above. Line numbers refer to the tree observed when this RFC was written and are expected to drift; the function names are the durable anchors.
A. Settled rebuild — the tool-call loop in renderMessages(). It has
no transparent-stream branch at all today.
Add if(isTransparentStream()): for each tc, build a standalone card with
the existing buildToolCard(tc), anchor it to its assistant segment via
_assistantAnchorForActivity(...), and insert it
inline in aIdx/segmentSeq/burstId order — bypassing ensureActivityGroup
and _syncToolCallGroupSummary. This is the “missing half” the maintainer
identified, and the highest-value fix because it kills the settle/reload
collapse.
Spike-validated (2026-06-09). A throwaway prototype against master
confirmed this branch is ~31 lines and needs no new data: activityOrder is
already sorted chronologically, and the standalone buildToolCard(tc) /
_thinkingActivityNode(text) builders supply the compact-preview cards
directly (so acceptance criterion 3 is met by existing code on this path).
Reload/re-render consistency (criterion 4, settled) comes for free because the
rebuild cleanup already removes .tool-card-row and .agent-activity-thinking
before each render. DOM insertion order — including the shared-anchor fallback
case where two activity entries resolve to the same assistant segment — was
verified with a small node harness. The spike was local-only and not pushed.
B. Live — ensureLiveWorklogShell() and the live
reasoning/tool/interim_assistant handlers. The transparent branch must
not just fall back to the old thinkingRow (that is the path that pushed
the running tool card off-screen). It must append each event as an
independent row and keep the currently-running tool card as the primary
visible item, with Thinking shown as a preview above it rather than the
dominant block.
C. Summary bypass — in transparent mode, _syncToolCallGroupSummary() /
_toolWorklogSummary() do not run. No “Checked the web 6 times” aggregation
rows.
The journal replay path already replays events one by one. In transparent mode
it simply must not invoke the settle-time collapse
(_convertLiveActivityGroupToSettled) and must route events through the same
renderActivityEvent() as live and settled. Replaying a session in transparent
mode should reproduce the same picture the live stream left.
compact_worklog for everyone (current proposal),
or honor a prior simplified_tool_calling=false as a hint to start in
transparent mode? Leaning toward: no implicit migration — keep it an explicit
opt-in.Shipped as independently-mergeable slices. Ordering principle: certain, testable, small-footprint changes first; subjective, hard-to-test, viewport-feel changes later.
chat_activity_display_mode preference, boot-time plumbing, and the
isTransparentStream() predicate behind tests, but do not expose a
Settings control that appears to do nothing. This keeps the first slice safe
without creating a confusing empty toggle.renderActivityEvent() on
the replay path; gate to the render window for long transcripts.Per the RFCs README: this RFC is a design direction, not an invitation to implement fragments. Before opening any implementation PR, confirm in #3820 that the slice is wanted and that @ai-ag2026 is not already building it, to avoid duplicate work.