Skip to content

fix(vscode): avoid eager worktree watchers - #12593

Merged
marius-kilocode merged 2 commits into
mainfrom
profile-active-kilo-sessions-cpu-usage
Jul 28, 2026
Merged

fix(vscode): avoid eager worktree watchers#12593
marius-kilocode merged 2 commits into
mainfrom
profile-active-kilo-sessions-cpu-usage

Conversation

@marius-kilocode

@marius-kilocode marius-kilocode commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Related Issue And Origin

Related resource report: #8941

This PR addresses a concrete watcher fanout discovered while investigating the same class of excessive background CPU and memory use. It does not close #8941 because that report was filed on Windows in April 2026, predates the regression described here, and FFF is disabled by default on Windows. The issue remains relevant as the broader user-facing symptom and should retain its independent investigation.

The regression came from the interaction of two otherwise separate changes:

  1. OpenCode PR #27802 introduced FFF-backed file search in June 2026. Kilo imported it through OpenCode v1.17.4 #12204 with OpenCode v1.17.4. FFF improves file lookup by maintaining a repository index, but each initialized location also owns native filesystem and Git-status watcher threads.
  2. [Regression] CLI sidebar no longer shows correct branch #12225 reported that the standalone CLI sidebar branch label stopped updating after an external git switch. fix: keep CLI sidebar branch label in sync with git #12314 fixed that regression by making KilocodeWatcher eagerly build and retain the complete v2 LocationServiceMap for every loaded CLI instance so the .git/HEAD watcher would publish vcs.branch.updated.

That tradeoff is appropriate for the standalone TUI, which consumes branch-update events. It was unintentionally applied to the VS Code-managed backend as well. Agent Manager restores many directories into one shared backend, so the eager warmup converted one branch-label fix into one complete FFF index and watcher set per restored worktree. The performance regression therefore appeared after #12314, with the FFF dependency supplied by #12204.

Problem

Agent Manager restores many worktree and session directories into one shared kilo serve backend. Each restored directory currently initializes KilocodeWatcher, which eagerly builds and retains the complete v2 location stack even when no v2 filesystem feature has been requested for that directory.

That eager stack includes FileSystemSearch. On macOS and Linux, FileSystemSearch selects FFF by default, and FFF creates a repository index plus native filesystem and Git-status watcher threads. The resulting cost scales with every restored Agent Manager worktree rather than with the worktrees that are actively using file search.

In the affected production backend, 36 retained worktree locations produced 36 fff-watcher-own threads, matching FSEvents and debouncer activity, and 637 open file descriptors. CPU reached 140 to 161 percent and memory reached 3 to 4 GB. Native samples repeatedly ended in GitStatusCache::git_status_for_paths, git_status_list_new, and git_diff_index_to_workdir, showing that the background indexes were continuously traversing worktrees for Git status.

The scaling was consistent across control processes:

Retained FFF locations CPU Memory
1 about 0.7% about 450 MB
8 about 10% about 1.1 GB
36 140 to 161% 3 to 4 GB

Root Cause

KilocodeWatcher was introduced specifically to restore standalone CLI branch-label synchronization. Its implementation warms the entire location layer because the v2 watcher service lives inside that layer. The warmup is keyed by directory and retained for the instance lifetime.

That location layer contains substantially more than .git/HEAD monitoring. It also constructs filesystem search, plugins, catalogs, and tool registries. Constructing filesystem search selects FFF, and constructing FFF immediately creates its index and background watcher. The branch-label dependency therefore pulled the complete filesystem indexing stack into every loaded instance.

The VS Code extension shares one backend across the sidebar, editor tabs, and Agent Manager. Agent Manager can restore dozens of directory-scoped sessions into that process, so the per-instance warmup scaled linearly with historical and active worktrees. FFF was initialized even when the user never opened file mentions or another filesystem feature in those worktrees.

The VS Code extension does not consume vcs.branch.updated. Agent Manager obtains branch names, worktree status, diffs, and pull request state through extension-host Git operations and polling. The eager branch watcher therefore paid a substantial ongoing cost without serving the VS Code worktree UI.

Change

KilocodeWatcher now skips only its eager location-stack warmup when the existing runtime client identity is KILO_CLIENT=vscode. Other clients retain the current behavior.

This is intentionally narrower than disabling Core file watching or FFF globally:

  • FFF remains enabled for the managed VS Code backend.
  • The @ file and folder picker still uses the indexed FFF implementation.
  • A location stack can still be created on demand when a real feature requests it.
  • The FFF index remains live after creation, so newly created, deleted, or renamed files continue updating in mention results.
  • The standalone CLI continues eagerly watching .git/HEAD, preserving external branch-switch updates in the TUI.
  • The existing explicit file-watcher disable flag continues to work independently.

The implementation and tests live in Kilo-owned kilocode paths. No shared upstream Core filesystem files are changed, which minimizes future OpenCode merge conflicts.

Measured Improvement

A controlled VS Code self-test restored the same Agent Manager state before and after suppressing the eager warmup. Before any explicit file search, the resource profile changed as follows:

Metric Eager warmup VS Code lazy warmup Improvement
FFF watcher threads 19 1 18 fewer, 95% reduction
Total backend threads 89 31 58 fewer, 65% reduction
Resident memory about 1.6 GB about 270 MB about 1.3 GB lower, 83% reduction
Repeated Git-status hot stack Present Absent Removed for unloaded worktrees

The remaining initial watcher belonged to a location actually requested by the loaded VS Code surfaces rather than to every restored worktree.

The same run then selected an Agent Manager worktree and queried @server-manager:

  • Expected file and folder results were displayed.
  • FFF remained the active search implementation.
  • The selected worktree created one additional on-demand watcher.
  • The total increased from 1 to 2 watchers, not back to all 19 restored locations.
  • The backend settled at 34 threads and about 679 MB while the two requested indexes were resident.

This changes resource growth from proportional to every restored Agent Manager worktree to proportional to locations that actually request the v2 location stack. For the original 36-location production case, it removes the source of the 36-way background Git-status fanout rather than merely reducing its polling frequency.

Preserved VS Code Behavior

Agent Manager worktree behavior remains pull-based:

  • Branch and repository metadata continue to come from WorktreeManager and GitOps.
  • Git statistics and diffs continue to refresh through GitStatsPoller and the extension diff controllers.
  • Pull request state continues to use the existing PR polling path.
  • Backend file.watcher.updated and vcs.branch.updated events have no current VS Code or Agent Manager consumer.

Other Kilo features remain on independent implementations:

  • Agent glob and grep tools continue using Ripgrep directly.
  • Agent read, write, edit, and patch behavior is unchanged. Synthetic edit events are still published.
  • Optional semantic codebase indexing retains its separate Chokidar watcher and incremental refresh behavior.
  • Inline, chat, and next-edit autocomplete retain their VS Code document and workspace listeners.
  • FFF ranking, frecency, and live file-catalog behavior remain available for @ mentions.

Standalone CLI And Release Deployment

The standalone CLI should not disable this watcher globally because its TUI is the feature that consumes branch-update events. The runtime gate defaults to eager behavior for cli, run, separately launched servers, and unspecified clients. Only vscode skips the unconditional warmup.

The change is included in both @kilocode/cli and kilo-code patch changesets. A VS Code extension release builds and packages the CLI binary from the same repository revision, then launches that bundled binary with the existing KILO_CLIENT=vscode environment identity. No compile-time feature flag, user setting, or deployment-specific environment configuration is required. Standalone CLI releases include the same code but do not activate the VS Code branch.

Scope

This does not attempt to remove every file watcher from Kilo. Watchers that serve an active user feature remain available and are created on demand. In particular, enabled semantic indexing keeps its own watcher, and an FFF-backed file picker keeps the selected location index current. The change removes the unused eager watcher fanout caused solely by restoring Agent Manager worktrees.

Comment thread packages/opencode/src/kilocode/watcher.ts Outdated
@kilo-code-bot

kilo-code-bot Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

The latest commit (8f4b146) addresses the prior suggestion by defaulting eager()'s parameter to Flag.KILO_CLIENT instead of raw process.env["KILO_CLIENT"], matching the pattern used elsewhere in packages/opencode/src/kilocode/. No new issues found in the incremental diff.

Files Reviewed (3 files)
  • .changeset/quiet-vscode-watchers.md
  • packages/opencode/src/kilocode/watcher.ts
  • packages/opencode/test/kilocode/instance-vcs-watcher.test.ts
Previous Review Summary (commit 160b066)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 160b066)

Status: 1 Issue Found | Recommendation: Merge (optional cleanup)

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1
Issue Details (click to expand)

SUGGESTION

File Line Issue
packages/opencode/src/kilocode/watcher.ts 18 Default eager()'s parameter to Flag.KILO_CLIENT (already imported here) instead of raw process.env["KILO_CLIENT"], for consistency with every other client check in packages/opencode/src/kilocode/
Files Reviewed (3 files)
  • .changeset/quiet-vscode-watchers.md
  • packages/opencode/src/kilocode/watcher.ts - 1 issue
  • packages/opencode/test/kilocode/instance-vcs-watcher.test.ts

The gating logic in defaultLayer (!eager() || flag) correctly skips building the LocationServiceMap layer for VS Code while preserving eager branch-watching for the standalone CLI and other clients. The change is confined to packages/opencode/src/kilocode/, so no kilocode_change markers are needed, and a changeset is included. Tests cover both eager() outcomes.

Fix these issues in Kilo Cloud


Reviewed by claude-sonnet-5 · Input: 28 · Output: 3K · Cached: 598.1K

Review guidance: REVIEW.md from base branch main

@marius-kilocode
marius-kilocode merged commit fb5f5ae into main Jul 28, 2026
30 checks passed
@marius-kilocode
marius-kilocode deleted the profile-active-kilo-sessions-cpu-usage branch July 28, 2026 11:05
@josephcrowell

Copy link
Copy Markdown

@marius-kilocode You're a legend. Thanks

marius-kilocode added a commit that referenced this pull request Jul 29, 2026
The indexing-worker disable did not deflake it: the same unhandled
EBADF/Invalid handle recurred on the next run with indexing logs absent.
The actual native fd source is the PTY connect handler, which wraps even
the 404 existence check in locations.get(Location.Ref.make(dir)) and thus
builds the full v2 location stack for the throwaway git tmpdir, spawning
FFF native index and git-status watcher threads on it (the fanout measured
in #12593). Instance disposal awaits locations.invalidate, but FFF's
native destroy signals its threads instead of joining them, so native
teardown races the fixture's rm -rf and surfaces EBADF on Linux and
invalid handle on Windows as an unhandled error between tests. The flake
first appeared on main with the v1.17.9 merge, which reworked core pty
and this handler.

KILO_EXPERIMENTAL_DISABLE_FILEWATCHER (set for unit jobs) does not gate
FFF; KILO_DISABLE_FFF does. The test asserts auth gating and a 404, which
do not involve file search, so select the ripgrep search layer for this
file.
t7tran pushed a commit to t7tran/kilocode that referenced this pull request Aug 14, 2026
…essions-cpu-usage

fix(vscode): avoid eager worktree watchers
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

actually, kilo code is consuming way more resources than it should, cause of background process.

3 participants