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
6 changes: 6 additions & 0 deletions apps/kimi-code/test/cli/goal-prompt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,11 @@ describe('runPrompt headless goal mode', () => {
let savedExitCode: typeof process.exitCode;

beforeEach(() => {
// Pin the experimental engine flag off so runPrompt stays on the v1 path
// this suite mocks, regardless of the host environment (matches
// run-prompt.test.ts). With the flag on, runPrompt dispatches to the
// native v2 runner, which ignores these mocks and hangs the test.
vi.stubEnv('KIMI_CODE_EXPERIMENTAL_FLAG', '');
savedExitCode = process.exitCode;
mocks.experimentalFeatures = [{ id: 'micro_compaction', enabled: true }];
mocks.sessions = [];
Expand All @@ -180,6 +185,7 @@ describe('runPrompt headless goal mode', () => {
});

afterEach(() => {
vi.unstubAllEnvs();
process.exitCode = savedExitCode;
});

Expand Down
4 changes: 4 additions & 0 deletions apps/kimi-code/test/cli/update/preflight.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,10 @@ async function flushBackgroundInstall(): Promise<void> {

describe('runUpdatePreflight', () => {
beforeEach(() => {
// Pin the experimental flag off so rollout gating is deterministic
// regardless of the host environment (the flag bypasses batch holds).
// Tests that exercise the bypass opt back in with `vi.stubEnv(..., '1')`.
vi.stubEnv('KIMI_CODE_EXPERIMENTAL_FLAG', '');
mocks.readUpdateInstallState.mockResolvedValue(emptyUpdateInstallState());
mocks.writeUpdateInstallState.mockResolvedValue(undefined);
mocks.loadTuiConfig.mockResolvedValue(tuiConfig());
Expand Down
30 changes: 25 additions & 5 deletions packages/agent-core-v2/src/session/sessionFs/fsWatchService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,16 @@ import type { FsChangeEntry, FsChangeEvent } from '@moonshot-ai/protocol';

import { ISessionFsWatchService } from './fsWatch';

const DEBOUNCE_MS = 200;
const MAX_CHANGES_PER_WINDOW = 500;
const DEFAULT_DEBOUNCE_MS = 200;
const DEFAULT_MAX_CHANGES_PER_WINDOW = 500;

/** Positive-int env read for the test-only window overrides below. */
function readPositiveIntEnv(name: string, fallback: number): number {
const raw = process.env[name];
if (raw === undefined || raw === '') return fallback;
const n = Number.parseInt(raw, 10);
return Number.isFinite(n) && n > 0 ? n : fallback;
}

export class SessionFsWatchService extends Disposable implements ISessionFsWatchService {
declare readonly _serviceBrand: undefined;
Expand All @@ -48,6 +56,18 @@ export class SessionFsWatchService extends Disposable implements ISessionFsWatch
private rawCount = 0;
private truncated = false;

// Env-overridable for tests: the burst-truncation e2e cannot rely on
// chokidar delivering >500 events inside one 200ms window under CPU
// contention. Production leaves both unset and gets the defaults.
private readonly debounceMs = readPositiveIntEnv(
'KIMI_CODE_FS_WATCH_DEBOUNCE_MS',
DEFAULT_DEBOUNCE_MS,
);
private readonly maxChangesPerWindow = readPositiveIntEnv(
'KIMI_CODE_FS_WATCH_MAX_CHANGES_PER_WINDOW',
DEFAULT_MAX_CHANGES_PER_WINDOW,
);

/** Always present; starts with `.git/` and is augmented with `.gitignore` once loaded. */
private readonly matcher: Ignore = ignore().add('.git/');
private gitignoreLoaded = false;
Expand Down Expand Up @@ -116,12 +136,12 @@ export class SessionFsWatchService extends Disposable implements ISessionFsWatch

this.pending.push({ path: rel, change: e.action, kind: e.kind });
this.rawCount += 1;
if (this.pending.length > MAX_CHANGES_PER_WINDOW) {
if (this.pending.length > this.maxChangesPerWindow) {
this.truncated = true;
this.pending = [];
}
if (this.debounceTimer === undefined) {
const timer = setTimeout(() => this.flush(), DEBOUNCE_MS);
const timer = setTimeout(() => this.flush(), this.debounceMs);
timer.unref?.();
this.debounceTimer = timer;
}
Expand All @@ -139,7 +159,7 @@ export class SessionFsWatchService extends Disposable implements ISessionFsWatch

const event: FsChangeEvent = {
changes,
coalesced_window_ms: DEBOUNCE_MS,
coalesced_window_ms: this.debounceMs,
...(truncated ? { truncated: true, count } : {}),
};
this.emitter.fire(event);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ describe('runHook process runner', () => {
hostProcess,
nodeCommand('setTimeout(() => {}, 10000);'),
{ tool_name: 'Bash' },
{ timeout: 1 },
{ timeout: 0.05 },
);

expect(result.action).toBe('allow');
Expand Down
Loading
Loading