Manages message data and widget window for virtualization.
This class stores all messages as data and manages a sliding window of widgets that are actually mounted in the DOM.
Total number of messages stored.
Number of user-authored rows stored, including local-only commands.
Counts USER and SKILL rows. A /skill invocation mounts a SKILL row
instead of a USER row, so each skill turn contributes exactly one;
everything else (ASSISTANT, TOOL, APP, ERROR, ...) is excluded.
This counts stored transcript rows, not server turns: it spans the whole
store rather than the rendered window (visible_count), and it includes
local-only flows such as !shell and most slash commands, which mount a
UserMessage without ever invoking the server. It is therefore a broader
population than the "conversation turns" the offload report derives from
graph state, which counts only non-internal HumanMessages the model
actually saw. Do not converge the two -- they answer different questions.
Number of messages currently visible (as widgets).
Check if there are archived messages above the visible window.
Check if there are archived messages below the visible window.
Add a new message to the store.
Load many messages at once, keeping only the tail visible.
This is optimized for thread resumption: all messages are stored as
lightweight data, but only the last INITIAL_WINDOW_SIZE entries are
marked visible. A smaller monkeypatched WINDOW_SIZE still caps the
initial window, which keeps focused virtualization tests deterministic.
Get a message by its ID.
Update a message's data.
Only fields in _UPDATABLE_FIELDS may be updated. Unknown field
names raise ValueError to catch typos early.
Set the currently active (streaming) message.
Active messages are never archived. Only the previous active message's
_ACTIVE_REASON is released, so a message also protected for another
reason (e.g. a live tool) stays protected.
Check if a message is the active streaming message.
Keep a live message mounted during window updates.
Reasons accumulate independently; a message stays protected until every reason is released. Idempotent per reason.
Release one protection reason from a message.
The message becomes virtualizable only once it has no remaining reasons. Releasing a reason the message does not hold is a no-op.
Check whether a message is protected from virtualization.
Check if the visible window exceeds the soft target size.
Check if mounted history exceeds the immediate-pruning limit.
Get the oldest visible messages that should be pruned.
Returns a contiguous run of messages from the START of the visible window. Stops at the first protected message (the active stream or a live tool run) to avoid creating gaps in the visible window (which would desync store state from the DOM).
Get newest visible messages that should be pruned below the viewport.
Mark messages as pruned (widgets removed).
Advances _visible_start past consecutive pruned messages at the front
of the window.
Mark bottom-window messages as pruned.
Get messages above the visible window to hydrate.
Mark that messages above were hydrated.
Get messages below the visible window to hydrate.
Mark that messages below were hydrated.
Check if older messages should hydrate near the mounted-window edge.
Check if we should prune messages below the current view.
Check if we should hydrate messages below the current view.
Clear all messages.
Get the range of visible message indices.
Get all stored messages.
Get messages in the visible window.
Update a measured message height, clamped to MIN_HEIGHT_HINT.
The single write path for height_hint; height_hint is intentionally
excluded from update_message's allowlist so every write clamps here.
Invalidate or scale cached height hints after terminal reflow.
Return the best available row estimate for a message.
Estimate rows in [start:end].