@@ -3,6 +3,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
33import { testing as cliBackendsTesting } from "../agents/cli-backends.js" ;
44import type { ModelCatalogEntry } from "../agents/model-catalog.js" ;
55import type { OpenClawConfig } from "../config/config.js" ;
6+ import type { WizardMultiSelectParams , WizardPrompter } from "../wizard/prompts.js" ;
67import {
78 applyModelAllowlist ,
89 applyModelFallbacksFromSelection ,
@@ -1424,6 +1425,64 @@ describe("promptModelAllowlist", () => {
14241425 expect ( optionValues ( options ) ) . toEqual ( [ "openai/gpt-5.5" , "openai/gpt-5.4-mini" ] ) ;
14251426 } ) ;
14261427
1428+ it ( "includes stale configured preferred provider models in the scoped cleanup" , async ( ) => {
1429+ loadModelCatalog . mockResolvedValue ( [
1430+ {
1431+ provider : "openrouter" ,
1432+ id : "meta-llama/llama-3.3-70b:free" ,
1433+ name : "Llama 3.3 70B" ,
1434+ } ,
1435+ {
1436+ provider : "openai" ,
1437+ id : "gpt-5.5" ,
1438+ name : "GPT-5.5" ,
1439+ } ,
1440+ ] ) ;
1441+
1442+ const activeModel = "openrouter/meta-llama/llama-3.3-70b:free" ;
1443+ const staleModel = "openrouter/elephant-alpha" ;
1444+ const multiselect = vi . fn ( async ( params : WizardMultiSelectParams ) => {
1445+ return params . options . map ( ( option ) => option . value ) . filter ( ( value ) => value === activeModel ) ;
1446+ } ) ;
1447+ const prompter = makePrompter ( {
1448+ multiselect : multiselect as unknown as WizardPrompter [ "multiselect" ] ,
1449+ } ) ;
1450+ const config = {
1451+ agents : {
1452+ defaults : {
1453+ models : {
1454+ [ activeModel ] : { alias : "llama" } ,
1455+ [ staleModel ] : { alias : "elephant" } ,
1456+ "anthropic/claude-sonnet-4-6" : { alias : "sonnet" } ,
1457+ } ,
1458+ } ,
1459+ } ,
1460+ } as OpenClawConfig ;
1461+
1462+ const result = await promptModelAllowlist ( {
1463+ config,
1464+ prompter,
1465+ preferredProvider : "openrouter" ,
1466+ } ) ;
1467+
1468+ const options = pickerOptions ( multiselect ) ;
1469+ expect ( optionValues ( options ) ) . toEqual ( [ activeModel , staleModel ] ) ;
1470+ expect ( requireOption ( options , staleModel ) . hint ) . toBe ( "configured (not in catalog)" ) ;
1471+ expect ( multiselect . mock . calls [ 0 ] ?. [ 0 ] ?. initialValues ) . toEqual ( [ activeModel , staleModel ] ) ;
1472+ expect ( result ) . toEqual ( {
1473+ models : [ activeModel ] ,
1474+ scopeKeys : [ activeModel , staleModel ] ,
1475+ } ) ;
1476+
1477+ const next = applyModelAllowlist ( config , result . models ?? [ ] , {
1478+ scopeKeys : result . scopeKeys ,
1479+ } ) ;
1480+ expect ( next . agents ?. defaults ?. models ) . toEqual ( {
1481+ [ activeModel ] : { alias : "llama" } ,
1482+ "anthropic/claude-sonnet-4-6" : { alias : "sonnet" } ,
1483+ } ) ;
1484+ } ) ;
1485+
14271486 it ( "shows configured preferred provider models when the catalog has no entries" , async ( ) => {
14281487 loadModelCatalog . mockResolvedValue ( [ ] ) ;
14291488
0 commit comments