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