Per review: CJK and legacy single-byte encodings already passed the old
control-char heuristic (their bytes are all >= 0x80, never NUL), so the
full Encoding.detect pass in isBinaryFile was solving a non-problem for
them. The only realistic regression in the old heuristic is UTF-16 with
BOM, where the second byte of every ASCII character is 0x00 and fires
the NUL-byte early-return.
Replace the detect call with a 2-byte BOM check via a new
Encoding.hasUtf16Bom helper. Also reword the move/add inline markers in
patch/index.ts to note that Encoding.write handles mkdir.
- Encoding.detect: lowercase-compare UTF-32 rejection so jschardet's
uppercase 'UTF-32'/'UTF-32BE'/'UTF-32LE' labels no longer slip through
(iconv-lite happens to have utf-32 codecs that would then be accepted).
- Encoding.read: drop redundant Buffer.from wrap around readFile's
already-Buffer return value.
- EncodedIO: switch to Effect.tryPromise so I/O failures surface as
typed errors that .pipe(Effect.catch(...)) can recover from; the
apply_patch delete branch relies on this to translate read errors
into 'apply_patch verification failed'.
- Shrink kilocode_change blocks in edit.ts, write.ts, apply_patch.ts,
patch/index.ts, and read.ts to per-line inline markers where each
block was only wrapping 1-3 changed lines.
- Collapse the 'if (exists) { let contentOld; let encoding; ... }'
blocks in edit.ts and write.ts into a single ternary + destructure
that keeps all three lines self-contained and each independently
annotated.
- Remove the unused 'latin1' sample and fix the 'providEncoded' typo
in the encoding integration test file.
- Strengthen the apply_patch Shift_JIS test to assert exact bytes
instead of the weak 'does not contain these two UTF-8 bytes' check.
- Drop the stray .kilo/ and .kilocode/ lockfiles that were re-added
by an upstream 'wip' commit.
- Wire Readable.from through a named `stream` variable so the `lines`
helper can destroy it in the finally block, matching the previous
createReadStream pattern.
- Restore the 'Create parent directories', 'Handle file move', 'Regular
update', 'Read original file content', and 'For delete, we need to read
the current content' comments that were dropped when switching to
Encoding.write / Encoding.read. The explicit fs.mkdir calls are no
longer needed because Encoding.write mkdirs recursively, but the intent
comments still apply.
iconv-lite's utf-8 codec strips any leading BOM on decode and never emits
one on encode, so files that started with EF BB BF would lose their BOM
after an edit. Track UTF-8-with-BOM as a distinct synthetic encoding label
and re-prepend the BOM bytes on write so the round-trip keeps the file
byte-identical (modulo any actual edits).
- Restore the streaming createInterface loop in the `lines` helper so the
diff against main stays small; decode bytes with iconv-lite up front and
feed the resulting text through Readable.from.
- Drop 'new file' kilocode_change markers from files under kilocode/
directories (those paths are exempt from the annotation check).
- Tighten the changeset copy to sound like a user-facing release note.
Exercise Read/Write/Edit/ApplyPatch tools directly against files in
UTF-8, UTF-16 LE/BE (with BOM), Shift_JIS, EUC-JP, GB2312, Big5, EUC-KR,
Windows-1251, and KOI8-R to verify the tools decode input correctly and
round-trip bytes back to the original encoding. Also ensure UTF-16
BOMs survive the write pipeline.
Drop manual BOM parsing, UTF-32, and UTF-16-without-BOM detection: rely on
TextDecoder + jschardet + iconv-lite which already handle BOM round-tripping
for UTF-16 LE/BE. Move the encoding helpers and Effect wrappers into
packages/opencode/src/kilocode/ so shared tool files only carry targeted
kilocode_change markers.
Mark new plan follow-up sessions busy as soon as the tab opens so the UI
shows work is still in progress while the handover summary is generated.
Clear the temporary busy state if that pre-loop handover phase is aborted
or fails before the normal prompt loop takes over.
Show the plan text right away in the new session tab without waiting
for the handover LLM call. The message part is created with plan+todos
as soon as the session opens; the handover section is upserted onto the
same part once the slow LLM call resolves.
Create the follow-up session before running the handover LLM call so
the extension's pendingFollowup SSE gate fires inside its 30s TTL. The
handover and todos are now injected into the already-live session
after it resolves, instead of blocking session creation.
When the plan agent pauses for the follow-up question, CLI users had no
way to send a free-text reply: the main prompt input is hidden while a
blocking question is active, and the "Type your own answer" row was
being forced off to avoid duplicating the VS Code prompt input. CLI now
shows the custom-answer row again, while VS Code keeps it hidden.
The prompt queue's scope() hid the user message injected by
PlanFollowup.inject() because its ID was newer than the queue target,
so the loop saw the same plan_exit messages and re-asked the question
in an infinite cycle. Retarget the queue after inject so the new
message is visible on the next iteration.
Restore custom: false on the plan follow-up question — the "Type your own answer" row was redundant because the main prompt already routes typed text as a question reply. Auto-submit single-question single-select option picks in the VS Code QuestionDock so the button behaves like the TUI instead of silently waiting for a second Submit click.
Add optional `labelKey`/`descriptionKey` to Question.Option and `questionKey`/`headerKey` to Question.Info, annotated with `kilocode_change` markers. Populate these keys from the plan follow-up so the "Ready to implement?" question, "Start new session", and "Continue here" buttons render in the sidebar language while the canonical English labels remain on the reply wire (unchanged server-side matching).
* fix: persist custom provider model and variant deletions
The CLI config.update endpoint deep-merges its payload with existing
global config, so removing a model or reasoning variant from a custom
provider in the UI had no effect on disk. Save payloads now emit null
sentinels for removed IDs, and the Provider schema accepts nullable
record values so stripNulls can delete them during the merge.
Closes#9186
* chore: update kilo-vscode visual regression baselines
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Restore custom: false on the plan follow-up question — the "Type your own answer" row was redundant because the main prompt already routes typed text as a question reply. Auto-submit single-question single-select option picks in the VS Code QuestionDock so the button behaves like the TUI instead of silently waiting for a second Submit click.
Lock in that a session with an open suggestion reports as idle (so reopening VS Code or switching worktrees does not show it stuck/running), that accepting flips it back to busy without an idle flash, and that a dismissed suggestion leaves it idle for the run loop to resume cleanly.
Move worktree-scoped gitdir construction, ACP guard logic, and diff
caching directly into the core snapshot service, eliminating the
separate kilocode/snapshot module. This reduces indirection and keeps
all snapshot concerns co-located in a single file.
If deriveAndSyncStatus hangs, the per-session promise chain would never
drain and map entries would accumulate. Wrap the derive with a 3s timeout
so stuck work fails fast, is logged, and the existing cleanup runs.