@@ -132,31 +132,52 @@ export function hasInterruptedStream(
132132 * In that window, AIView previously hid RetryBarrier because canInterrupt flipped to true,
133133 * causing the banner to flicker on every retry attempt.
134134 *
135- * This helper keeps RetryBarrier visible while the new stream is active but has not produced
136- * any visible assistant content yet.
135+ * Note: A new stream-start doesn't always immediately produce a new assistant DisplayedMessage
136+ * (we don't render empty assistant messages). During TTFT gaps, the last displayed message can
137+ * still be the prior interruption.
137138 */
138139export function shouldKeepRetryBarrierVisibleDuringRetry (
139140 messages : DisplayedMessage [ ] ,
140141 retryAttempt : number
141142) : boolean {
142143 if ( retryAttempt <= 0 ) return false ;
143- if ( messages . length < 2 ) return false ;
144+ if ( messages . length === 0 ) return false ;
144145
145146 const lastMessage = messages [ messages . length - 1 ] ;
146- if ( lastMessage . type !== "assistant" ) return false ;
147- if ( ! lastMessage . isStreaming ) return false ;
148- if ( lastMessage . content . trim ( ) . length > 0 ) return false ;
149147
150- const previousMessage = messages [ messages . length - 2 ] ;
148+ // If we're still showing the interruption message while a new retry stream is active,
149+ // keep the banner visible so it doesn't flicker away on stream-start.
150+ if ( lastMessage . type === "stream-error" ) {
151+ return true ;
152+ }
151153
152- // Only keep the banner sticky when this stream is a retry/resume attempt.
153- // If the previous message is a fresh user message, we want normal streaming UX.
154- return (
155- previousMessage . type === "stream-error" ||
156- ( previousMessage . type === "assistant" && previousMessage . isPartial === true ) ||
157- ( previousMessage . type === "tool" && previousMessage . isPartial === true ) ||
158- ( previousMessage . type === "reasoning" && previousMessage . isPartial === true )
159- ) ;
154+ if (
155+ ( lastMessage . type === "assistant" ||
156+ lastMessage . type === "tool" ||
157+ lastMessage . type === "reasoning" ) &&
158+ lastMessage . isPartial === true
159+ ) {
160+ return true ;
161+ }
162+
163+ // If we do have a streaming assistant block but it hasn't produced any visible content yet,
164+ // keep the banner sticky until the first non-empty delta arrives.
165+ if (
166+ lastMessage . type === "assistant" &&
167+ lastMessage . isStreaming &&
168+ lastMessage . content . trim ( ) . length === 0 &&
169+ messages . length >= 2
170+ ) {
171+ const previousMessage = messages [ messages . length - 2 ] ;
172+ return (
173+ previousMessage . type === "stream-error" ||
174+ ( previousMessage . type === "assistant" && previousMessage . isPartial === true ) ||
175+ ( previousMessage . type === "tool" && previousMessage . isPartial === true ) ||
176+ ( previousMessage . type === "reasoning" && previousMessage . isPartial === true )
177+ ) ;
178+ }
179+
180+ return false ;
160181}
161182
162183/**
0 commit comments