1- /** Logger interface for SSE events */
1+ /** Logger interface for SSE events and error logging */
22export interface SSELogger {
33 logSSEEvent ( rawLine : string ) : void ;
4+ /** Optional method to log errors and debug info */
5+ log ?( level : 'error' | 'warn' | 'system' , message : string ) : void ;
46}
57
68/**
@@ -167,23 +169,48 @@ export async function* invokeAgentStreaming(
167169 return ;
168170 } catch ( err ) {
169171 lastError = err instanceof Error ? err : new Error ( String ( err ) ) ;
170- if ( lastError . message . includes ( 'fetch' ) || lastError . message . includes ( 'ECONNREFUSED' ) ) {
171- await sleep ( baseDelay * Math . pow ( 2 , attempt ) ) ;
172+ const isConnectionError = lastError . message . includes ( 'fetch' ) || lastError . message . includes ( 'ECONNREFUSED' ) ;
173+
174+ if ( isConnectionError ) {
175+ const delay = baseDelay * Math . pow ( 2 , attempt ) ;
176+ logger ?. log ?.(
177+ 'warn' ,
178+ `Connection failed (attempt ${ attempt + 1 } /${ maxRetries } ): ${ lastError . message } . Retrying in ${ delay } ms...`
179+ ) ;
180+ await sleep ( delay ) ;
172181 continue ;
173182 }
183+
184+ // Log non-connection errors with full stack trace before throwing
185+ logger ?. log ?.( 'error' , `Request failed: ${ lastError . stack ?? lastError . message } ` ) ;
174186 throw lastError ;
175187 }
176188 }
177189
178- throw lastError ?? new Error ( 'Failed to connect to dev server after retries' ) ;
190+ // Log final failure after all retries exhausted with full details
191+ const finalError = lastError ?? new Error ( 'Failed to connect to dev server after retries' ) ;
192+ logger ?. log ?.( 'error' , `Failed to connect after ${ maxRetries } attempts: ${ finalError . stack ?? finalError . message } ` ) ;
193+ throw finalError ;
194+ }
195+
196+ export interface InvokeOptions {
197+ port : number ;
198+ message : string ;
199+ /** Optional logger for error logging */
200+ logger ?: SSELogger ;
179201}
180202
181203/**
182204 * Invokes an agent running on the local dev server.
183205 * Handles both JSON and streaming text responses.
184206 * Includes retry logic for server startup race conditions.
185207 */
186- export async function invokeAgent ( port : number , message : string ) : Promise < string > {
208+ export async function invokeAgent ( portOrOptions : number | InvokeOptions , message ?: string ) : Promise < string > {
209+ // Support both old signature (port, message) and new signature (options)
210+ const options : InvokeOptions =
211+ typeof portOrOptions === 'number' ? { port : portOrOptions , message : message ! } : portOrOptions ;
212+ const { port, message : msg , logger } = options ;
213+
187214 const maxRetries = 5 ;
188215 const baseDelay = 500 ; // ms
189216 let lastError : Error | null = null ;
@@ -193,7 +220,7 @@ export async function invokeAgent(port: number, message: string): Promise<string
193220 const res = await fetch ( `http://localhost:${ port } /invocations` , {
194221 method : 'POST' ,
195222 headers : { 'Content-Type' : 'application/json' } ,
196- body : JSON . stringify ( { prompt : message } ) ,
223+ body : JSON . stringify ( { prompt : msg } ) ,
197224 } ) ;
198225
199226 const text = await res . text ( ) ;
@@ -210,16 +237,26 @@ export async function invokeAgent(port: number, message: string): Promise<string
210237 return extractResult ( text ) ;
211238 } catch ( err ) {
212239 lastError = err instanceof Error ? err : new Error ( String ( err ) ) ;
213- // Only retry on connection errors (server not ready)
214- if ( lastError . message . includes ( 'fetch' ) || lastError . message . includes ( 'ECONNREFUSED' ) ) {
240+ const isConnectionError = lastError . message . includes ( 'fetch' ) || lastError . message . includes ( 'ECONNREFUSED' ) ;
241+
242+ if ( isConnectionError ) {
215243 const delay = baseDelay * Math . pow ( 2 , attempt ) ;
244+ logger ?. log ?.(
245+ 'warn' ,
246+ `Connection failed (attempt ${ attempt + 1 } /${ maxRetries } ): ${ lastError . message } . Retrying in ${ delay } ms...`
247+ ) ;
216248 await sleep ( delay ) ;
217249 continue ;
218250 }
219- // For other errors, throw immediately
251+
252+ // Log non-connection errors with full stack trace before throwing
253+ logger ?. log ?.( 'error' , `Request failed: ${ lastError . stack ?? lastError . message } ` ) ;
220254 throw lastError ;
221255 }
222256 }
223257
224- throw lastError ?? new Error ( 'Failed to connect to dev server after retries' ) ;
258+ // Log final failure after all retries exhausted with full details
259+ const finalError = lastError ?? new Error ( 'Failed to connect to dev server after retries' ) ;
260+ logger ?. log ?.( 'error' , `Failed to connect after ${ maxRetries } attempts: ${ finalError . stack ?? finalError . message } ` ) ;
261+ throw finalError ;
225262}
0 commit comments