@@ -380,6 +380,59 @@ suite('ByokLmProxyService', () => {
380380 } ) ;
381381 } ) ;
382382
383+ test ( 'consumes an explicit continuation only after a successful bridge result' , async ( ) => {
384+ const captured : IByokLmChatRequest [ ] = [ ] ;
385+ const output = { type : 'function_call_output' , call_id : 'call_1' , output : 'done' } ;
386+ const replayedInput = [
387+ { type : 'function_call' , call_id : 'call_1' , name : 'tool' , arguments : '{}' } ,
388+ output ,
389+ ] ;
390+
391+ await withProxy (
392+ async request => {
393+ captured . push ( request ) ;
394+ if ( captured . length === 1 ) {
395+ return {
396+ responseId : 'resp_1' ,
397+ output : [ { type : 'function_call' , callId : 'call_1' , name : 'tool' , argumentsJson : '{}' } ] ,
398+ } ;
399+ }
400+ return captured . length === 2
401+ ? { output : [ ] , error : 'retryable failure' }
402+ : { output : [ { type : 'message' , content : [ { type : 'text' , text : 'done' } ] } ] } ;
403+ } ,
404+ async handle => {
405+ const post = ( input : unknown , previousResponseId ?: string ) => fetch ( responsesUrl ( handle , 'acme' ) , {
406+ method : 'POST' ,
407+ headers : authHeaders ( handle ) ,
408+ body : JSON . stringify ( { model : 'm' , input, ...( previousResponseId ? { previous_response_id : previousResponseId } : { } ) } ) ,
409+ } ) ;
410+
411+ for ( const [ input , previousResponseId , expectedStatus ] of [
412+ [ [ ] , undefined , 200 ] ,
413+ [ [ output ] , 'resp_1' , 502 ] ,
414+ [ [ output ] , 'resp_1' , 200 ] ,
415+ [ replayedInput , undefined , 200 ] ,
416+ ] as const ) {
417+ const response = await post ( input , previousResponseId ) ;
418+ assert . strictEqual ( response . status , expectedStatus ) ;
419+ await response . text ( ) ;
420+ }
421+ } ,
422+ ) ;
423+
424+ assert . deepStrictEqual ( {
425+ previousResponseIds : captured . map ( request => request . previousResponseId ) ,
426+ finalInput : captured [ 3 ] ?. input ,
427+ } , {
428+ previousResponseIds : [ undefined , 'resp_1' , 'resp_1' , undefined ] ,
429+ finalInput : [
430+ { type : 'function_call' , callId : 'call_1' , name : 'tool' , argumentsJson : '{}' } ,
431+ { type : 'function_call_output' , callId : 'call_1' , output : 'done' } ,
432+ ] ,
433+ } ) ;
434+ } ) ;
435+
383436 test ( 'preserves full stateless replay when no resumable provider state is reported' , async ( ) => {
384437 const captured : IByokLmChatRequest [ ] = [ ] ;
385438 const initialInput = [ { type : 'message' , role : 'user' , content : [ { type : 'input_text' , text : 'Use the tool.' } ] } ] ;
@@ -644,16 +697,18 @@ suite('ByokLmProxyService', () => {
644697 ) ;
645698 } ) ;
646699
647- test ( 'rejects a malformed JSON body with 400' , async ( ) => {
700+ test ( 'rejects a malformed or null JSON body with 400' , async ( ) => {
648701 await withProxy (
649702 async ( ) => ( { output : [ ] } ) ,
650703 async ( handle ) => {
651- const response = await fetch ( responsesUrl ( handle , 'acme' ) , {
652- method : 'POST' ,
653- headers : authHeaders ( handle ) ,
654- body : 'not json' ,
655- } ) ;
656- assert . strictEqual ( response . status , 400 ) ;
704+ for ( const body of [ 'not json' , 'null' ] ) {
705+ const response = await fetch ( responsesUrl ( handle , 'acme' ) , {
706+ method : 'POST' ,
707+ headers : authHeaders ( handle ) ,
708+ body,
709+ } ) ;
710+ assert . strictEqual ( response . status , 400 ) ;
711+ }
657712 } ,
658713 ) ;
659714 } ) ;
0 commit comments