Goal
Long-running session conversations should remain visually stable and responsive while a new response is streaming. Sending or streaming a new turn in a long session should not cause visible flicker, scroll jitter, or unnecessary whole-timeline work.
Scope
In scope:
- Investigate and optimize the session timeline render path for long conversations.
- Keep DOM work bounded for long-lived sessions, not only initial history loads.
- Reduce per-turn work so streaming updates affect only the active/current turn where possible.
- Review the current use of
content-visibility: auto / contain-intrinsic-size and avoid height-estimation jitter near the active/bottom turns.
- Preserve existing scroll behavior: stay pinned when the user is near the bottom, but do not pull the user down when they are reading older history.
Out of scope:
- Redesigning the session UI.
- Changing model/runtime behavior.
- Rewriting vendored agent runtime code under
packages/opencode/ unless investigation proves it is required.
Relevant files or context
Observed code paths:
packages/app/src/pages/session.tsx
packages/app/src/pages/session/message-timeline.tsx
packages/app/src/pages/session/use-session-timeline-data.ts
packages/app/src/pages/session/use-session-history-window.ts
packages/app/src/pages/session/use-session-timeline-interaction.ts
packages/ui/src/components/session-turn.tsx
packages/ui/src/components/message-part.tsx
packages/ui/src/components/message-part.css
packages/ui/src/components/session-turn.css
packages/app/src/context/renderer-diagnostics.ts
Current likely causes from inspection:
- Long-lived sessions can keep accumulating rendered turns after the initial history window, so DOM size grows over time.
- Each
SessionTurn receives the full messages array and derives its assistant messages by scanning/slicing/filtering the full list, causing repeated work as the conversation grows.
content-visibility: auto with estimated intrinsic sizes can reduce paint cost, but it does not reduce DOM node count and can contribute to scroll/height jitter when estimates differ from real message heights.
- Existing diagnostics already track related incidents such as scroll jumps, timeline remounts, visible message clears, layout shift, and jank bursts.
Performance best-practice direction:
- Prefer a virtualized/windowed message list for large histories, with dynamic height measurement/caching and overscan.
- Normalize timeline data by IDs so list order changes separately from per-message content updates.
- During streaming, update only the active/current row and avoid making sibling turns recompute.
- Treat
content-visibility as a supplemental optimization, not a substitute for virtualization.
Verification
- Add or update targeted tests around the session history/windowing behavior so long-lived sessions remain bounded.
- Add focused tests for timeline derivation to ensure assistant messages can be resolved without each turn scanning the full message list.
- Manually verify a long session while streaming a response:
- no visible flicker near the bottom,
- no scroll jump to top,
- jump-to-latest behavior still works,
- user scroll position is respected when reading older messages.
- Use existing renderer diagnostics to confirm no new
incident.session_scroll_jump_to_top, incident.session_timeline_remount, incident.session_visible_messages_cleared, incident.session_layout_shift, or incident.session_jank_burst appears during the repro path.
Execution mode
Agent should investigate and propose a plan first.
Goal
Long-running session conversations should remain visually stable and responsive while a new response is streaming. Sending or streaming a new turn in a long session should not cause visible flicker, scroll jitter, or unnecessary whole-timeline work.
Scope
In scope:
content-visibility: auto/contain-intrinsic-sizeand avoid height-estimation jitter near the active/bottom turns.Out of scope:
packages/opencode/unless investigation proves it is required.Relevant files or context
Observed code paths:
packages/app/src/pages/session.tsxpackages/app/src/pages/session/message-timeline.tsxpackages/app/src/pages/session/use-session-timeline-data.tspackages/app/src/pages/session/use-session-history-window.tspackages/app/src/pages/session/use-session-timeline-interaction.tspackages/ui/src/components/session-turn.tsxpackages/ui/src/components/message-part.tsxpackages/ui/src/components/message-part.csspackages/ui/src/components/session-turn.csspackages/app/src/context/renderer-diagnostics.tsCurrent likely causes from inspection:
SessionTurnreceives the fullmessagesarray and derives its assistant messages by scanning/slicing/filtering the full list, causing repeated work as the conversation grows.content-visibility: autowith estimated intrinsic sizes can reduce paint cost, but it does not reduce DOM node count and can contribute to scroll/height jitter when estimates differ from real message heights.Performance best-practice direction:
content-visibilityas a supplemental optimization, not a substitute for virtualization.Verification
incident.session_scroll_jump_to_top,incident.session_timeline_remount,incident.session_visible_messages_cleared,incident.session_layout_shift, orincident.session_jank_burstappears during the repro path.Execution mode
Agent should investigate and propose a plan first.