diff --git a/src/components/AgentSettingsPanel.tsx b/src/components/AgentSettingsPanel.tsx index c4fb147..f2b4787 100644 --- a/src/components/AgentSettingsPanel.tsx +++ b/src/components/AgentSettingsPanel.tsx @@ -236,19 +236,37 @@ export function AgentSettingsPanel({ rawOther, onChange, models, defaultModel }: - {/* Top-level `[watch] enabled` (kimi-code 2.0.1+; off by default since - 2.0.2). KIMI_CODE_WATCH outranks this config at runtime — the env - var is probed by get_experimental_env_status as a non-flag entry - but deliberately locks nothing here, the toggle just documents the - config value that applies when the env var is unset. */} + {/* Top-level `[watch] enabled` (kimi-code 2.0.1+; 2.0.2 flipped the + default off, #4015 flipped it back on). KIMI_CODE_WATCH outranks + this config at runtime — the env var is probed by + get_experimental_env_status as a non-flag entry but deliberately + locks nothing here, the toggle just documents the config value + that applies when the env var is unset. */} updateWatch({ enabled: checked })} />

{t("watchEnabledDesc")}

+ + {/* Top-level booleans, both default true upstream: false is written + explicitly, true removes the key (see setAgentSettings). */} + update({ auto_session_title: checked })} + /> +

{t("autoSessionTitleDesc")}

+ update({ repeat_breaker: checked })} + /> +

{t("repeatBreakerDesc")}

