Skip to content

fix(cli): fix hanging sessions on huge-file diffs#9119

Merged
alex-alecu merged 20 commits intomainfrom
fix/snapshot-diff-freeze
Apr 22, 2026
Merged

fix(cli): fix hanging sessions on huge-file diffs#9119
alex-alecu merged 20 commits intomainfrom
fix/snapshot-diff-freeze

Conversation

@alex-alecu
Copy link
Copy Markdown
Contributor

@alex-alecu alex-alecu commented Apr 17, 2026

Why

The CLI could appear to lock up after a turn — typing would stop responding, ESC would not interrupt, and live updates would freeze. This happened when a file you edited was very large (tens of thousands of lines). On top of that, the file diff preview for large files came back empty, so you could not see what actually changed.

What changed

Diffs are now produced by git itself instead of an in-process JavaScript diff algorithm. The session stays responsive on edits of any size, and the full diff of large files is visible in the TUI, VS Code sidebar, and web UI instead of showing an empty patch.

Windows safety

This remains Windows-safe because diff generation is still delegated to git, the active callers pass normalized slash-separated paths instead of platform-specific separators, and subprocess spawning already uses the existing Windows-safe handling that hides console windows; Linux behavior is unchanged for the same reason.

Non-git folders

In folders without a git repository, this change is a no-op: diff generation is skipped and the UI still returns plain file contents with no patch, so there is no new failure mode or behavior change outside git-backed projects.

How to test

  1. Run bun dev
  2. Give this prompt:
Run `awk 'BEGIN { for (i=0; i<15000; i++) print "v1_line_"i"_payload" }' > freeze.txt` , add freeze.txt to staged and then run `awk 'BEGIN { for (i=0; i<15000; i++) print "v2_line_"i"_payload" }' > freeze.txt`

This rewrites every line of freeze.txt between snapshots, which is the worst case for the old diff implementation.

On stable v7.2.10 the TUI locks for ~28s after the assistant says "Done" — during that window typing and ESC are dead, and the file preview for freeze.txt comes back empty.

On this branch the TUI stays live and the freeze.txt preview shows the full diff.

Screen.Recording.2026-04-17.at.17.16.48.mov

Large snapshot diffs (tens of thousands of lines) ran the Myers algorithm synchronously on the TUI worker thread, starving the abort endpoint and SSE heartbeats. Cap inputs, offload structuredPatch to a dedicated worker, and race the session summary against a timeout + cancel signal so ESC actually stops the work.
Copy link
Copy Markdown
Collaborator

@marius-kilocode marius-kilocode left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its quite a complicated change for a seemingly small fix. Are you sure the complexity is worth it?

Comment thread packages/opencode/src/kilocode/snapshot/diff-engine.ts Outdated
Comment thread packages/opencode/src/session/summary.ts Outdated
Comment thread packages/opencode/src/snapshot/index.ts Outdated
Comment thread packages/opencode/test/kilocode/snapshot-freeze-repro.test.ts Outdated
Comment thread packages/opencode/src/session/summary.ts Outdated
Comment thread packages/opencode/src/kilocode/snapshot/diff-engine.ts Outdated
Comment thread packages/opencode/src/kilocode/snapshot/diff-engine.ts Outdated
Comment thread packages/opencode/src/kilocode/session/summary-dispatch.ts Outdated
@kilo-code-bot
Copy link
Copy Markdown
Contributor

kilo-code-bot Bot commented Apr 17, 2026

Code Review Summary

Status: 1 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0

Fix these issues in Kilo Cloud

Issue Details (click to expand)

WARNING

File Line Issue
None N/A No new inline-commentable issues found in the current incremental diff.
Other Observations (not in diff)

Issues found in unchanged code that cannot receive inline comments:

File Line Issue
packages/server/src/definition/question.ts 11 The shared HttpApi question schema still omits labelKey, descriptionKey, questionKey, headerKey, and blocking, but makeQuestionHandler() decodes live Question.Request payloads through this narrower schema. Clients using the experimental question HttpApi can therefore lose translated labels/text and non-blocking question metadata when listing pending questions.
Files Reviewed (2 files)
  • packages/opencode/src/file/index.ts - 0 issues
  • packages/opencode/src/snapshot/index.ts - 0 issues

Reviewed by gpt-5.4-20260305 · 1,440,918 tokens

@alex-alecu
Copy link
Copy Markdown
Contributor Author

