mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-07-10 01:39:25 +00:00
fix: skip legacy max step limit during migration (#772)
* fix: skip legacy max step limit during migration * fix: skip more obsolete legacy migration fields
This commit is contained in:
parent
ecd7a0afb6
commit
d47e699015
3 changed files with 106 additions and 0 deletions
6
.changeset/skip-legacy-max-steps.md
Normal file
6
.changeset/skip-legacy-max-steps.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
"@moonshot-ai/kimi-code": patch
|
||||
"@moonshot-ai/migration-legacy": patch
|
||||
---
|
||||
|
||||
Do not carry obsolete legacy loop, background, plan, yolo, or unknown experimental flags into migrated config files.
|
||||
|
|
@ -7,6 +7,7 @@ import {
|
|||
ProviderConfigSchema,
|
||||
transformTomlData,
|
||||
} from '@moonshot-ai/agent-core';
|
||||
import { FLAG_DEFINITIONS } from '@moonshot-ai/agent-core/flags/registry';
|
||||
import { atomicWrite } from '../atomic-write.js';
|
||||
import { DEFAULT_CONFIG_FILE_TEXT, isTuiStubOrMissing } from '../stub-detect.js';
|
||||
import {
|
||||
|
|
@ -19,6 +20,18 @@ import {
|
|||
|
||||
// `theme` / `default_editor` belong in tui.toml, not config.toml.
|
||||
const TUI_TOP_LEVEL_KEYS = new Set(['theme', 'default_editor']);
|
||||
const TOP_LEVEL_KEYS_TO_DROP = new Set(['plan_mode', 'yolo']);
|
||||
const LOOP_CONTROL_FIELDS_TO_KEEP = new Set([
|
||||
'max_retries_per_step',
|
||||
'reserved_context_size',
|
||||
]);
|
||||
const BACKGROUND_FIELDS_TO_KEEP = new Set([
|
||||
'max_running_tasks',
|
||||
'keep_alive_on_exit',
|
||||
]);
|
||||
const REGISTERED_EXPERIMENTAL_FLAGS: ReadonlySet<string> = new Set(
|
||||
FLAG_DEFINITIONS.map((definition) => definition.id),
|
||||
);
|
||||
|
||||
// kimi-code's tui.toml `theme` enum (mirrors apps/kimi-code TuiThemeSchema).
|
||||
// A legacy theme outside this set would fail loadTuiConfig()'s whole-file
|
||||
|
|
@ -97,6 +110,23 @@ function emptyResult(): ConfigStepResult {
|
|||
};
|
||||
}
|
||||
|
||||
function filterFields(
|
||||
value: Record<string, unknown>,
|
||||
fieldsToKeep: ReadonlySet<string>,
|
||||
): Record<string, unknown> | undefined {
|
||||
const keptEntries = Object.entries(value).filter(([field]) => fieldsToKeep.has(field));
|
||||
return keptEntries.length > 0 ? Object.fromEntries(keptEntries) : undefined;
|
||||
}
|
||||
|
||||
function filterRegisteredExperimentalFlags(
|
||||
value: Record<string, unknown>,
|
||||
): Record<string, unknown> | undefined {
|
||||
const keptEntries = Object.entries(value).filter(
|
||||
([field, flag]) => REGISTERED_EXPERIMENTAL_FLAGS.has(field) && typeof flag === 'boolean',
|
||||
);
|
||||
return keptEntries.length > 0 ? Object.fromEntries(keptEntries) : undefined;
|
||||
}
|
||||
|
||||
/** True when the kimi-cli provider entry validates against kimi-code's schema. */
|
||||
function providerIsSupported(prov: Record<string, unknown>): boolean {
|
||||
const transformed = transformTomlData({ providers: { x: prov } });
|
||||
|
|
@ -309,6 +339,7 @@ export async function migrateConfigStep(input: ConfigStepInput): Promise<ConfigS
|
|||
for (const [k, v] of Object.entries(parsed)) {
|
||||
if (k === 'providers' || k === 'models' || k === 'hooks') continue;
|
||||
if (TUI_TOP_LEVEL_KEYS.has(k)) continue;
|
||||
if (TOP_LEVEL_KEYS_TO_DROP.has(k)) continue;
|
||||
if (k === 'default_yolo') {
|
||||
// kimi-cli's `default_yolo` maps to kimi-code's `default_permission_mode`.
|
||||
if (v === true) migratedTop['default_permission_mode'] = 'yolo';
|
||||
|
|
@ -330,6 +361,27 @@ export async function migrateConfigStep(input: ConfigStepInput): Promise<ConfigS
|
|||
) {
|
||||
continue;
|
||||
}
|
||||
if (k === 'loop_control' && isRecord(v)) {
|
||||
const filteredLoopControl = filterFields(v, LOOP_CONTROL_FIELDS_TO_KEEP);
|
||||
if (filteredLoopControl !== undefined) {
|
||||
migratedTop[k] = filteredLoopControl;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (k === 'background' && isRecord(v)) {
|
||||
const filteredBackground = filterFields(v, BACKGROUND_FIELDS_TO_KEEP);
|
||||
if (filteredBackground !== undefined) {
|
||||
migratedTop[k] = filteredBackground;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (k === 'experimental' && isRecord(v)) {
|
||||
const filteredExperimental = filterRegisteredExperimentalFlags(v);
|
||||
if (filteredExperimental !== undefined) {
|
||||
migratedTop[k] = filteredExperimental;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
migratedTop[k] = v;
|
||||
}
|
||||
if (Object.keys(keptProviders).length > 0) migratedTop['providers'] = keptProviders;
|
||||
|
|
|
|||
|
|
@ -430,6 +430,54 @@ base_url = "https://target.example/v1"
|
|||
expect(r.siblingContents.hooks).toBe(2);
|
||||
});
|
||||
|
||||
it('drops legacy migration fields but keeps supported loop and background fields', async () => {
|
||||
await writeFile(
|
||||
join(src, 'config.toml'),
|
||||
'default_thinking = true\n' +
|
||||
'plan_mode = true\n' +
|
||||
'yolo = true\n' +
|
||||
'[experimental]\n' +
|
||||
'micro_compaction = false\n' +
|
||||
'unknown_flag = true\n' +
|
||||
'[loop_control]\n' +
|
||||
'max_steps_per_turn = 1000\n' +
|
||||
'max_steps_per_run = 42\n' +
|
||||
'max_retries_per_step = 2\n' +
|
||||
'max_ralph_iterations = 3\n' +
|
||||
'reserved_context_size = 60000\n' +
|
||||
'compaction_trigger_ratio = 0.7\n' +
|
||||
'[background]\n' +
|
||||
'max_running_tasks = 8\n' +
|
||||
'keep_alive_on_exit = true\n' +
|
||||
'kill_grace_period_ms = 2000\n' +
|
||||
'print_wait_ceiling_s = 3600\n' +
|
||||
'read_max_bytes = 30000\n',
|
||||
);
|
||||
|
||||
const r = await migrateConfigStep({ sourceHome: src, targetHome: tgt });
|
||||
|
||||
expect(r.migrated).toBe(true);
|
||||
const cfg = await readFile(join(tgt, 'config.toml'), 'utf-8');
|
||||
expect(cfg).toContain('[experimental]');
|
||||
expect(cfg).toContain('micro_compaction = false');
|
||||
expect(cfg).not.toContain('unknown_flag');
|
||||
expect(cfg).toContain('[loop_control]');
|
||||
expect(cfg).toContain('max_retries_per_step = 2');
|
||||
expect(cfg).toContain('reserved_context_size = 60000');
|
||||
expect(cfg).not.toContain('max_steps_per_turn');
|
||||
expect(cfg).not.toContain('max_steps_per_run');
|
||||
expect(cfg).not.toContain('max_ralph_iterations');
|
||||
expect(cfg).not.toContain('compaction_trigger_ratio');
|
||||
expect(cfg).toContain('[background]');
|
||||
expect(cfg).toContain('max_running_tasks = 8');
|
||||
expect(cfg).toContain('keep_alive_on_exit = true');
|
||||
expect(cfg).not.toContain('kill_grace_period_ms');
|
||||
expect(cfg).not.toContain('print_wait_ceiling_s');
|
||||
expect(cfg).not.toContain('read_max_bytes');
|
||||
expect(cfg).not.toContain('plan_mode = true');
|
||||
expect(cfg).not.toContain('yolo = true');
|
||||
});
|
||||
|
||||
it('maps default_yolo to default_permission_mode = "yolo"', async () => {
|
||||
await writeFile(
|
||||
join(src, 'config.toml'),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue