Skip to content

fix(chat): retry banner, context %, t/s and history window after session switches (#988, #1031, #877, #896) - #1114

Open
agegr wants to merge 4 commits into
mainfrom
fix/chat-client-state
Open

agegr wants to merge 4 commits into
mainfrom
fix/chat-client-state

Conversation

@agegr

@agegr agegr commented Oct 8, 2026

Copy link
Copy Markdown
Owner

Four client-state fixes in useAgentSession / ChatWindow / MessageView, one commit each (kept in one PR because each adds a note to the same part of docs/agents/sessions.md). Each new test fails on main.

#988: retry banner stays up through the retried reply
pi 1.1 emits a successful auto_retry_end only with the retry's first complete assistant message (agent-session.js ~777), so "Retrying (n/max)…" stayed up for the whole retried reply. pi's TUI drops its retry indicator when that run starts. Cleared on the retried run's agent_start. Test: hooks/retry-banner.test.mjs replays pi's event order.

#1031: context % blank after opening an idle-reaped session
Gap A (busy runs never refreshed it) was already fixed by 9182fdf (#1058). Gap B remained: opening the event stream is what resumes a reaped wrapper, so the mount's state read can come back running:false and nothing re-read usage after connected. The connected handler now calls the existing refreshContextUsage() (one extra GET per stream connect). Test: hooks/context-usage.test.mjs.

#877: t/s wrong after switching sessions and back
The tok/s start lived in MessageView; switching back remounts the chat, so the clock restarted and all tokens streamed so far were divided by ~0.5 s (thousands of t/s). New lib/stream-token-rate.ts keeps the start per reply (provider, model, request timestamp), capped at 32 replies. After a reload mid-reply only the tokens that follow are counted. Test: lib/stream-token-rate.test.mjs.

#896 (part 1): scrolling up to load earlier messages sometimes does nothing
The render window holds as many rows as loaded messages, but a turn with thinking renders more rows than messages, so the oldest loaded rows stay hidden behind "Load earlier messages". Since #587 the sentinel only fetched from the server (if (!hasEarlierMessages) return), so with nothing older on the server, reaching the top did nothing. Now it widens the window in that case (reusing getNextVisibleCount()), the observer is renewed when the window grows, and the sentinel is held in state so it is observed whenever it mounts. Test: components/ChatWindow.lazy-load.test.mjs; one #587 source assertion in hooks/useAgentSession.test.mjs updated.

Not covered: the live thinking-block timers still reset on switching back; the extension status bar / widgets / system prompt can miss the same resume race as #1031; #896's loading indicator and minimap (#791).

Checks: tsc --noEmit, eslint on changed files, npm test pass. Not checked in a browser.

Closes #988
Closes #1031
Closes #877
Refs #896

🤖 Generated with Claude Code

agegr and others added 4 commits October 9, 2026 02:03
pi emits a successful auto_retry_end only at the retry's first complete
assistant message, so the banner said "Retrying" through the whole retried
reply. pi's TUI drops its retry indicator when that run starts; clear it on
the run's agent_start the same way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Opening a session's event stream is what resumes a wrapper the idle timeout
reaped, so the mount's state read often finds no runtime and leaves the
context percentage blank. Since #1058 a run fills it in after its first
reply; read it on `connected` too, through the same ordered usage read, so
it shows as soon as the runtime is back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#877)

The start of the tokens-per-second measurement lived in the message view,
which a session switch remounts. Back on a running session, the new view
took its start at the remount and divided every token streamed so far by
half a second, showing thousands of t/s. Keep the start per reply (provider,
model and request timestamp) outside the view; a reply joined with no record
(a reload) times only the tokens that follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The window keeps as many rows as messages are loaded, but a turn's answer
and its thinking render as two rows, so a thinking-heavy session hides its
oldest loaded rows behind "Load earlier messages". Since #587 the sentinel
only fetched from the server, so with no older page there, reaching it did
nothing. Widen the window in that case again, renew the observer when it
grows, and observe the sentinel whenever it mounts, not only when the
history cursor changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant