@@ -36,15 +36,53 @@ vi.mock("../agents/auth-profiles.js", () => ({
3636} ) ) ;
3737
3838const resolveEnvApiKey = vi . hoisted ( ( ) =>
39- vi . fn < ( _provider : string ) => { apiKey : string ; source : string } | null > ( ( _provider : string ) => ( {
40- apiKey : "test-key" ,
41- source : "test" ,
42- } ) ) ,
39+ vi . fn < ( _provider : string , _env ?: NodeJS . ProcessEnv ) => { apiKey : string ; source : string } | null > (
40+ ( _provider : string ) => ( {
41+ apiKey : "test-key" ,
42+ source : "test" ,
43+ } ) ,
44+ ) ,
45+ ) ;
46+ const hasUsableCustomProviderApiKey = vi . hoisted ( ( ) =>
47+ vi . fn < ( _cfg ?: OpenClawConfig , _provider ?: string , _env ?: NodeJS . ProcessEnv ) => boolean > (
48+ ( ) => false ,
49+ ) ,
50+ ) ;
51+ const hasRuntimeAvailableProviderAuth = vi . hoisted ( ( ) =>
52+ vi . fn (
53+ ( {
54+ provider,
55+ cfg,
56+ env,
57+ } : {
58+ provider : string ;
59+ cfg ?: OpenClawConfig ;
60+ env ?: NodeJS . ProcessEnv ;
61+ } ) => {
62+ if ( provider === "amazon-bedrock" ) {
63+ const auth = cfg ?. models ?. providers ?. [ "amazon-bedrock" ] ?. auth ;
64+ return auth === undefined || auth === "aws-sdk" ;
65+ }
66+ if ( resolveEnvApiKey ( provider , env ) ?. apiKey ) {
67+ return true ;
68+ }
69+ if ( hasUsableCustomProviderApiKey ( cfg , provider , env ) ) {
70+ return true ;
71+ }
72+ const providerConfig = cfg ?. models ?. providers ?. [ provider ] ;
73+ return Boolean (
74+ providerConfig ?. baseUrl ?. startsWith ( "http://127.0.0.1" ) &&
75+ providerConfig . api &&
76+ providerConfig . models ?. length &&
77+ ! providerConfig . apiKey ,
78+ ) ;
79+ } ,
80+ ) ,
4381) ;
44- const hasUsableCustomProviderApiKey = vi . hoisted ( ( ) => vi . fn ( ( ) => false ) ) ;
4582vi . mock ( "../agents/model-auth.js" , ( ) => ( {
4683 resolveEnvApiKey,
4784 hasUsableCustomProviderApiKey,
85+ hasRuntimeAvailableProviderAuth,
4886} ) ) ;
4987
5088const resolveOwningPluginIdsForProvider = vi . hoisted ( ( ) =>
@@ -208,6 +246,30 @@ describe("promptDefaultModel", () => {
208246 expect ( values ) . toEqual ( [ "anthropic/claude-sonnet-4-6" ] ) ;
209247 } ) ;
210248
249+ it ( "keeps implicit Bedrock AWS SDK models visible without API-key auth" , async ( ) => {
250+ resolveEnvApiKey . mockReturnValue ( null ) ;
251+ loadModelCatalog . mockResolvedValue ( [
252+ { provider : "amazon-bedrock" , id : "us.anthropic.claude-sonnet-4-5" , name : "Claude Sonnet" } ,
253+ { provider : "openai" , id : "gpt-5.5" , name : "GPT-5.5" } ,
254+ ] ) ;
255+
256+ const select = vi . fn ( async ( params ) => params . initialValue as never ) ;
257+ const prompter = makePrompter ( { select } ) ;
258+
259+ await promptDefaultModel ( {
260+ config : { agents : { defaults : { } } } as OpenClawConfig ,
261+ prompter,
262+ allowKeep : false ,
263+ includeManual : false ,
264+ ignoreAllowlist : true ,
265+ } ) ;
266+
267+ const values = ( select . mock . calls [ 0 ] ?. [ 0 ] ?. options ?? [ ] ) . map (
268+ ( option : { value : string } ) => option . value ,
269+ ) ;
270+ expect ( values ) . toEqual ( [ "amazon-bedrock/us.anthropic.claude-sonnet-4-5" ] ) ;
271+ } ) ;
272+
211273 it ( "hides legacy runtime providers from default model choices" , async ( ) => {
212274 loadModelCatalog . mockResolvedValue ( [
213275 { provider : "codex" , id : "gpt-5.5" , name : "GPT-5.5" } ,
@@ -899,6 +961,44 @@ describe("promptModelAllowlist", () => {
899961 } ) ;
900962 } ) ;
901963
964+ it ( "keeps local no-key provider models visible in allowlist choices" , async ( ) => {
965+ resolveEnvApiKey . mockReturnValue ( null ) ;
966+ loadModelCatalog . mockResolvedValue ( [
967+ {
968+ provider : "vllm" ,
969+ id : "meta-llama/Meta-Llama-3-8B-Instruct" ,
970+ name : "Meta Llama" ,
971+ } ,
972+ {
973+ provider : "openai" ,
974+ id : "gpt-5.5" ,
975+ name : "GPT-5.5" ,
976+ } ,
977+ ] ) ;
978+
979+ const multiselect = createSelectAllMultiselect ( ) ;
980+ const prompter = makePrompter ( { multiselect } ) ;
981+ const config = {
982+ models : {
983+ providers : {
984+ vllm : {
985+ api : "openai-completions" ,
986+ baseUrl : "http://127.0.0.1:8000/v1" ,
987+ models : [ configuredTextModel ( "meta-llama/Meta-Llama-3-8B-Instruct" , "Meta Llama" ) ] ,
988+ } ,
989+ } ,
990+ } ,
991+ agents : { defaults : { } } ,
992+ } as OpenClawConfig ;
993+
994+ const result = await promptModelAllowlist ( { config, prompter } ) ;
995+
996+ expect (
997+ multiselect . mock . calls [ 0 ] ?. [ 0 ] ?. options . map ( ( option : { value : string } ) => option . value ) ,
998+ ) . toEqual ( [ "vllm/meta-llama/Meta-Llama-3-8B-Instruct" ] ) ;
999+ expect ( result . models ) . toEqual ( [ "vllm/meta-llama/Meta-Llama-3-8B-Instruct" ] ) ;
1000+ } ) ;
1001+
9021002 it ( "seeds existing model fallbacks into unscoped allowlist selections" , async ( ) => {
9031003 loadModelCatalog . mockResolvedValue ( [
9041004 {
0 commit comments