Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/clear-defaults-via-replace.md
Original file line number Diff line number Diff line change
@@ -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.
6 changes: 4 additions & 2 deletions packages/agent-core-v2/src/app/auth/authService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
4 changes: 3 additions & 1 deletion packages/agent-core-v2/src/app/provider/providerService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>(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.
Comment on lines +67 to +68

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove inline comments from agent-core-v2 code

packages/agent-core-v2/AGENTS.md requires comments in this subtree to live only in the top-of-file header block and says to never place comments beside functions, methods, or statements. This newly added explanatory inline comment violates that scoped rule (the same pattern is repeated in the auth and test changes), so the diff won't meet the package's review standards; move the context to the file header if it is still needed, or delete it.

Useful? React with 👍 / 👎.

await this.config.replace(DEFAULT_PROVIDER_SECTION, undefined);
}
}
}
Expand Down
15 changes: 13 additions & 2 deletions packages/agent-core-v2/test/app/auth/auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,14 @@ describe('OAuthService', () => {
services = value as Record<string, unknown> | 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 = [];
Expand Down Expand Up @@ -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 () => {
Expand Down
5 changes: 3 additions & 2 deletions packages/agent-core-v2/test/app/provider/provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand All @@ -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', () => {
Expand Down
Loading