diff --git a/.changeset/clear-defaults-via-replace.md b/.changeset/clear-defaults-via-replace.md new file mode 100644 index 0000000000..ab0944e210 --- /dev/null +++ b/.changeset/clear-defaults-via-replace.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Fix the daemon leaving a stale default provider or default model behind when the default provider is removed or after OAuth logout. diff --git a/packages/agent-core-v2/src/app/auth/authService.ts b/packages/agent-core-v2/src/app/auth/authService.ts index 00e402f978..bd5306cd9c 100644 --- a/packages/agent-core-v2/src/app/auth/authService.ts +++ b/packages/agent-core-v2/src/app/auth/authService.ts @@ -510,8 +510,10 @@ export class OAuthService extends Disposable implements IOAuthService { await this.config.replace(SERVICES_SECTION, next.services); } if (cleanup.defaultModelCleared) { - await this.config.set(DEFAULT_MODEL_SECTION, undefined); - await this.config.set(THINKING_SECTION, undefined); + // `set()` cannot delete: its deepMerge resolves an undefined patch back + // to the existing base value. `replace()` removes the section instead. + await this.config.replace(DEFAULT_MODEL_SECTION, undefined); + await this.config.replace(THINKING_SECTION, undefined); } } diff --git a/packages/agent-core-v2/src/app/provider/providerService.ts b/packages/agent-core-v2/src/app/provider/providerService.ts index f02de8031f..4ccebff9fe 100644 --- a/packages/agent-core-v2/src/app/provider/providerService.ts +++ b/packages/agent-core-v2/src/app/provider/providerService.ts @@ -64,7 +64,9 @@ export class ProviderService extends Disposable implements IProviderService { const { [name]: _removed, ...rest } = current; await this.config.replace(PROVIDERS_SECTION, rest); if (this.config.get(DEFAULT_PROVIDER_SECTION) === name) { - await this.config.set(DEFAULT_PROVIDER_SECTION, undefined); + // `set()` cannot delete: its deepMerge resolves an undefined patch back + // to the existing base value. `replace()` removes the section instead. + await this.config.replace(DEFAULT_PROVIDER_SECTION, undefined); } } } diff --git a/packages/agent-core-v2/test/app/auth/auth.test.ts b/packages/agent-core-v2/test/app/auth/auth.test.ts index 6081ef9243..af57d9b5b4 100644 --- a/packages/agent-core-v2/test/app/auth/auth.test.ts +++ b/packages/agent-core-v2/test/app/auth/auth.test.ts @@ -133,6 +133,14 @@ describe('OAuthService', () => { services = value as Record | undefined; return; } + if (domain === 'defaultModel') { + defaultModel = value as string | undefined; + return; + } + if (domain === 'thinking') { + thinking = value as { enabled?: boolean; effort?: string } | undefined; + return; + } throw new Error(`unexpected config replace: ${domain}`); }); events = []; @@ -593,8 +601,11 @@ describe('OAuthService', () => { maxContextSize: 8192, }, }); - expect(configSet).toHaveBeenCalledWith('defaultModel', undefined); - expect(configSet).toHaveBeenCalledWith('thinking', undefined); + // Clearing must go through `replace` — `set()`'s deepMerge cannot delete. + expect(configReplace).toHaveBeenCalledWith('defaultModel', undefined); + expect(configReplace).toHaveBeenCalledWith('thinking', undefined); + expect(defaultModel).toBeUndefined(); + expect(thinking).toBeUndefined(); }); it('logout removes managed web services while preserving unrelated services', async () => { diff --git a/packages/agent-core-v2/test/app/provider/provider.test.ts b/packages/agent-core-v2/test/app/provider/provider.test.ts index 98d3f301c4..da018d85c8 100644 --- a/packages/agent-core-v2/test/app/provider/provider.test.ts +++ b/packages/agent-core-v2/test/app/provider/provider.test.ts @@ -118,7 +118,8 @@ describe('ProviderService', () => { expect(configReplace).toHaveBeenCalledWith(PROVIDERS_SECTION, { p2: { type: 'kimi' }, }); - expect(configSet).toHaveBeenCalledWith('defaultProvider', undefined); + // Clearing must go through `replace` — `set()`'s deepMerge cannot delete. + expect(configReplace).toHaveBeenCalledWith('defaultProvider', undefined); }); it('delete leaves defaultProvider when removing a different provider', async () => { @@ -127,7 +128,7 @@ describe('ProviderService', () => { defaultProvider = 'p2'; const svc = ix.get(IProviderService); await svc.delete('p1'); - expect(configSet).not.toHaveBeenCalled(); + expect(configReplace).toHaveBeenCalledTimes(1); }); it('forwards providers section changes as onDidChangeProviders with a diff', () => {