11import { afterEach , describe , expect , test } from "vitest" ;
2+ import { resetProviderAuthAliasMapCacheForTest } from "../agents/provider-auth-aliases.js" ;
23import type { OpenClawConfig } from "../config/config.js" ;
34import type { SessionEntry } from "../config/sessions.js" ;
45import { createEmptyPluginRegistry } from "../plugins/registry-empty.js" ;
@@ -108,6 +109,7 @@ function createAllowlistedAnthropicModelCfg(): OpenClawConfig {
108109
109110describe ( "gateway sessions patch" , ( ) => {
110111 afterEach ( ( ) => {
112+ resetProviderAuthAliasMapCacheForTest ( ) ;
111113 resetPluginRuntimeStateForTest ( ) ;
112114 } ) ;
113115
@@ -271,7 +273,7 @@ describe("gateway sessions patch", () => {
271273 expectPatchError ( result , "invalid elevatedLevel" ) ;
272274 } ) ;
273275
274- test ( "clears auth overrides when model patch changes" , async ( ) => {
276+ test ( "preserves same-provider auth overrides when model patch changes" , async ( ) => {
275277 const store : Record < string , SessionEntry > = {
276278 "agent:main:main" : {
277279 sessionId : "sess" ,
@@ -294,6 +296,151 @@ describe("gateway sessions patch", () => {
294296 ) ;
295297 expect ( entry . providerOverride ) . toBe ( "anthropic" ) ;
296298 expect ( entry . modelOverride ) . toBe ( "claude-sonnet-4-6" ) ;
299+ expect ( entry . authProfileOverride ) . toBe ( "anthropic:default" ) ;
300+ expect ( entry . authProfileOverrideSource ) . toBe ( "user" ) ;
301+ expect ( entry . authProfileOverrideCompactionCount ) . toBe ( 3 ) ;
302+ } ) ;
303+
304+ test ( "preserves auth overrides for provider-auth aliases when model patch changes" , async ( ) => {
305+ const store : Record < string , SessionEntry > = {
306+ "agent:main:main" : {
307+ sessionId : "sess-alias" ,
308+ updatedAt : 1 ,
309+ providerOverride : "byteplus" ,
310+ modelOverride : "seedance-1-0-lite-t2v-250428" ,
311+ authProfileOverride : "byteplus:work" ,
312+ authProfileOverrideSource : "user" ,
313+ authProfileOverrideCompactionCount : 2 ,
314+ } as SessionEntry ,
315+ } ;
316+ const entry = expectPatchOk (
317+ await runPatch ( {
318+ store,
319+ patch : { key : MAIN_SESSION_KEY , model : "byteplus-plan/ark-code-latest" } ,
320+ loadGatewayModelCatalog : async ( ) => [
321+ { provider : "byteplus-plan" , id : "ark-code-latest" , name : "ark-code-latest" } ,
322+ ] ,
323+ } ) ,
324+ ) ;
325+ expect ( entry . providerOverride ) . toBe ( "byteplus-plan" ) ;
326+ expect ( entry . modelOverride ) . toBe ( "ark-code-latest" ) ;
327+ expect ( entry . authProfileOverride ) . toBe ( "byteplus:work" ) ;
328+ expect ( entry . authProfileOverrideSource ) . toBe ( "user" ) ;
329+ expect ( entry . authProfileOverrideCompactionCount ) . toBe ( 2 ) ;
330+ } ) ;
331+
332+ test ( "preserves unprefixed auth overrides when existing provider matches model patch" , async ( ) => {
333+ const store : Record < string , SessionEntry > = {
334+ "agent:main:main" : {
335+ sessionId : "sess-unprefixed-same-provider" ,
336+ updatedAt : 1 ,
337+ providerOverride : "anthropic" ,
338+ modelOverride : "claude-opus-4-6" ,
339+ authProfileOverride : "work" ,
340+ authProfileOverrideSource : "user" ,
341+ authProfileOverrideCompactionCount : 4 ,
342+ } as SessionEntry ,
343+ } ;
344+ const entry = expectPatchOk (
345+ await runPatch ( {
346+ store,
347+ patch : { key : MAIN_SESSION_KEY , model : "anthropic/claude-sonnet-4-6" } ,
348+ loadGatewayModelCatalog : async ( ) => [
349+ { provider : "anthropic" , id : "claude-sonnet-4-6" , name : "sonnet" } ,
350+ ] ,
351+ } ) ,
352+ ) ;
353+ expect ( entry . providerOverride ) . toBe ( "anthropic" ) ;
354+ expect ( entry . modelOverride ) . toBe ( "claude-sonnet-4-6" ) ;
355+ expect ( entry . authProfileOverride ) . toBe ( "work" ) ;
356+ expect ( entry . authProfileOverrideSource ) . toBe ( "user" ) ;
357+ expect ( entry . authProfileOverrideCompactionCount ) . toBe ( 4 ) ;
358+ } ) ;
359+
360+ test ( "preserves unprefixed auth overrides when existing provider is the default" , async ( ) => {
361+ const store : Record < string , SessionEntry > = {
362+ "agent:main:main" : {
363+ sessionId : "sess-unprefixed-default-provider" ,
364+ updatedAt : 1 ,
365+ authProfileOverride : "work" ,
366+ authProfileOverrideSource : "user" ,
367+ authProfileOverrideCompactionCount : 4 ,
368+ } as SessionEntry ,
369+ } ;
370+ const entry = expectPatchOk (
371+ await runPatch ( {
372+ cfg : {
373+ agents : {
374+ defaults : {
375+ model : { primary : "anthropic/claude-opus-4-6" } ,
376+ } ,
377+ } ,
378+ } as OpenClawConfig ,
379+ store,
380+ patch : { key : MAIN_SESSION_KEY , model : "anthropic/claude-sonnet-4-6" } ,
381+ loadGatewayModelCatalog : async ( ) => [
382+ { provider : "anthropic" , id : "claude-sonnet-4-6" , name : "sonnet" } ,
383+ ] ,
384+ } ) ,
385+ ) ;
386+ expect ( entry . providerOverride ) . toBe ( "anthropic" ) ;
387+ expect ( entry . modelOverride ) . toBe ( "claude-sonnet-4-6" ) ;
388+ expect ( entry . authProfileOverride ) . toBe ( "work" ) ;
389+ expect ( entry . authProfileOverrideSource ) . toBe ( "user" ) ;
390+ expect ( entry . authProfileOverrideCompactionCount ) . toBe ( 4 ) ;
391+ } ) ;
392+
393+ test ( "clears unprefixed auth overrides when model patch changes provider" , async ( ) => {
394+ const store : Record < string , SessionEntry > = {
395+ "agent:main:main" : {
396+ sessionId : "sess-unprefixed-provider-change" ,
397+ updatedAt : 1 ,
398+ providerOverride : "anthropic" ,
399+ modelOverride : "claude-opus-4-6" ,
400+ authProfileOverride : "work" ,
401+ authProfileOverrideSource : "user" ,
402+ authProfileOverrideCompactionCount : 4 ,
403+ } as SessionEntry ,
404+ } ;
405+ const entry = expectPatchOk (
406+ await runPatch ( {
407+ store,
408+ patch : { key : MAIN_SESSION_KEY , model : "openai/gpt-5.4" } ,
409+ loadGatewayModelCatalog : async ( ) => [
410+ { provider : "openai" , id : "gpt-5.4" , name : "gpt-5.4" } ,
411+ ] ,
412+ } ) ,
413+ ) ;
414+ expect ( entry . providerOverride ) . toBe ( "openai" ) ;
415+ expect ( entry . modelOverride ) . toBe ( "gpt-5.4" ) ;
416+ expect ( entry . authProfileOverride ) . toBeUndefined ( ) ;
417+ expect ( entry . authProfileOverrideSource ) . toBeUndefined ( ) ;
418+ expect ( entry . authProfileOverrideCompactionCount ) . toBeUndefined ( ) ;
419+ } ) ;
420+
421+ test ( "clears provider-prefixed auth overrides when model patch changes provider" , async ( ) => {
422+ const store : Record < string , SessionEntry > = {
423+ "agent:main:main" : {
424+ sessionId : "sess-provider-change" ,
425+ updatedAt : 1 ,
426+ providerOverride : "anthropic" ,
427+ modelOverride : "claude-opus-4-6" ,
428+ authProfileOverride : "anthropic:default" ,
429+ authProfileOverrideSource : "user" ,
430+ authProfileOverrideCompactionCount : 3 ,
431+ } as SessionEntry ,
432+ } ;
433+ const entry = expectPatchOk (
434+ await runPatch ( {
435+ store,
436+ patch : { key : MAIN_SESSION_KEY , model : "openai/gpt-5.4" } ,
437+ loadGatewayModelCatalog : async ( ) => [
438+ { provider : "openai" , id : "gpt-5.4" , name : "gpt-5.4" } ,
439+ ] ,
440+ } ) ,
441+ ) ;
442+ expect ( entry . providerOverride ) . toBe ( "openai" ) ;
443+ expect ( entry . modelOverride ) . toBe ( "gpt-5.4" ) ;
297444 expect ( entry . authProfileOverride ) . toBeUndefined ( ) ;
298445 expect ( entry . authProfileOverrideSource ) . toBeUndefined ( ) ;
299446 expect ( entry . authProfileOverrideCompactionCount ) . toBeUndefined ( ) ;
0 commit comments