@@ -366,4 +366,100 @@ trust_level = "trusted"
366366 "[projects." ,
367367 )
368368 } )
369+
370+ test ( "recovers from orphan managed block and stale provider table" , async ( ) => {
371+ const p = await makeConfigPath ( )
372+ await writeFile (
373+ p ,
374+ `model = "gpt-5.5"
375+ model_reasoning_effort = "xhigh"
376+ model_auto_compact_token_limit = 200000
377+
378+ # >>> copilot-bridge managed block — auto-generated, do not edit between markers >>>
379+ model_provider = "bridge"
380+
381+ # >>> copilot-bridge managed block — auto-generated, do not edit between markers >>>
382+ model_provider = "bridge"
383+ model_context_window = 1050000
384+ model_supports_reasoning_summaries = true
385+
386+ [model_providers.bridge]
387+ name = "Copilot Bridge"
388+ base_url = "http://old/v1"
389+ wire_api = "responses"
390+ prefer_websockets = false
391+ requires_openai_auth = false
392+ # <<< copilot-bridge managed block — edits outside this block are preserved <<<
393+
394+ [model_providers.bridge]
395+ name = "Copilot Bridge"
396+ base_url = "http://old/v1"
397+ wire_api = "responses"
398+ prefer_websockets = false
399+ requires_openai_auth = false
400+
401+ [tui.model_availability_nux]
402+ "gpt-5.5" = 4
403+
404+ [projects."/Users/z/ai_run"]
405+ trust_level = "trusted"
406+ ` ,
407+ )
408+ await applyCodexConfig ( {
409+ configPath : p ,
410+ baseUrl : "http://127.0.0.1:4242/v1" ,
411+ settings : baseSettings ,
412+ modelContextWindow : 1050000 ,
413+ } )
414+ const content = await readFile ( p , "utf8" )
415+
416+ expect ( content . match ( / ^ m o d e l _ p r o v i d e r = / gm) ) . toHaveLength ( 1 )
417+ expect ( content . match ( / ^ m o d e l _ c o n t e x t _ w i n d o w = / gm) ) . toHaveLength ( 1 )
418+ expect ( content . match ( / ^ m o d e l _ s u p p o r t s _ r e a s o n i n g _ s u m m a r i e s = / gm) ) . toHaveLength ( 1 )
419+ expect ( content . match ( / ^ \[ m o d e l _ p r o v i d e r s \. b r i d g e \] $ / gm) ) . toHaveLength ( 1 )
420+ expect ( content . match ( / c o p i l o t - b r i d g e m a n a g e d b l o c k — a u t o - g e n e r a t e d / g) ) . toHaveLength ( 1 )
421+ expect ( content . match ( / c o p i l o t - b r i d g e m a n a g e d b l o c k — e d i t s o u t s i d e / g) ) . toHaveLength ( 1 )
422+ expect ( content ) . toContain ( "model_auto_compact_token_limit = 200000" )
423+ expect ( content ) . toContain ( '[projects."/Users/z/ai_run"]' )
424+ expect ( content ) . toContain ( '[tui.model_availability_nux]' )
425+ expect ( content ) . toContain ( 'base_url = "http://127.0.0.1:4242/v1"' )
426+ } )
427+
428+ test ( "preserves non-bridge provider tables while replacing bridge provider" , async ( ) => {
429+ const p = await makeConfigPath ( )
430+ await writeFile (
431+ p ,
432+ `model = "gpt-5.5"
433+
434+ [model_providers.openai]
435+ name = "OpenAI"
436+ base_url = "https://api.openai.com/v1"
437+ wire_api = "responses"
438+
439+ [model_providers.bridge]
440+ name = "Old Bridge"
441+ base_url = "http://old/v1"
442+ wire_api = "responses"
443+
444+ [profiles.work]
445+ model_provider = "openai"
446+ ` ,
447+ )
448+ await applyCodexConfig ( {
449+ configPath : p ,
450+ baseUrl : "http://127.0.0.1:4242/v1" ,
451+ settings : baseSettings ,
452+ modelContextWindow : 1050000 ,
453+ } )
454+ const content = await readFile ( p , "utf8" )
455+
456+ expect ( content ) . toContain ( "[model_providers.openai]" )
457+ expect ( content ) . toContain ( 'base_url = "https://api.openai.com/v1"' )
458+ expect ( content ) . toContain ( "[profiles.work]" )
459+ expect ( content ) . toContain ( 'model_provider = "openai"' )
460+ expect ( content . match ( / ^ \[ m o d e l _ p r o v i d e r s \. b r i d g e \] $ / gm) ) . toHaveLength ( 1 )
461+ expect ( content ) . toContain ( 'name = "Copilot Bridge"' )
462+ expect ( content ) . toContain ( 'base_url = "http://127.0.0.1:4242/v1"' )
463+ expect ( content ) . not . toContain ( 'name = "Old Bridge"' )
464+ } )
369465} )
0 commit comments