alex-alecu commented Apr 17, 2026

Its quite a complicated change for a seemingly small fix. Are you sure the complexity is worth it?

@marius-kilocode

Core fix (what actually addresses the freeze)

  • src/snapshot/index.ts (+9/-22) — skip/cap logic for huge diffs
  • src/session/summary.ts (+11/-2) — timeout + abort bridge
  • src/session/prompt.ts (+4/-0) — ESC wiring

That's the trivial fix.

Everything else — added in the name of doing it "properly":

  1. Worker offload infrastructure — moving medium diffs off the main loop. This is a real feature, not a fix. It's the bulk of the diff.
  2. Kilocode extraction — pulled out of shared opencode files into kilocode/ dirs to minimize merge conflicts with upstream (per AGENTS.md).
  3. Tests — repro test, caps test, timeout test, engine test.
  4. SDK regen (types.gen.ts, openapi.json together +494/-267) — auto-generated from a server route change, probably a new endpoint for the warning toast or diff status.

@alex-alecu alex-alecu changed the title fix(cli): prevent freeze on huge-file diffs fix(cli): fix hanging sessions on huge-file diffs Apr 17, 2026
Merge origin/main into fix/snapshot-diff-freeze.

Conflicts resolved:
- packages/opencode/script/build.ts: keep kilocode diff-worker entrypoint
  alongside upstream rgPath ripgrep worker.
- packages/opencode/src/file/index.ts: port kilocode DiffEngine.shouldSkip
  guard onto upstream's effect-based gitText/git.show flow.
- packages/opencode/src/project/vcs.ts: accept upstream rename ServiceMap ->
  Context; drop unused structuredPatch/formatPatch imports (kilocode routes
  through DiffEngine.patchAsync).
- packages/opencode/src/session/summary.ts: keep kilocode SummaryDispatch for
  tracked summarize/cancel alongside upstream Layer.suspend defaultLayer.
- packages/sdk/openapi.json: take origin/main (no kilocode server changes).
Revert the worker pool, dispatcher, flags, and related scaffolding added
on top of the initial cap guard. Post-#9046, the only remaining CLI
callers of Vcs.diff are one-shot review opens, so the worker offload is
no longer needed. The freeze repro is eliminated by the input caps in
DiffEngine.shouldSkip alone.

Shrinks the fork diff from 28 files (+1063) to 6 files (+199) and
reduces shared opencode files with kilocode_change markers to 2.
@alex-alecu
Copy link
Copy Markdown
Contributor Author

@marius-kilocode I have removed the migration of git diff to another thread - that should happen in upstream OpenCode. I kept only skipping files too big from the diff - targeted fix with minimal code. Please check again.

# Conflicts:
#	packages/opencode/src/snapshot/index.ts
@alex-alecu alex-alecu enabled auto-merge April 20, 2026 12:56
@alex-alecu alex-alecu disabled auto-merge April 20, 2026 12:56
Comment thread packages/opencode/src/kilocode/snapshot/diff-engine.ts Outdated
Comment thread packages/opencode/src/kilocode/snapshot/diff-engine.ts Outdated
Comment thread packages/opencode/src/snapshot/index.ts Outdated
Files over a few thousand lines no longer hang the session for minutes during
summary generation or file view. Diffs for files of any size now render with
full content in the TUI, VS Code sidebar, and web UI — previously they either
froze the event loop or were reported with empty patch text.
The git-based diff path has been reliable across all tests and in production since the TUI-freeze fix shipped. Removing the JS Myers fallback eliminates ~220 lines of belt-and-suspenders code (a size-cap guard, a git cat-file blob loader, and a per-file git show fallback). If git ever fails now, callers emit an empty patch string; additions/deletions from git --numstat still come through unchanged.
The previous commit deleted ~150 lines of upstream OpenCode diff code because the git-based path always runs first. Restoring those lines as dead code (behind an early-return kilocode_change block) keeps our diff from upstream minimal, so future OpenCode syncs to the hot diffFull code path don't conflict. No runtime behavior change — the git-based DiffFull path still short-circuits before the restored Myers loop executes.
@alex-alecu alex-alecu merged commit 3bbe667 into main Apr 22, 2026
31 checks passed
@alex-alecu alex-alecu deleted the fix/snapshot-diff-freeze branch April 22, 2026 13:29
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.

2 participants