@@ -51,10 +51,14 @@ export default function ContainerSession({
5151
5252 const lineIdRef = useRef ( 0 )
5353 const sseOffsetRef = useRef ( 0 )
54+ const reconnectCountRef = useRef ( 0 )
5455 const eventSourceRef = useRef < EventSource | null > ( null )
5556 const mountedRef = useRef ( true )
5657 const inputRef = useRef < HTMLInputElement > ( null )
5758
59+ const MAX_RECONNECTS = 5
60+ const RECONNECT_BASE_DELAY_MS = 2000
61+
5862 const appendLine = useCallback ( ( text : string , kind ?: TerminalLine [ 'kind' ] ) => {
5963 if ( ! mountedRef . current ) return
6064 lineIdRef . current += 1
@@ -73,6 +77,7 @@ export default function ContainerSession({
7377 const abortController = new AbortController ( )
7478 lineIdRef . current = 0
7579 sseOffsetRef . current = 0
80+ reconnectCountRef . current = 0
7681 setLines ( [ ] )
7782
7883 async function init ( ) {
@@ -111,6 +116,19 @@ export default function ContainerSession({
111116 }
112117
113118 async function pollSessionStatus ( sessionId : string ) {
119+ reconnectCountRef . current += 1
120+ if ( reconnectCountRef . current > MAX_RECONNECTS ) {
121+ setStatus ( 'failed' )
122+ setError ( 'Lost connection to session' )
123+ appendLine ( '[error] Lost connection after multiple retries.' )
124+ return
125+ }
126+
127+ // Exponential backoff: 2s, 4s, 8s, 16s, 32s
128+ const delay = RECONNECT_BASE_DELAY_MS * Math . pow ( 2 , reconnectCountRef . current - 1 )
129+ await new Promise ( ( r ) => setTimeout ( r , delay ) )
130+ if ( ! mountedRef . current ) return
131+
114132 try {
115133 const fresh = await getContainerSession ( sessionId )
116134 if ( ! mountedRef . current ) return
@@ -121,7 +139,6 @@ export default function ContainerSession({
121139 setError ( 'Container failed' )
122140 appendLine ( '[error] Container failed.' )
123141 } else if ( fresh . status === 'running' ) {
124- appendLine ( '[reconnecting] Stream dropped, container still running...' )
125142 connectStream ( sessionId )
126143 }
127144 } catch {
@@ -146,6 +163,8 @@ export default function ContainerSession({
146163 source . addEventListener ( 'open' , ( ) => {
147164 if ( ! mountedRef . current ) return
148165 setStatus ( 'running' )
166+ // Connection succeeded — reset reconnect counter
167+ reconnectCountRef . current = 0
149168 } )
150169
151170 // Default message event — parse Claude Code stream-json
0 commit comments