feat(history): replay a window of a task's history, with older messages on demand - #19
Open
Antisophy wants to merge 2 commits into
Open
feat(history): replay a window of a task's history, with older messages on demand#19Antisophy wants to merge 2 commits into
Antisophy wants to merge 2 commits into
Conversation
Opening a task replays its entire history, one WebSocket frame per event. A long-lived task runs to tens of thousands of events, which takes tens of seconds to stream, builds a DOM to match, and on a phone can stall the client badly enough to drop its WebSocket. Add an optional `history_window` (desktop/mobile, in messages). Absent or zero replays everything, so current behaviour is the default. The client sends only its device class and the server picks the number, because the server is the only side that always knows it: `tasks_list` is sent before `server_status` and is what triggers the first history request, so a client deciding for itself would ask for everything whenever the two messages arrive in separate ticks. Each requested message carries a budget of four events, since page weight tracks events rather than bubbles and a single turn can drag dozens of tool calls along. Whichever budget runs out first ends the window, always cutting at a non-pending user message so the client's reducers never see a split turn. Sequence numbers stay true history indices, so anything anchored to them is unaffected. `content-visibility` on messages now applies only to unwindowed replays: it reserves a guessed 100px per offscreen message, so heights change as they scroll into view and the scroll region grows under a dragged scrollbar handle. That trade only pays when the whole transcript is in the DOM.
This was referenced Aug 21, 2026
A windowed replay needs a way back into the rest of the task. The existing request takes a window size, but a larger window resends everything the client already holds and rebuilds its list; a separate request for the range before the current start leaves what is on screen untouched. Add request_history_before: the client names the seq it currently starts at, the server replays only the messages before it, framed by task_history_prepend_start/end. The client reduces that batch into a state of its own and merges it in front of what it holds. Staged message ids start well above the live counter, since ids come from a per-state counter and would otherwise collide, handing preact two different messages under one key. The list is column-reverse, so scrollTop measures distance from the bottom, which is exactly the quantity to hold across the batch: the reader stays where they were and the older messages arrive above them. Scroll anchoring is switched off for the duration, since it latches onto the load-more row and drags the viewport to the new top.
Antisophy
force-pushed
the
feat/history-window
branch
from
August 21, 2026 22:51
df8d812 to
bafaa73
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Opening a task replays its entire history, one WebSocket frame per event. That is fine for a young task and increasingly not fine for an old one: a task here reached ~26,000 events, which takes tens of seconds to stream, builds a DOM of roughly 21,000 nodes, and on a phone can stall the client long enough to drop its WebSocket (which disables the composer, since sending needs a live connection).
Two commits, separable and reviewable on their own:
1. Replay only a window (
feat(history): replay only a window of a task's history)history_windowwithdesktopandmobilekeys, in messages. Absent or zero replays everything, so current behaviour is the default and nothing changes for anyone who does not opt in.tasks_listis sent beforeserver_statusand is what triggers the first history request, so a client deciding for itself asks for everything whenever those two messages arrive in separate ticks. That failure is invisible on a fast local connection and reliable on a slow one.content-visibilityon messages now applies only to unwindowed replays. It reserves a guessed 100px per offscreen message, so heights change as they scroll into view and the scroll region grows under a dragged scrollbar handle; that trade only pays when the whole transcript is in the DOM.2. Load older history on demand (
feat(web): load older history on demand)request_history_before: the client names the seq it currently starts at, and the server replays only the messages before it, framed bytask_history_prepend_start/_end.column-reverse, soscrollTopmeasures distance from the bottom, which is exactly the quantity held constant across the batch: the reader stays put and older messages arrive above them.Measured end to end on that task with a 30-message window: 127 of 26,039 events replayed, ~1,000 DOM nodes, and a load-more click costing 131 events instead of the whole history.
One of five independent changes for using cydo on a phone: #15 (composer spacing), #16 (the page dragging sideways), #17 (hiding the composer while reading history), #18 (attaching images with a file picker). They touch different code and can merge in any order.