@@ -87,7 +87,7 @@ export function runHermesAgentAssertionRetry(
8787
8888export function runOpenClawAgentAssertionRetry (
8989 options : AgentAssertionRetryOptions & {
90- reconcile : ( attempt : AgentAssertionAttempt , attemptNumber : number ) => Promise < boolean > ;
90+ recover : ( attempt : AgentAssertionAttempt , attemptNumber : number ) => Promise < boolean > ;
9191 } ,
9292) : Promise < BoundedRetryResult < AgentAssertionAttempt > > {
9393 return runBoundedRetry ( {
@@ -98,20 +98,20 @@ export function runOpenClawAgentAssertionRetry(
9898 delayMs : options . delayMs ,
9999 onEvidence : options . onEvidence ,
100100 run : options . run ,
101+ reconcile : async ( attempt , _error , attemptNumber ) => {
102+ if ( ! attempt ?. recoveryRequired ) return false ;
103+ try {
104+ return await options . recover ( attempt , attemptNumber ) ;
105+ } catch {
106+ return false ;
107+ }
108+ } ,
101109 sleep : options . sleep ,
102110 classify : ( value , error ) => {
103111 if ( error !== undefined ) return { outcome : "failed" , failureClass : "deterministic" } ;
104112 if ( value ?. passed ) return { outcome : "passed" } ;
105113 return { outcome : "failed" , failureClass : value ?. failureClass ?? "deterministic" } ;
106114 } ,
107- reconcile : async ( value , _error , attemptNumber ) => {
108- if ( ! value ) return false ;
109- try {
110- return await options . reconcile ( value , attemptNumber ) ;
111- } catch {
112- return false ;
113- }
114- } ,
115115 } ) ;
116116}
117117
@@ -170,8 +170,19 @@ function compactAgentReply(value: string): string {
170170 return value . replace ( / \s + / gu, "" ) ;
171171}
172172
173+ const AUTHENTICATION_AGENT_FAILURE_RE =
174+ / a u t h e n t i c a t i o n f a i l e d | u n a u t h o r i z e d | H T T P 4 0 1 \b | \b 4 0 1 \b | i n v a l i d (?: c r e d e n t i a l | a p i [ _ - ] ? k e y ) / iu;
175+ const AUTHORIZATION_AGENT_FAILURE_RE = / a u t h o r i z a t i o n f a i l e d | f o r b i d d e n | H T T P 4 0 3 \b | \b 4 0 3 \b / iu;
176+ const POLICY_AGENT_FAILURE_RE =
177+ / S s r F B l o c k e d E r r o r | B l o c k e d h o s t n a m e | d e n i e d b y n e t w o r k p o l i c y | n e t w o r k p o l i c y d e n i e d | p o l i c y (?: u p d a t e | v a l i d a t i o n ) ? f a i l e d / iu;
178+ const MALFORMED_AGENT_FAILURE_RE = / m a l f o r m e d | i n v a l i d r e q u e s t / iu;
179+ const TERMINAL_PROVIDER_VALIDATION_RE =
180+ / i n v a l i d .* ( a p i [ _ - ] ? k e y | c r e d e n t i a l | c o n f i g u r a t i o n | r e q u e s t | j s o n ) | a u t h e n t i c a t i o n f a i l e d | a u t h o r i z a t i o n f a i l e d | u n a u t h o r i z e d | f o r b i d d e n | H T T P 4 0 [ 1 3 ] \b | \b 4 0 [ 1 3 ] \b | d e n i e d b y n e t w o r k p o l i c y | n e t w o r k p o l i c y d e n i e d | p o l i c y .* f a i l e d | r o u t i n g .* f a i l e d | r o u t e .* f a i l e d | p r o x y .* f a i l e d | h o p - b y - h o p | h e a d e r s t r i p p i n g | m a l f o r m e d / iu;
181+ const TRANSIENT_AGENT_FAILURE_RE =
182+ / E C O N N R E F U S E D | E A I _ A G A I N | E C O N N R E S E T | E T I M E D O U T | g a t e w a y u n a v a i l a b l e | n e t w o r k c o n n e c t i o n e r r o r | D N S e r r o r | f e t c h f a i l e d | L L M r e q u e s t t i m e d o u t | F a i l o v e r E r r o r | i n f e r e n c e s e r v i c e u n a v a i l a b l e | r a w E r r o r = 5 0 3 / iu;
183+
173184function isOpenClawPolicyBlock ( output : string ) : boolean {
174- return / S s r F B l o c k e d E r r o r | B l o c k e d h o s t n a m e / i . test ( output ) ;
185+ return POLICY_AGENT_FAILURE_RE . test ( output ) ;
175186}
176187
177188function isOpenClawScopeUpgradePending ( output : string ) : boolean {
@@ -181,8 +192,12 @@ function isOpenClawScopeUpgradePending(output: string): boolean {
181192}
182193
183194function isOpenClawTransientAgentError ( output : string ) : boolean {
184- return / E C O N N R E F U S E D | E A I _ A G A I N | E C O N N R E S E T | E T I M E D O U T | g a t e w a y u n a v a i l a b l e | n e t w o r k c o n n e c t i o n e r r o r | D N S e r r o r | f e t c h f a i l e d | L L M r e q u e s t t i m e d o u t | F a i l o v e r E r r o r | i n f e r e n c e s e r v i c e u n a v a i l a b l e | r a w E r r o r = 5 0 3 / i. test (
185- output ,
195+ return (
196+ ! AUTHENTICATION_AGENT_FAILURE_RE . test ( output ) &&
197+ ! AUTHORIZATION_AGENT_FAILURE_RE . test ( output ) &&
198+ ! POLICY_AGENT_FAILURE_RE . test ( output ) &&
199+ ! MALFORMED_AGENT_FAILURE_RE . test ( output ) &&
200+ TRANSIENT_AGENT_FAILURE_RE . test ( output )
186201 ) ;
187202}
188203
@@ -200,17 +215,20 @@ export function classifyOpenClawAgentAssertion(
200215 if ( isOpenClawPolicyBlock ( result . response ) ) {
201216 return { passed : false , failureClass : "policy-denial" } ;
202217 }
203- if ( / \b 4 0 1 \b | u n a u t h o r i z e d | a u t h e n t i c a t i o n f a i l e d | i n v a l i d a p i k e y / iu . test ( result . response ) ) {
218+ if ( AUTHENTICATION_AGENT_FAILURE_RE . test ( result . response ) ) {
204219 return { passed : false , failureClass : "authentication" } ;
205220 }
206- if ( / \b 4 0 3 \b | f o r b i d d e n / iu . test ( result . response ) ) {
221+ if ( AUTHORIZATION_AGENT_FAILURE_RE . test ( result . response ) ) {
207222 return { passed : false , failureClass : "authorization" } ;
208223 }
224+ if ( MALFORMED_AGENT_FAILURE_RE . test ( result . response ) ) {
225+ return { passed : false , failureClass : "malformed-input" } ;
226+ }
209227 const recoveryRequired = isOpenClawScopeUpgradePending ( result . response ) ;
210228 return {
211229 passed : false ,
212230 failureClass :
213- recoveryRequired || isOpenClawTransientAgentError ( result . response )
231+ recoveryRequired || ( result . exitCode !== 0 && isOpenClawTransientAgentError ( result . response ) )
214232 ? "transient-external"
215233 : "deterministic" ,
216234 recoveryRequired,
@@ -233,6 +251,18 @@ export function classifyHermesAgentAssertion(
233251 if ( result . httpStatus === "403" ) {
234252 return { passed : false , failureClass : "authorization" } ;
235253 }
254+ if ( AUTHENTICATION_AGENT_FAILURE_RE . test ( result . response ) ) {
255+ return { passed : false , failureClass : "authentication" } ;
256+ }
257+ if ( AUTHORIZATION_AGENT_FAILURE_RE . test ( result . response ) ) {
258+ return { passed : false , failureClass : "authorization" } ;
259+ }
260+ if ( POLICY_AGENT_FAILURE_RE . test ( result . response ) ) {
261+ return { passed : false , failureClass : "policy-denial" } ;
262+ }
263+ if ( MALFORMED_AGENT_FAILURE_RE . test ( result . response ) ) {
264+ return { passed : false , failureClass : "malformed-input" } ;
265+ }
236266 return {
237267 passed : false ,
238268 failureClass : isHermesTransientAgentFailure ( result . httpStatus , result . response )
@@ -243,13 +273,19 @@ export function classifyHermesAgentAssertion(
243273
244274/** Recognize transport/provider failures without retrying a successful product response. */
245275export function isHermesTransientAgentFailure ( httpStatus : string , output : string ) : boolean {
246- if ( httpStatus === "200" ) return false ;
247- return (
248- / ^ ( 4 0 8 | 4 2 9 | 5 [ 0 - 9 ] { 2 } ) $ / u. test ( httpStatus ) ||
249- / E C O N N R E F U S E D | E A I _ A G A I N | E C O N N R E S E T | E T I M E D O U T | g a t e w a y u n a v a i l a b l e | n e t w o r k c o n n e c t i o n e r r o r | D N S e r r o r | f e t c h f a i l e d | i n f e r e n c e s e r v i c e u n a v a i l a b l e / iu. test (
250- output ,
251- )
252- ) ;
276+ if (
277+ httpStatus === "200" ||
278+ / ^ ( 4 0 1 | 4 0 3 ) $ / u. test ( httpStatus ) ||
279+ AUTHENTICATION_AGENT_FAILURE_RE . test ( output ) ||
280+ AUTHORIZATION_AGENT_FAILURE_RE . test ( output ) ||
281+ POLICY_AGENT_FAILURE_RE . test ( output ) ||
282+ MALFORMED_AGENT_FAILURE_RE . test ( output )
283+ ) {
284+ return false ;
285+ }
286+ if ( / ^ ( 4 0 8 | 4 2 9 | 5 [ 0 - 9 ] { 2 } ) $ / u. test ( httpStatus ) ) return true ;
287+ const hasNoResponseStatus = httpStatus === "" || httpStatus === "000" ;
288+ return hasNoResponseStatus && TRANSIENT_AGENT_FAILURE_RE . test ( output ) ;
253289}
254290
255291export function classifyPreContractProviderValidationSkip (
@@ -260,11 +296,16 @@ export function classifyPreContractProviderValidationSkip(
260296 / e n d p o i n t v a l i d a t i o n f a i l e d | f a i l e d t o v e r i f y i n f e r e n c e e n d p o i n t | C h a t C o m p l e t i o n s A P I v a l i d a t i o n / i. test (
261297 output ,
262298 ) ;
263- const transientProviderValidationFailure = isTransientProviderValidationFailure ( result ) ;
299+ const terminalProviderValidationFailure = TERMINAL_PROVIDER_VALIDATION_RE . test ( output ) ;
300+ const transientProviderValidationFailure =
301+ ! terminalProviderValidationFailure && isTransientProviderValidationFailure ( result ) ;
264302 const http429ProviderValidationFailure =
265- providerValidation && / H T T P \s * 4 2 9 | \b 4 2 9 \b | r a t e [ - ] ? l i m i t | t o o m a n y r e q u e s t s / i. test ( output ) ;
303+ providerValidation &&
304+ ! terminalProviderValidationFailure &&
305+ / H T T P \s * 4 2 9 | \b 4 2 9 \b | r a t e [ - ] ? l i m i t | t o o m a n y r e q u e s t s / i. test ( output ) ;
266306 const sanitizedEndpointValidationFailure =
267307 providerValidation &&
308+ ! terminalProviderValidationFailure &&
268309 / V a l i d a t i o n d e t a i l s w e r e o m i t t e d t o a v o i d e x p o s i n g c r e d e n t i a l s / i. test ( output ) &&
269310 process . env . GITHUB_ACTIONS === "true" ;
270311
0 commit comments