+
+ {/* kimi-code `[permission] dangerous_command_guard`. The env var KIMI_CODE_DANGEROUS_COMMAND_GUARD (literal "true"/"false") diff --git a/src/i18n/en.ts b/src/i18n/en.ts index 2807385..2fc8db5 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -112,7 +112,7 @@ export const enTranslations: Record = { envPairs: "Env pairs", addEnv: "+ Add", modelMappingDesc: "Display name only affects the /model menu.", - maxOutputSizeDesc: "Max output tokens writes max_output_size — leave blank to use the upstream default.", + maxOutputSizeDesc: "Max output tokens writes max_output_size — leave blank to send no output cap upstream.", oneClickSetup: "One-click setup", fetchModels: "Fetch models", fetchingModels: "Fetching...", @@ -188,7 +188,14 @@ export const enTranslations: Record = { watchSettings: "File Watching", watchEnabled: "Enable file watching", watchEnabledDesc: - "Watch config files and the workspace for changes (configurable since kimi-code 2.0.1, off by default since 2.0.2). The KIMI_CODE_WATCH environment variable overrides this setting.", + "Watch config files and the workspace for changes (configurable since kimi-code 2.0.1, on by default). The KIMI_CODE_WATCH environment variable overrides this setting.", + behaviorSettings: "Behavior", + autoSessionTitle: "Auto session title", + autoSessionTitleDesc: + "Let clients auto-generate session titles (on by default; turning it off writes false).", + repeatBreaker: "Repeat breaker", + repeatBreakerDesc: + "Remind and force-stop on consecutively repeated identical tool calls (on by default). The KIMI_CODE_REPEAT_BREAKER environment variable overrides this setting.", permissionRules: "Permission Rules", permissionDecision: "Decision", permissionPattern: "Pattern", diff --git a/src/i18n/zh.ts b/src/i18n/zh.ts index a33674b..b1016bb 100644 --- a/src/i18n/zh.ts +++ b/src/i18n/zh.ts @@ -109,7 +109,7 @@ export const zhTranslations = { envPairs: "Env 键值对", addEnv: "+ 添加", modelMappingDesc: "显示名称只影响 /model 菜单。", - maxOutputSizeDesc: "「最大输出 Token」写入 max_output_size,留空则用上游默认值。", + maxOutputSizeDesc: "「最大输出 Token」写入 max_output_size,留空则不发送输出上限。", oneClickSetup: "一键设置", fetchModels: "获取模型列表", fetchingModels: "获取中...", @@ -181,7 +181,14 @@ export const zhTranslations = { watchSettings: "文件监听", watchEnabled: "启用文件监听", watchEnabledDesc: - "监听配置文件与工作目录变化(kimi-code 2.0.1 起可配置,2.0.2 起默认关闭)。环境变量 KIMI_CODE_WATCH 会覆盖本配置。", + "监听配置文件与工作目录变化(kimi-code 2.0.1 起可配置,默认开启)。环境变量 KIMI_CODE_WATCH 会覆盖本配置。", + behaviorSettings: "行为开关", + autoSessionTitle: "自动生成会话标题", + autoSessionTitleDesc: + "允许客户端自动生成会话标题(默认开启;关闭后写入 false)。", + repeatBreaker: "重复调用拦截", + repeatBreakerDesc: + "连续重复相同工具调用时提醒并强制停止(默认开启)。环境变量 KIMI_CODE_REPEAT_BREAKER 会覆盖本配置。", permissionRules: "权限规则", permissionDecision: "处置", permissionPattern: "模式", diff --git a/src/lib/agent-settings.test.ts b/src/lib/agent-settings.test.ts index 0063325..7caca11 100644 --- a/src/lib/agent-settings.test.ts +++ b/src/lib/agent-settings.test.ts @@ -201,8 +201,8 @@ describe("loop_control.compaction_max_attempts", () => { }); // --------------------------------------------------------------------------- -// [watch] enabled (kimi-code 2.0.1+) — top-level section, off by default since -// 2.0.2; mirrors the [background] handling +// [watch] enabled (kimi-code 2.0.1+) — top-level section; 2.0.2 flipped the +// default off, #4015 flipped it back on. Mirrors the [background] handling. // --------------------------------------------------------------------------- function watchOf(raw: unknown): Record { @@ -213,9 +213,9 @@ function watchOf(raw: unknown): Record { } describe("[watch] enabled", () => { - it("reads as false when the section or key is absent (2.0.2 default)", () => { - expect(getAgentSettings({}).watch?.enabled).toBe(false); - expect(getAgentSettings({ watch: {} }).watch?.enabled).toBe(false); + it("reads as true when the section or key is absent (#4015 default)", () => { + expect(getAgentSettings({}).watch?.enabled).toBe(true); + expect(getAgentSettings({ watch: {} }).watch?.enabled).toBe(true); }); it("reads an explicit value", () => { @@ -248,6 +248,49 @@ describe("[watch] enabled", () => { }); }); +// --------------------------------------------------------------------------- +// Top-level booleans (auto_session_title #3962 / repeat_breaker #3995): +// upstream default true — false is written explicitly, true removes the key. +// --------------------------------------------------------------------------- + +describe("top-level boolean switches", () => { + it("reads as undefined when the key is absent (default true)", () => { + const s = getAgentSettings({}); + expect(s.auto_session_title).toBeUndefined(); + expect(s.repeat_breaker).toBeUndefined(); + }); + + it("reads explicit false values", () => { + const s = getAgentSettings({ + auto_session_title: false, + repeat_breaker: false, + }); + expect(s.auto_session_title).toBe(false); + expect(s.repeat_breaker).toBe(false); + }); + + it("writes false explicitly and removes the key on true", () => { + const off = setAgentSettings({}, { repeat_breaker: false }) as Record< + string, + unknown + >; + expect(off.repeat_breaker).toBe(false); + const on = setAgentSettings( + { repeat_breaker: false }, + { repeat_breaker: true } + ) as Record; + expect("repeat_breaker" in on).toBe(false); + }); + + it("keeps an explicit false on saves that do not touch it", () => { + const next = setAgentSettings( + { auto_session_title: false }, + { thinking: { effort: "high" } } + ) as Record; + expect(next.auto_session_title).toBe(false); + }); +}); + describe("permission.dangerous_command_guard", () => { it("reads as undefined when the key is absent (default on)", () => { diff --git a/src/lib/agent-settings.ts b/src/lib/agent-settings.ts index 158c046..2dabb74 100644 --- a/src/lib/agent-settings.ts +++ b/src/lib/agent-settings.ts @@ -13,10 +13,11 @@ const DEFAULT_SETTINGS: AgentSettings = { background: { keep_alive_on_exit: false, }, - // `[watch] enabled` — kimi-code 2.0.1 added the key, 2.0.2 flipped the - // default to off, so an absent key reads as "off". + // `[watch] enabled` — kimi-code 2.0.1 added the key; 2.0.2 flipped the + // default to off, then #4015 flipped it back on, so an absent key reads + // as "on". watch: { - enabled: false, + enabled: true, }, permission: { rules: [] }, hooks: [], @@ -37,6 +38,7 @@ function getSection(rawOther: unknown, key: string): T | undefined { } export function getAgentSettings(rawOther: unknown): AgentSettings { + const root = asRecord(rawOther); const sectionLoop = getSection( rawOther, "loop_control" @@ -92,6 +94,16 @@ export function getAgentSettings(rawOther: unknown): AgentSettings { : {}), }, hooks: getSection(rawOther, "hooks") ?? [], + // Top-level booleans (upstream default true): keep only explicit values, + // an absent key means "on" — the UI shows `?? true`. + auto_session_title: + typeof root.auto_session_title === "boolean" + ? root.auto_session_title + : undefined, + repeat_breaker: + typeof root.repeat_breaker === "boolean" + ? root.repeat_breaker + : undefined, }; } @@ -166,5 +178,17 @@ export function setAgentSettings( delete root.hooks; } + // Top-level booleans with an upstream default of true: an explicit false + // is written; true/undefined removes the key so the config tracks the + // upstream default instead of pinning it. + for (const key of ["auto_session_title", "repeat_breaker"] as const) { + const value = patch[key] ?? current[key]; + if (value === false) { + root[key] = false; + } else { + delete root[key]; + } + } + return root; } diff --git a/src/types/index.ts b/src/types/index.ts index d0e3c4a..d101321 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -172,6 +172,13 @@ export interface AgentSettings { background?: BackgroundConfig; /** Top-level `[watch]` section (kimi-code 2.0.1+). */ watch?: WatchConfig; + /** Top-level config.toml key (kimi-code #3962): let clients auto-generate + * session titles. Default true; only an explicit false disables. */ + auto_session_title?: boolean; + /** Top-level config.toml key (kimi-code #3995): repeat-breaker for + * consecutively repeated identical tool calls. Default true; env + * KIMI_CODE_REPEAT_BREAKER outranks this config. */ + repeat_breaker?: boolean; permission?: { rules?: PermissionRule[]; /** kimi-code `[permission]` key. Default true when the key is