Skip to content

Commit f17a22e

Browse files
authored
feat: turn off filesystem watch by default (#3931)
* feat: turn off filesystem watch by default * test: enable filesystem watch explicitly in watch-dependent suites
1 parent 9d07f63 commit f17a22e

12 files changed

Lines changed: 37 additions & 17 deletions

File tree

‎.changeset/watch-default-off.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@moonshot-ai/kimi-code": patch
3+
---
4+
5+
Turn off filesystem watchers for config and workspace files by default. Set `[watch] enabled` to `true` or `KIMI_CODE_WATCH=1` to turn them back on.

‎docs/en/configuration/config-files.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -474,11 +474,11 @@ Both values must be positive integers. A call's `max_chars` overrides the defaul
474474

475475
## `watch`
476476

477-
`watch` controls filesystem watchers that reload local.toml, AGENTS.md, skills, MCP config, and `config.toml` itself. It defaults to on. Set `enabled` to `false` to start with no watchers; changing the file later will not be picked up until restart.
477+
`watch` controls filesystem watchers that reload local.toml, AGENTS.md, skills, MCP config, and `config.toml` itself. It defaults to off. Set `enabled` to `true` to attach watchers; with watchers off, changing the file later will not be picked up until restart.
478478

479479
| Field | Type | Default | Description |
480480
| --- | --- | --- | --- |
481-
| `enabled` | `boolean` | `true` | Attach filesystem watchers; `false` disables every `watch()` for the process |
481+
| `enabled` | `boolean` | `false` | Attach filesystem watchers; `false` disables every `watch()` for the process |
482482

483483
`enabled` can be overridden by the `KIMI_CODE_WATCH` environment variable, which takes higher priority than `config.toml`.
484484

‎docs/en/configuration/env-vars.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ Switches that control the behavior of subsystems such as telemetry, background t
160160
| `KIMI_CODE_TUI_FULL_SCREEN` | Experimental fullscreen UI: scrollable transcript, mouse selection, clickable links, Ctrl-Shift-F search | `1` enables it; anything else keeps the regular inline UI |
161161
| `KIMI_CODE_EXPERIMENTAL_SUBAGENT_FORK` | Experimental `fork` parameter on `Agent`/`AgentSwarm`: start the subagent from a snapshot of the caller's history instead of an empty context; `KIMI_CODE_EXPERIMENTAL_FLAG=1` also enables it | Truthy: `1`/`true`/`yes`/`on`; falsy: `0`/`false`/`no`/`off` |
162162
| `KIMI_CODE_EXPERIMENTAL_TOOL_SELECT` | Experimental on-demand tool loading: tools of MCP servers marked `deferred: true` stay out of the top-level tool list and are loaded via `select_tools`; also requires the model to declare the `dynamically_loaded_tools` capability — see [MCP](../customization/mcp.md#loading-tools-on-demand) | Truthy: `1`/`true`/`yes`/`on`; falsy: `0`/`false`/`no`/`off` |
163-
| `KIMI_CODE_WATCH` | Attach filesystem watchers that reload config and workspace files; higher priority than `[watch] enabled` (default `true`) | Truthy: `1`/`true`/`yes`/`on`; falsy: `0`/`false`/`no`/`off` |
163+
| `KIMI_CODE_WATCH` | Attach filesystem watchers that reload config and workspace files; higher priority than `[watch] enabled` (default `false`) | Truthy: `1`/`true`/`yes`/`on`; falsy: `0`/`false`/`no`/`off` |
164164
| `KIMI_CODE_SEARCH_WORKER` | Run the global search index in a dedicated worker thread; higher priority than `[database] search` (default `true`) | Truthy: `1`/`true`/`yes`/`on`; falsy: `0`/`false`/`no`/`off` |
165165
| `KIMI_CODE_PERSISTENCE_MINIDB_READMODEL` | Use the minidb-backed read model for session indexing; higher priority than `[database] base` (default `true`) | Truthy: `1`/`true`/`yes`/`on`; falsy: `0`/`false`/`no`/`off` |
166166
| `KIMI_MCP_STARTUP_TIMEOUT_MS` | Global default connection timeout (ms) for MCP servers; overrides the config file, but `mcp.json` `startupTimeoutMs` still wins | Integer from `1` to `2147483647`; invalid values are ignored |

‎docs/zh/configuration/config-files.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -473,11 +473,11 @@ max_chars = 500000
473473

474474
## `watch`
475475

476-
`watch` 控制 local.toml、AGENTS.md、skills、MCP 配置以及 `config.toml` 自身的文件系统热更新。默认开启。把 `enabled` 设为 `false` 后进程内不再挂任何 watcher;之后改文件要重启才会再读。
476+
`watch` 控制 local.toml、AGENTS.md、skills、MCP 配置以及 `config.toml` 自身的文件系统热更新。默认关闭。把 `enabled` 设为 `true` 后进程内才会挂 watcher;关闭时改文件要重启才会再读。
477477

478478
| 字段 | 类型 | 默认值 | 说明 |
479479
| --- | --- | --- | --- |
480-
| `enabled` | `boolean` | `true` | 是否挂文件系统 watch;`false` 关闭进程内全部 `watch()` |
480+
| `enabled` | `boolean` | `false` | 是否挂文件系统 watch;`false` 关闭进程内全部 `watch()` |
481481

482482
`enabled` 可被环境变量 `KIMI_CODE_WATCH` 覆盖,优先级高于配置文件。
483483

‎docs/zh/configuration/env-vars.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ kimi
160160
| `KIMI_CODE_TUI_FULL_SCREEN` | 启用实验性的 fullscreen 界面:可滚动 transcript、鼠标选择、可点击链接、Ctrl-Shift-F 搜索 | `1` 开启;其他值保持常规内联界面 |
161161
| `KIMI_CODE_EXPERIMENTAL_SUBAGENT_FORK` | 在 `Agent`/`AgentSwarm` 上启用实验性 `fork` 参数:以调用方对话历史快照而非空上下文启动 subagent | 真值:`1`/`true`/`yes`/`on`;假值:`0`/`false`/`no`/`off` |
162162
| `KIMI_CODE_EXPERIMENTAL_TOOL_SELECT` | 启用实验性按需加载工具:标记 `deferred: true` 的 MCP server 工具不进入顶层工具列表,由模型经 `select_tools` 按需加载;还需模型声明 `dynamically_loaded_tools` 能力,详见 [MCP](../customization/mcp.md#按需加载工具) | 真值:`1`/`true`/`yes`/`on`;假值:`0`/`false`/`no`/`off` |
163-
| `KIMI_CODE_WATCH` | 是否挂文件系统 watch 以热更新配置和工作区文件,优先级高于 `[watch] enabled`(默认 `true`) | 真值:`1`/`true`/`yes`/`on`;假值:`0`/`false`/`no`/`off` |
163+
| `KIMI_CODE_WATCH` | 是否挂文件系统 watch 以热更新配置和工作区文件,优先级高于 `[watch] enabled`(默认 `false`) | 真值:`1`/`true`/`yes`/`on`;假值:`0`/`false`/`no`/`off` |
164164
| `KIMI_CODE_SEARCH_WORKER` | 在独立 worker 线程中运行全局搜索索引,优先级高于 `[database] search`(默认 `true`) | 真值:`1`/`true`/`yes`/`on`;假值:`0`/`false`/`no`/`off` |
165165
| `KIMI_CODE_PERSISTENCE_MINIDB_READMODEL` | 会话索引使用基于 minidb 的读模型,优先级高于 `[database] base`(默认 `true`) | 真值:`1`/`true`/`yes`/`on`;假值:`0`/`false`/`no`/`off` |
166166
| `KIMI_MCP_STARTUP_TIMEOUT_MS` | MCP server 全局默认连接超时(毫秒);优先级高于配置文件,低于 `mcp.json` 的 `startupTimeoutMs` | `1` 到 `2147483647` 的整数;非法值被忽略 |

‎packages/agent-core-v2/src/app/config/configService.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,7 @@ export class ConfigService extends Disposable implements IConfigService {
681681
}
682682

683683
private applyWatchEnabled(): void {
684-
setWatchEnabled(this.get<WatchConfig | undefined>(WATCH_SECTION)?.enabled ?? true);
684+
setWatchEnabled(this.get<WatchConfig | undefined>(WATCH_SECTION)?.enabled ?? false);
685685
}
686686

687687
private deliveredValue(domain: string): unknown {

‎packages/agent-core-v2/src/human/test/utils/watch.test.ts‎

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { mkdtemp, mkdir, realpath, rm, symlink, writeFile } from 'node:fs/promis
44
import { tmpdir } from 'node:os';
55
import { basename, join } from 'node:path';
66

7-
import { afterEach, describe, expect, it } from 'vitest';
7+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
88

99
import {
1010
createWatchService,
@@ -20,6 +20,14 @@ const wait = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms)
2020
const longTempDir = (prefix: string): Promise<string> =>
2121
mkdtemp(join(realpathSync.native(tmpdir()), prefix));
2222

23+
beforeEach(() => {
24+
setWatchEnabled(true);
25+
});
26+
27+
afterEach(() => {
28+
setWatchEnabled(false);
29+
});
30+
2331
class TestNativeWatcher {
2432
private errorListener: ((error: NodeJS.ErrnoException) => void) | undefined;
2533
closed = false;
@@ -469,14 +477,10 @@ describe('watch chokidar mode', () => {
469477
it('does not start a filesystem watch when watch is disabled', async () => {
470478
root = await mkdtemp(join(tmpdir(), 'watch-disabled-'));
471479
setWatchEnabled(false);
472-
try {
473-
const events = await start();
474-
await writeFile(join(root, 'a.txt'), 'x');
475-
await wait(300);
476-
expect(events).toHaveLength(0);
477-
} finally {
478-
setWatchEnabled(true);
479-
}
480+
const events = await start();
481+
await writeFile(join(root, 'a.txt'), 'x');
482+
await wait(300);
483+
expect(events).toHaveLength(0);
480484
});
481485

482486
it('stops firing after the handle is disposed', async () => {

‎packages/agent-core-v2/src/human/utils/watch.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,7 @@ export const WATCH_ENV = 'KIMI_CODE_WATCH';
513513
const TRUE_WATCH_ENV = new Set(['1', 'true', 'yes', 'on']);
514514
const FALSE_WATCH_ENV = new Set(['0', 'false', 'no', 'off']);
515515

516-
let watchEnabledFromConfig = true;
516+
let watchEnabledFromConfig = false;
517517

518518
export function setWatchEnabled(enabled: boolean): void {
519519
watchEnabledFromConfig = enabled;

‎packages/agent-core-v2/test/app/workspaceAliases/workspaceAliasesService.test.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
} from '#/app/workspace/workspacePersistence';
3333
import { IWorkspaceAliases } from '#/app/workspaceAliases/workspaceAliases';
3434
import { WorkspaceAliasesService } from '#/app/workspaceAliases/workspaceAliasesService';
35+
import { setWatchEnabled } from '#human/utils/watch';
3536
import { stubBootstrap } from '../bootstrap/stubs';
3637

3738
interface SessionIndexLine {
@@ -68,9 +69,11 @@ describe('WorkspaceAliasesService (file-backed)', () => {
6869
'workspaceAliases',
6970
);
7071
homeDir = await fsp.mkdtemp(join(os.tmpdir(), 'ws-aliases-'));
72+
setWatchEnabled(true);
7173
});
7274

7375
afterEach(async () => {
76+
setWatchEnabled(false);
7477
currentHost?.dispose();
7578
currentHost = undefined;
7679
await fsp.rm(homeDir, { recursive: true, force: true });

‎packages/agent-core-v2/test/workspace/workspaceAgentProfileLoader/agentProfileLoader.test.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ import { IWorkspaceAgentProfileLoader } from '#/workspace/workspaceAgentProfileL
4646
import { IExtraAgentProfileLoader } from '#/workspace/workspaceAgentProfileLoader/extraAgentProfileLoader';
4747
import { IExplicitAgentProfileLoader } from '#/workspace/workspaceAgentProfileLoader/explicitAgentProfileLoader';
4848

49+
import { setWatchEnabled } from '#human/utils/watch';
50+
4951
import { stubBootstrap } from '../../app/bootstrap/stubs';
5052

5153
const watchMockState = vi.hoisted(() => ({ mode: 'inert' as 'inert' | 'real' }));
@@ -334,6 +336,7 @@ async function withStack(
334336
describe('agent profile loaders + session catalog', () => {
335337
beforeEach(() => {
336338
watchMockState.mode = 'inert';
339+
setWatchEnabled(false);
337340
_clearAgentProfileContributionsForTests();
338341
const builtinDefault: AgentProfile = normalizeAgentProfile({
339342
name: DEFAULT_AGENT_PROFILE_NAME,
@@ -757,6 +760,7 @@ describe('agent profile loaders + session catalog', () => {
757760

758761
it('rescans the workspace source when a project agent file changes on disk', async () => {
759762
watchMockState.mode = 'real';
763+
setWatchEnabled(true);
760764
await withFixture(async (fixture) => {
761765
await mkdir(join(fixture.workDir, '.kimi-code', 'agents'), { recursive: true });
762766
await withStack(fixture, undefined, async (stack) => {

0 commit comments

Comments
 (0)