@@ -24,6 +24,29 @@ import type { AgentStreamChunk } from './types.js';
2424
2525const APP_NAME = 'Solenoid' ;
2626
27+ /**
28+ * Serialize any error value into a readable string.
29+ * ADK sometimes throws non-standard error objects (e.g. `{}`) that lose
30+ * information with naive stringification.
31+ */
32+ function serializeError ( error : unknown ) : string {
33+ if ( error instanceof Error ) return error . message || error . constructor . name ;
34+ if ( typeof error === 'string' ) return error ;
35+ try {
36+ const str = String ( error ) ;
37+ if ( str !== '[object Object]' ) return str ;
38+ } catch {
39+ // fall through
40+ }
41+ try {
42+ const json = JSON . stringify ( error ) ;
43+ if ( json && json !== '{}' ) return json ;
44+ } catch {
45+ // fall through
46+ }
47+ return 'Unknown error (non-serializable)' ;
48+ }
49+
2750/**
2851 * Debug: Log the agent hierarchy
2952 */
@@ -101,24 +124,34 @@ export async function* runAgent(
101124 return lastErrorMessage ?? lastErrorCode ?? 'empty response' ;
102125 }
103126
104- try {
105- while ( attempt <= MAX_RETRIES && ! gotFinalContent ) {
106- if ( attempt > 0 ) {
107- const delayMs = BASE_DELAY_MS * Math . pow ( 2 , attempt - 1 ) ; // 1s, 2s, 4s, 8s, 16s
108- const reason = retryReason ( ) ;
109- agentLogger . info (
110- `[Runner] Retrying in ${ delayMs } ms (attempt ${ attempt + 1 } /${ MAX_RETRIES + 1 } ): ${ reason } `
111- ) ;
112- yield {
113- type : 'status' ,
114- content : `Retrying (${ attempt } /${ MAX_RETRIES } ): ${ reason } ` ,
115- } ;
116- await new Promise ( ( resolve ) => setTimeout ( resolve , delayMs ) ) ;
117- }
127+ while ( attempt <= MAX_RETRIES && ! gotFinalContent ) {
128+ if ( attempt > 0 ) {
129+ const delayMs = BASE_DELAY_MS * Math . pow ( 2 , attempt - 1 ) ; // 1s, 2s, 4s, 8s, 16s
130+ const reason = retryReason ( ) ;
131+ agentLogger . info (
132+ `[Runner] Retrying in ${ delayMs } ms (attempt ${ attempt + 1 } /${ MAX_RETRIES + 1 } ): ${ reason } `
133+ ) ;
134+ yield {
135+ type : 'status' ,
136+ content : `Retrying (${ attempt } /${ MAX_RETRIES } ): ${ reason } ` ,
137+ } ;
138+ await new Promise ( ( resolve ) => setTimeout ( resolve , delayMs ) ) ;
139+ }
118140
119- const message =
120- attempt === 0 ? userMessage : createUserContent ( 'Please continue with your response.' ) ;
141+ let message : Content ;
142+ if ( attempt === 0 ) {
143+ message = userMessage ;
144+ } else if ( lastErrorMessage && ! lastErrorCode ) {
145+ // Exception occurred (no ADK error code) — give the model error context
146+ message = createUserContent (
147+ `The previous attempt encountered an error: ${ lastErrorMessage } . Please try an alternative approach or a different agent.`
148+ ) ;
149+ } else {
150+ // Empty response or ADK error code — nudge the model to continue
151+ message = createUserContent ( 'Please continue with your response.' ) ;
152+ }
121153
154+ try {
122155 let eventIndex = 0 ;
123156 for await ( const event of runner . runAsync ( {
124157 userId : 'default_user' ,
@@ -132,9 +165,10 @@ export async function* runAgent(
132165 ) ;
133166
134167 if ( event . actions ?. transferToAgent ) {
135- agentLogger . debug (
136- `[Runner] *** TRANSFER DETECTED : ${ event . author } -> ${ event . actions . transferToAgent } ***`
168+ agentLogger . info (
169+ `[Runner] *** TRANSFER: ${ event . author } -> ${ event . actions . transferToAgent } ***`
137170 ) ;
171+ yield { type : 'transfer' , transferTo : event . actions . transferToAgent } ;
138172 }
139173
140174 const partTypes =
@@ -199,26 +233,27 @@ export async function* runAgent(
199233 break ;
200234 }
201235 }
202-
203- if ( ! gotFinalContent ) {
204- attempt ++ ;
205- }
236+ } catch ( error ) {
237+ const serialized = serializeError ( error ) ;
238+ lastErrorMessage = serialized ;
239+ lastErrorCode = undefined ;
240+ agentLogger . error (
241+ { errorType : error ?. constructor ?. name , attempt : attempt + 1 , message : serialized } ,
242+ '[Runner] Exception during runAsync — will retry'
243+ ) ;
206244 }
207245
208246 if ( ! gotFinalContent ) {
209- const reason = retryReason ( ) ;
210- agentLogger . error ( `[Runner] All ${ MAX_RETRIES + 1 } attempts exhausted — ${ reason } ` ) ;
211- yield {
212- type : 'text' ,
213- content : `The model failed after ${ MAX_RETRIES + 1 } attempts: ${ reason } ` ,
214- } ;
215- yield { type : 'done' } ;
247+ attempt ++ ;
216248 }
217- } catch ( error ) {
218- agentLogger . error ( { error } , '[Runner] Error during agent execution' ) ;
249+ }
250+
251+ if ( ! gotFinalContent ) {
252+ const reason = retryReason ( ) ;
253+ agentLogger . error ( `[Runner] All ${ MAX_RETRIES + 1 } attempts exhausted — ${ reason } ` ) ;
219254 yield {
220255 type : 'text' ,
221- content : `Error: ${ error instanceof Error ? error . message : String ( error ) } ` ,
256+ content : `The model failed after ${ MAX_RETRIES + 1 } attempts: ${ reason } ` ,
222257 } ;
223258 yield { type : 'done' } ;
224259 }
0 commit comments