Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions cli/src/chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1473,15 +1473,17 @@ export const Chat = ({
)}

{reviewMode ? (
// Review takes precedence over the session-ended banner: during the
// grace window the agent may still be asking to run tools, and
// those approvals must be reachable for the run to finish.
// Review and ask_user take precedence over the session-ended banner:
// during the grace window the agent may still be asking to run tools
// or asking the user a question, and those approvals/answers must be
// reachable for the run to finish — otherwise the agent hangs
// waiting for input that can never be given.
<ReviewScreen
onSelectOption={handleReviewOptionSelect}
onCustom={handleReviewCustom}
onCancel={handleCloseReviewScreen}
/>
) : isFreebuffSessionOver ? (
) : isFreebuffSessionOver && !askUserState ? (
<SessionEndedBanner
isStreaming={isStreaming || isWaitingForResponse}
/>
Expand Down
14 changes: 10 additions & 4 deletions cli/src/hooks/helpers/send-message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -510,10 +510,16 @@ function handleFreebuffGateError(
switch (kind) {
case 'session_expired':
case 'waiting_room_required':
// Our seat is gone mid-chat. Flip to `ended` instead of auto re-queuing:
// the Chat surface stays mounted so any in-flight agent work can finish
// under the server-side grace period, and the session-ended banner
// prompts the user to press Enter when they're ready to rejoin.
// Our seat is gone mid-chat. Finalize the AI message so its streaming
// indicator stops — otherwise `isComplete` stays false and the message
// keeps rendering a blinking cursor forever, making the user think the
// agent is still working even though the SessionEndedBanner is visible
// and actionable. Also disposes the batched-updater flush interval.
updater.markComplete()
// Flip to `ended` instead of auto re-queuing: the Chat surface stays
// mounted so any in-flight agent work can finish under the server-side
// grace period, and the session-ended banner prompts the user to press
// Enter when they're ready to rejoin.
markFreebuffSessionEnded()
return
case 'waiting_room_queued':
Expand Down
Loading