Skip to content

Commit 43d1559

Browse files
jahoomaclaude
andcommitted
Fix freebuff grace-period hang where UI looks stuck streaming
Two separate hang vectors after a freebuff session ends mid-run: 1. `handleFreebuffGateError` for session_expired/waiting_room_required only flipped session state — it never finalized the in-flight AI message. Result: `isComplete` stayed false, the streaming cursor kept rendering, and the batched-updater flush interval leaked. Users saw an apparently still-streaming message next to the rejoin banner, assumed the agent was working, and waited indefinitely. Now calls `updater.markComplete()` so the message visibly finalizes. 2. If the agent was mid-`ask_user` when the session ended, the SessionEndedBanner replaced the ChatInputBar and hid the answer form, leaving the agent waiting on input that could never arrive. Chat.tsx now lets ask_user take precedence over the banner (same pattern as review mode) so in-flight runs can still finish during grace. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 21d5dd3 commit 43d1559

2 files changed

Lines changed: 16 additions & 8 deletions

File tree

cli/src/chat.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1473,15 +1473,17 @@ export const Chat = ({
14731473
)}
14741474

14751475
{reviewMode ? (
1476-
// Review takes precedence over the session-ended banner: during the
1477-
// grace window the agent may still be asking to run tools, and
1478-
// those approvals must be reachable for the run to finish.
1476+
// Review and ask_user take precedence over the session-ended banner:
1477+
// during the grace window the agent may still be asking to run tools
1478+
// or asking the user a question, and those approvals/answers must be
1479+
// reachable for the run to finish — otherwise the agent hangs
1480+
// waiting for input that can never be given.
14791481
<ReviewScreen
14801482
onSelectOption={handleReviewOptionSelect}
14811483
onCustom={handleReviewCustom}
14821484
onCancel={handleCloseReviewScreen}
14831485
/>
1484-
) : isFreebuffSessionOver ? (
1486+
) : isFreebuffSessionOver && !askUserState ? (
14851487
<SessionEndedBanner
14861488
isStreaming={isStreaming || isWaitingForResponse}
14871489
/>

cli/src/hooks/helpers/send-message.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -510,10 +510,16 @@ function handleFreebuffGateError(
510510
switch (kind) {
511511
case 'session_expired':
512512
case 'waiting_room_required':
513-
// Our seat is gone mid-chat. Flip to `ended` instead of auto re-queuing:
514-
// the Chat surface stays mounted so any in-flight agent work can finish
515-
// under the server-side grace period, and the session-ended banner
516-
// prompts the user to press Enter when they're ready to rejoin.
513+
// Our seat is gone mid-chat. Finalize the AI message so its streaming
514+
// indicator stops — otherwise `isComplete` stays false and the message
515+
// keeps rendering a blinking cursor forever, making the user think the
516+
// agent is still working even though the SessionEndedBanner is visible
517+
// and actionable. Also disposes the batched-updater flush interval.
518+
updater.markComplete()
519+
// Flip to `ended` instead of auto re-queuing: the Chat surface stays
520+
// mounted so any in-flight agent work can finish under the server-side
521+
// grace period, and the session-ended banner prompts the user to press
522+
// Enter when they're ready to rejoin.
517523
markFreebuffSessionEnded()
518524
return
519525
case 'waiting_room_queued':

0 commit comments

Comments
 (0)