@@ -13,6 +13,10 @@ state.copilotToken = "test-token"
1313state . vsCodeVersion = "1.0.0"
1414state . accountType = "individual"
1515
16+ const isolatedHome = path . join ( os . tmpdir ( ) , "copilot-api-test-home" )
17+ await mkdir ( isolatedHome , { recursive : true } )
18+ process . env . HOME = isolatedHome
19+
1620const createMockResponse = ( jsonBody : unknown , ok : boolean = true ) => ( {
1721 ok,
1822 status : ok ? 200 : 400 ,
@@ -41,11 +45,9 @@ const fetchMock = mock(
4145afterEach ( ( ) => {
4246 fetchMock . mockClear ( )
4347 delete process . env . COPILOT_REASONING_EFFORT
44- process . env . HOME = originalHome
48+ process . env . HOME = isolatedHome
4549} )
4650
47- const originalHome = process . env . HOME
48-
4951test ( "sets X-Initiator to agent if tool/assistant present" , async ( ) => {
5052 const payload : ChatCompletionsPayload = {
5153 messages : [
@@ -96,6 +98,45 @@ test("uses max_completion_tokens for GPT-5 family models", async () => {
9698 expect ( body . max_tokens ) . toBeUndefined ( )
9799} )
98100
101+ test ( "uses max_completion_tokens for gpt-5.5 with explicit reasoning effort" , async ( ) => {
102+ const payload : ChatCompletionsPayload = {
103+ messages : [ { role : "user" , content : "hi" } ] ,
104+ model : "gpt-5.5" ,
105+ max_tokens : 384 ,
106+ reasoning_effort : "high" ,
107+ }
108+
109+ await createChatCompletions ( payload )
110+
111+ expect ( fetchMock ) . toHaveBeenCalled ( )
112+ const body = JSON . parse (
113+ ( fetchMock . mock . calls [ 0 ] [ 1 ] as { body : string } ) . body ,
114+ ) as Record < string , unknown >
115+
116+ expect ( body . max_completion_tokens ) . toBe ( 384 )
117+ expect ( body . max_tokens ) . toBeUndefined ( )
118+ expect ( body . reasoning_effort ) . toBe ( "high" )
119+ } )
120+
121+ test ( "passes through reasoning_effort none for gpt-5.5" , async ( ) => {
122+ const payload : ChatCompletionsPayload = {
123+ messages : [ { role : "user" , content : "hi" } ] ,
124+ model : "gpt-5.5" ,
125+ max_tokens : 128 ,
126+ reasoning_effort : "none" ,
127+ }
128+
129+ await createChatCompletions ( payload )
130+
131+ expect ( fetchMock ) . toHaveBeenCalled ( )
132+ const body = JSON . parse (
133+ ( fetchMock . mock . calls [ 0 ] [ 1 ] as { body : string } ) . body ,
134+ ) as Record < string , unknown >
135+
136+ expect ( body . max_completion_tokens ) . toBe ( 128 )
137+ expect ( body . reasoning_effort ) . toBe ( "none" )
138+ } )
139+
99140test ( "uses max_completion_tokens for gpt-5.3-codex" , async ( ) => {
100141 fetchMock . mockImplementationOnce (
101142 ( _url : string , opts : { headers : Record < string , string > ; body ?: string } ) =>
@@ -271,6 +312,42 @@ test("keeps max_tokens for non GPT-5 models", async () => {
271312 expect ( body . max_completion_tokens ) . toBeUndefined ( )
272313} )
273314
315+ test ( "keeps max_tokens and omits reasoning_effort for gemini-3.1-pro-preview" , async ( ) => {
316+ const payload : ChatCompletionsPayload = {
317+ messages : [ { role : "user" , content : "hi" } ] ,
318+ model : "gemini-3.1-pro-preview" ,
319+ max_tokens : 256 ,
320+ }
321+
322+ await createChatCompletions ( payload )
323+
324+ const body = JSON . parse (
325+ ( fetchMock . mock . calls [ 0 ] [ 1 ] as { body : string } ) . body ,
326+ ) as Record < string , unknown >
327+
328+ expect ( body . max_tokens ) . toBe ( 256 )
329+ expect ( body . max_completion_tokens ) . toBeUndefined ( )
330+ expect ( body . reasoning_effort ) . toBeUndefined ( )
331+ } )
332+
333+ test ( "keeps max_tokens and omits reasoning_effort for gemini-3-flash-preview" , async ( ) => {
334+ const payload : ChatCompletionsPayload = {
335+ messages : [ { role : "user" , content : "hi" } ] ,
336+ model : "gemini-3-flash-preview" ,
337+ max_tokens : 256 ,
338+ }
339+
340+ await createChatCompletions ( payload )
341+
342+ const body = JSON . parse (
343+ ( fetchMock . mock . calls [ 0 ] [ 1 ] as { body : string } ) . body ,
344+ ) as Record < string , unknown >
345+
346+ expect ( body . max_tokens ) . toBe ( 256 )
347+ expect ( body . max_completion_tokens ) . toBeUndefined ( )
348+ expect ( body . reasoning_effort ) . toBeUndefined ( )
349+ } )
350+
274351test ( "passes through explicit reasoning_effort for GPT-5 family models" , async ( ) => {
275352 const payload : ChatCompletionsPayload = {
276353 messages : [ { role : "user" , content : "hi" } ] ,
@@ -559,6 +636,42 @@ test("uses COPILOT_REASONING_EFFORT from Claude settings.json", async () => {
559636 }
560637} )
561638
639+ test ( "uses COPILOT_REASONING_EFFORT from Claude settings.json for gpt-5.5" , async ( ) => {
640+ const tempHome = await mkdtemp ( path . join ( os . tmpdir ( ) , "claude-settings-" ) )
641+ const claudeDirectory = path . join ( tempHome , ".claude" )
642+
643+ try {
644+ await mkdir ( claudeDirectory , { recursive : true } )
645+ await writeFile (
646+ path . join ( claudeDirectory , "settings.json" ) ,
647+ JSON . stringify ( {
648+ env : {
649+ COPILOT_REASONING_EFFORT : "none" ,
650+ } ,
651+ } ) ,
652+ )
653+
654+ process . env . HOME = tempHome
655+
656+ const payload : ChatCompletionsPayload = {
657+ messages : [ { role : "user" , content : "hi" } ] ,
658+ model : "gpt-5.5" ,
659+ max_tokens : 128 ,
660+ }
661+
662+ await createChatCompletions ( payload )
663+
664+ const body = JSON . parse (
665+ ( fetchMock . mock . calls [ 0 ] [ 1 ] as { body : string } ) . body ,
666+ ) as Record < string , unknown >
667+
668+ expect ( body . max_completion_tokens ) . toBe ( 128 )
669+ expect ( body . reasoning_effort ) . toBe ( "none" )
670+ } finally {
671+ await rm ( tempHome , { recursive : true , force : true } )
672+ }
673+ } )
674+
562675test ( "uses COPILOT_REASONING_EFFORT from Claude settings.json for claude-opus-4.7" , async ( ) => {
563676 const tempHome = await mkdtemp ( path . join ( os . tmpdir ( ) , "claude-settings-" ) )
564677 const claudeDirectory = path . join ( tempHome , ".claude" )
0 commit comments