@@ -341,6 +341,7 @@ export class ReplayingCapiProxy extends CapturingHttpProxy {
341341 state . testInfo ,
342342 state . workDir ,
343343 state . toolResultNormalizers ,
344+ state . storedData ,
344345 ) ;
345346 return ;
346347 }
@@ -380,30 +381,54 @@ async function exitWithNoMatchingRequestError(
380381 testInfo : { file : string ; line ?: number } | undefined ,
381382 workDir : string ,
382383 toolResultNormalizers : ToolResultNormalizer [ ] ,
384+ storedData ?: NormalizedData ,
383385) {
384386 const parts : string [ ] = [ ] ;
385387 if ( testInfo ?. file ) parts . push ( `file=${ testInfo . file } ` ) ;
386388 if ( typeof testInfo ?. line === "number" ) parts . push ( `line=${ testInfo . line } ` ) ;
387389 const header = parts . length ? ` ${ parts . join ( "," ) } ` : "" ;
388390
389- let finalMessageInfo : string ;
391+ let diagnostics = "" ;
390392 try {
391393 const normalized = await parseAndNormalizeRequest (
392394 options . body ,
393395 workDir ,
394396 toolResultNormalizers ,
395397 ) ;
396- const normalizedMessages = normalized . conversations [ 0 ] ?. messages ?? [ ] ;
397- finalMessageInfo = JSON . stringify (
398- normalizedMessages [ normalizedMessages . length - 1 ] ,
399- ) ;
400- } catch {
401- finalMessageInfo = `(unable to parse request body: ${ options . body ?. slice ( 0 , 200 ) ?? "empty" } )` ;
398+ const requestMessages = normalized . conversations [ 0 ] ?. messages ?? [ ] ;
399+
400+ diagnostics += `Request has ${ requestMessages . length } messages.\n` ;
401+
402+ if ( storedData ) {
403+ for ( let c = 0 ; c < storedData . conversations . length ; c ++ ) {
404+ const saved = storedData . conversations [ c ] . messages ;
405+ diagnostics += `Conversation ${ c } has ${ saved . length } messages. ` ;
406+ if ( requestMessages . length >= saved . length ) {
407+ diagnostics += `Skipped: request (${ requestMessages . length } ) >= saved (${ saved . length } ).\n` ;
408+ continue ;
409+ }
410+ let mismatchAt = - 1 ;
411+ for ( let i = 0 ; i < requestMessages . length ; i ++ ) {
412+ const reqMsg = JSON . stringify ( requestMessages [ i ] ) ;
413+ const savedMsg = JSON . stringify ( saved [ i ] ) ;
414+ if ( reqMsg !== savedMsg ) {
415+ mismatchAt = i ;
416+ diagnostics += `Mismatch at message ${ i } :\n request: ${ reqMsg . slice ( 0 , 200 ) } \n saved: ${ savedMsg . slice ( 0 , 200 ) } \n` ;
417+ break ;
418+ }
419+ }
420+ if ( mismatchAt === - 1 ) {
421+ const nextRole = saved [ requestMessages . length ] ?. role ;
422+ diagnostics += `Prefix matched but next message role is "${ nextRole } " (need "assistant").\n` ;
423+ }
424+ }
425+ }
426+ } catch ( e ) {
427+ diagnostics = `(unable to parse request: ${ e } )` ;
402428 }
403429
404430 const errorMessage =
405- `No cached response found for ${ options . requestOptions . method } ${ options . requestOptions . path } . ` +
406- `Final message: ${ finalMessageInfo } ` ;
431+ `No cached response found for ${ options . requestOptions . method } ${ options . requestOptions . path } .\n${ diagnostics } ` ;
407432 process . stderr . write ( `::error${ header } ::${ errorMessage } \n` ) ;
408433 options . onError ( new Error ( errorMessage ) ) ;
409434}
0 commit comments