@@ -186,23 +186,26 @@ export class SessionLifecycle {
186186 * session already has a claudeSessionId (process previously initialized).
187187 * Times out after `timeoutMs` (default 30s) to avoid hanging indefinitely.
188188 */
189- waitForReady ( sessionId : string , timeoutMs = 30_000 ) : Promise < void > {
189+ waitForReady ( sessionId : string , timeoutMs = 30_000 ) : Promise < boolean > {
190190 const session = this . deps . getSession ( sessionId )
191- if ( ! session ?. claudeProcess ) return Promise . resolve ( )
191+ if ( ! session ?. claudeProcess ) return Promise . resolve ( false )
192192 // If the process is already fully initialized, resolve immediately.
193193 // Uses isReady() which accounts for provider differences: Claude is ready
194194 // as soon as alive (stdin buffered), OpenCode needs alive + opencodeSessionId.
195- if ( session . claudeProcess . isReady ( ) ) return Promise . resolve ( )
195+ if ( session . claudeProcess . isReady ( ) ) return Promise . resolve ( true )
196196
197- return new Promise < void > ( ( resolve ) => {
198- const done = ( ) => { clearTimeout ( timer ) ; resolve ( ) }
197+ return new Promise < boolean > ( ( resolve ) => {
198+ const onReady = ( ) => { clearTimeout ( timer ) ; cp . removeListener ( 'exit' , onExit ) ; resolve ( true ) }
199+ const onExit = ( ) => { clearTimeout ( timer ) ; cp . removeListener ( 'system_init' , onReady ) ; resolve ( false ) }
200+ const cp = session . claudeProcess !
199201 const timer = setTimeout ( ( ) => {
200202 console . warn ( `[waitForReady] Timed out waiting for system_init on ${ sessionId } after ${ timeoutMs } ms` )
201- session . claudeProcess ?. removeListener ( 'exit' , done )
202- resolve ( )
203+ cp . removeListener ( 'system_init' , onReady )
204+ cp . removeListener ( 'exit' , onExit )
205+ resolve ( false )
203206 } , timeoutMs )
204- session . claudeProcess ! . once ( 'system_init' , done )
205- session . claudeProcess ! . once ( 'exit' , done ) // fail-fast if process dies during init
207+ cp . once ( 'system_init' , onReady )
208+ cp . once ( 'exit' , onExit ) // fail-fast if process dies during init
206209 } )
207210 }
208211
@@ -407,13 +410,26 @@ export class SessionLifecycle {
407410 // Fallback: if claudeSessionId was already null (fresh session that
408411 // crashed before system_init), inject a context summary so the new
409412 // session has some awareness of prior conversation.
410- if ( ! session . claudeSessionId && session . claudeProcess && session . outputHistory . length > 0 ) {
413+ if ( ! session . claudeSessionId && session . claudeProcess ) {
414+ const pendingInput = session . _lastUserInput
415+ const inputAge = session . _lastUserInputAt ? Date . now ( ) - session . _lastUserInputAt : Infinity
416+ const hasPendingInput = pendingInput && inputAge < 60_000
417+
411418 session . claudeProcess . once ( 'system_init' , ( ) => {
412- const context = this . deps . buildSessionContext ( session )
413- if ( context ) {
414- session . claudeProcess ?. sendMessage (
415- context + '\n\n[Session resumed after process restart. Continue where you left off. If you were in the middle of a task, resume it.]' ,
416- )
419+ if ( session . outputHistory . length > 0 ) {
420+ const context = this . deps . buildSessionContext ( session )
421+ if ( context ) {
422+ const msg = hasPendingInput
423+ ? context + '\n\n' + pendingInput
424+ : context + '\n\n[Session resumed after process restart. Continue where you left off. If you were in the middle of a task, resume it.]'
425+ session . claudeProcess ?. sendMessage ( msg )
426+ return
427+ }
428+ }
429+ // No output history but pending input (brand new session that
430+ // crashed before responding) — re-send the user's message.
431+ if ( hasPendingInput ) {
432+ session . claudeProcess ?. sendMessage ( pendingInput )
417433 }
418434 } )
419435 }
0 commit comments