@@ -460,6 +460,48 @@ module.exports = async function testConfig(ctx) {
460460 'update-provider should preserve preferred_auth_method inline comment'
461461 ) ;
462462
463+ const multilineStringConfig = [
464+ 'model_provider = "foo"' ,
465+ 'model = "gpt-5.3-codex"' ,
466+ '' ,
467+ '[model_providers.foo]' ,
468+ 'name = "foo"' ,
469+ 'base_url = "https://api.example.com/v1"' ,
470+ 'wire_api = "responses"' ,
471+ 'requires_openai_auth = false' ,
472+ 'preferred_auth_method = """sk-old' ,
473+ 'line-2""" # keep-triple-comment' ,
474+ 'request_max_retries = 4' ,
475+ 'stream_max_retries = 10' ,
476+ 'stream_idle_timeout_ms = 300000' ,
477+ '' ,
478+ '[model_providers.openai]' ,
479+ 'name = "openai"' ,
480+ 'base_url = "https://api.openai.com/v1"' ,
481+ 'wire_api = "responses"' ,
482+ 'requires_openai_auth = false' ,
483+ 'preferred_auth_method = ""' ,
484+ 'request_max_retries = 4' ,
485+ 'stream_max_retries = 10' ,
486+ 'stream_idle_timeout_ms = 300000' ,
487+ ''
488+ ] . join ( '\n' ) ;
489+ fs . writeFileSync ( legacyConfigPath , multilineStringConfig , 'utf-8' ) ;
490+ const updateMultilineString = await legacyApi ( 'update-provider' , {
491+ name : 'foo' ,
492+ key : 'sk-triple-updated'
493+ } ) ;
494+ assert ( updateMultilineString . success === true , 'update-provider should handle multiline TOML string values' ) ;
495+ const configAfterMultilineUpdate = fs . readFileSync ( legacyConfigPath , 'utf-8' ) ;
496+ assert (
497+ configAfterMultilineUpdate . includes ( 'preferred_auth_method = "sk-triple-updated" # keep-triple-comment' ) ,
498+ 'update-provider should safely replace multiline TOML string and preserve comment'
499+ ) ;
500+ assert (
501+ ! configAfterMultilineUpdate . includes ( 'line-2"""' ) ,
502+ 'update-provider should remove previous multiline TOML string tail'
503+ ) ;
504+
463505 const nestedMetadataConfig = [
464506 'model_provider = "foo"' ,
465507 'model = "gpt-5.3-codex"' ,
@@ -536,6 +578,69 @@ module.exports = async function testConfig(ctx) {
536578 'nested provider with dotted segment should update apiKey'
537579 ) ;
538580
581+ const ambiguousNestedProviderConfig = [
582+ 'model_provider = "foo"' ,
583+ 'model = "gpt-5.3-codex"' ,
584+ '' ,
585+ '[model_providers.foo]' ,
586+ 'name = "foo"' ,
587+ 'base_url = "https://api.example.com/v1"' ,
588+ 'wire_api = "responses"' ,
589+ 'requires_openai_auth = false' ,
590+ 'preferred_auth_method = "sk-foo"' ,
591+ 'request_max_retries = 4' ,
592+ 'stream_max_retries = 10' ,
593+ 'stream_idle_timeout_ms = 300000' ,
594+ '' ,
595+ "[ model_providers . foo . 'bar.baz' ]" ,
596+ 'base_url = "https://primary.example.com/v1"' ,
597+ 'wire_api = "responses"' ,
598+ 'preferred_auth_method = "sk-primary"' ,
599+ '' ,
600+ '[model_providers."foo.bar".baz]' ,
601+ 'base_url = "https://alt.example.com/v1"' ,
602+ 'wire_api = "responses"' ,
603+ 'preferred_auth_method = "sk-alt"' ,
604+ ''
605+ ] . join ( '\n' ) ;
606+ fs . writeFileSync ( legacyConfigPath , ambiguousNestedProviderConfig , 'utf-8' ) ;
607+ const ambiguousList = await legacyApi ( 'list' ) ;
608+ assert (
609+ ambiguousList . providers . some ( ( item ) => item && item . name === 'foo.bar.baz' ) ,
610+ 'ambiguous nested provider config should expose flattened provider name'
611+ ) ;
612+ const ambiguousUpdate = await legacyApi ( 'update-provider' , {
613+ name : 'foo.bar.baz' ,
614+ url : 'https://primary-updated.example.com/v1' ,
615+ key : 'sk-primary-updated'
616+ } ) ;
617+ assert ( ambiguousUpdate . success === true , 'ambiguous nested provider update should succeed' ) ;
618+ const configAfterAmbiguousUpdate = fs . readFileSync ( legacyConfigPath , 'utf-8' ) ;
619+ const primaryBlockMatch = configAfterAmbiguousUpdate . match (
620+ / (?: ^ | \n ) \s * \[ \s * m o d e l _ p r o v i d e r s \s * \. \s * f o o \s * \. \s * ' b a r \. b a z ' \s * \] [ \s \S ] * ?(? = \n \s * \[ | $ ) /
621+ ) ;
622+ assert ( primaryBlockMatch , 'primary nested provider block should exist after ambiguous update' ) ;
623+ assert (
624+ primaryBlockMatch [ 0 ] . includes ( 'base_url = "https://primary-updated.example.com/v1"' ) ,
625+ 'ambiguous update should target primary nested provider block url'
626+ ) ;
627+ assert (
628+ primaryBlockMatch [ 0 ] . includes ( 'preferred_auth_method = "sk-primary-updated"' ) ,
629+ 'ambiguous update should target primary nested provider block key'
630+ ) ;
631+ const alternateBlockMatch = configAfterAmbiguousUpdate . match (
632+ / (?: ^ | \n ) \s * \[ \s * m o d e l _ p r o v i d e r s \s * \. \s * " f o o \. b a r " \s * \. \s * b a z \s * \] [ \s \S ] * ?(? = \n \s * \[ | $ ) /
633+ ) ;
634+ assert ( alternateBlockMatch , 'alternate nested provider block should exist after ambiguous update' ) ;
635+ assert (
636+ alternateBlockMatch [ 0 ] . includes ( 'base_url = "https://alt.example.com/v1"' ) ,
637+ 'ambiguous update should not rewrite alternate nested provider block url'
638+ ) ;
639+ assert (
640+ alternateBlockMatch [ 0 ] . includes ( 'preferred_auth_method = "sk-alt"' ) ,
641+ 'ambiguous update should not rewrite alternate nested provider block key'
642+ ) ;
643+
539644 const ipv6Config = [
540645 'model_provider = "foo"' ,
541646 'model = "gpt-5.3-codex"' ,
0 commit comments