Skip to content
Merged
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/config-toml-writeback-preservation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---

Preserve comments, key order, and formatting in config.toml when configuration values are updated.
40 changes: 34 additions & 6 deletions packages/agent-core-v2/src/app/config/configService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ import { Emitter, type Event } from '#/_base/event';
import { BugIndicatingError, Error2, ErrorCodes, onUnexpectedError } from '#/errors';
import { IBootstrapService } from '#/app/bootstrap/bootstrap';
import { ILogService } from '#/_base/log/log';
import {
IAtomicTomlDocumentStore,
type IAtomicDocumentStore,
} from '#/persistence/interface/atomicDocumentStore';
import { IAtomicTomlDocumentStore } from '#/persistence/interface/atomicDocumentStore';

import {
type AnyEnvBindings,
Expand Down Expand Up @@ -48,6 +45,7 @@ import {
TomlError,
transformTomlData,
} from './toml';
import { planConfigWriteback } from './tomlWriteback';

const CONFIG_SCOPE = '';

Expand Down Expand Up @@ -315,7 +313,7 @@ export class ConfigService extends Disposable implements IConfigService {
@IConfigRegistry private readonly registry: IConfigRegistry,
@IBootstrapService private readonly bootstrap: IBootstrapService,
@ILogService private readonly log: ILogService,
@IAtomicTomlDocumentStore private readonly documentStore: IAtomicDocumentStore,
@IAtomicTomlDocumentStore private readonly documentStore: IAtomicTomlDocumentStore,
) {
super();
this.configKey = this.bootstrap.configKey;
Expand Down Expand Up @@ -789,13 +787,43 @@ export class ConfigService extends Disposable implements IConfigService {
{ cause: error },
);
}
let onDiskText: string | undefined;
try {
onDiskText = await this.documentStore.getText(CONFIG_SCOPE, this.configKey);
} catch {
onDiskText = undefined;
}
const stagedRawSnake = cloneRecord(onDisk);
const stagedRaw = transformTomlData(onDisk, this.registry);
const previousSnake: ResolvedConfig = {};
for (const domain of domains) {
const snakeKey = camelToSnake(domain);
previousSnake[snakeKey] = stagedRawSnake[snakeKey];
}
rebase(stagedRaw, stagedRawSnake);
for (const domain of domains) {
applySectionToToml(stagedRawSnake, domain, stagedRaw[domain], this.registry);
}
await this.documentStore.set(CONFIG_SCOPE, this.configKey, stagedRawSnake);
const plannedText =
onDiskText === undefined
? undefined
: planConfigWriteback(
onDiskText,
domains.map((domain) => {
const snakeKey = camelToSnake(domain);
return {
snakeKey,
previousValue: previousSnake[snakeKey],
nextValue: stagedRawSnake[snakeKey],
};
}),
stagedRawSnake,
);
if (plannedText === undefined) {
await this.documentStore.set(CONFIG_SCOPE, this.configKey, stagedRawSnake);
} else if (plannedText !== onDiskText) {
await this.documentStore.setText(CONFIG_SCOPE, this.configKey, plannedText);
}
this.rawSnake = stagedRawSnake;
this.raw = stagedRaw;
}
Expand Down
16 changes: 12 additions & 4 deletions packages/agent-core-v2/src/app/config/migrations.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import { join } from 'pathe';

import { type IAtomicDocumentStore } from '#/persistence/interface/atomicDocumentStore';
import { type IAtomicTomlDocumentStore } from '#/persistence/interface/atomicDocumentStore';

import { isPlainObject } from './configPure';
import { replaceThinkingEffortMax } from './tomlWriteback';

const MIGRATIONS_FILE = 'migrations-effort.json';
const THINKING_EFFORT_MAX_TO_HIGH = 'thinking-effort-max-to-high';
Expand Down Expand Up @@ -31,23 +32,30 @@ function writeMigrationMarker(homeDir: string, key: string): void {
}

export async function migrateThinkingEffortMaxToHigh(
documentStore: IAtomicDocumentStore,
documentStore: IAtomicTomlDocumentStore,
configKey: string,
homeDir: string,
): Promise<void> {
try {
if (readMigrationMarkers(homeDir)[THINKING_EFFORT_MAX_TO_HIGH] !== undefined) return;
let doc: Record<string, unknown> | undefined;
let text: string | undefined;
try {
text = await documentStore.getText(CONFIG_SCOPE, configKey);
const data = await documentStore.get<Record<string, unknown>>(CONFIG_SCOPE, configKey);
doc = data !== undefined && isPlainObject(data) ? data : {};
} catch {
return;
}
const thinking = doc['thinking'];
if (isPlainObject(thinking) && thinking['effort'] === 'max') {
doc['thinking'] = { ...thinking, effort: 'high' };
await documentStore.set(CONFIG_SCOPE, configKey, doc);
const migrated = text === undefined ? undefined : replaceThinkingEffortMax(text);
if (migrated === undefined) {
doc['thinking'] = { ...thinking, effort: 'high' };
await documentStore.set(CONFIG_SCOPE, configKey, doc);
} else if (migrated !== text) {
await documentStore.setText(CONFIG_SCOPE, configKey, migrated);
}
}
writeMigrationMarker(homeDir, THINKING_EFFORT_MAX_TO_HIGH);
} catch {
Expand Down
Loading
Loading