Handle tab-close paths where the shell dies without forwarding SIGHUP/SIGTERM, which left `bun run ...` alive as an orphan. Add a parent-liveness watchdog in the TUI thread and route shutdown through a single idempotent `shutdownAndExit` path. Also guard the normal `process.exit(0)` path so signal/parent-exit codes are not overridden.
* fix: resolve diff viewer race condition and add diagnostics for Agent Manager
onRequestWorktreeDiff now awaits stateReady before resolving the diff
target, preventing a race where startDiffWatch fires before state loads
from disk. Added diagnostic logging to resolveDiffTarget, diff polling,
and the server-side /experimental/worktree/diff endpoint so silent
failures become visible in the output channel.
* fix: guard stateReady await so rejection doesn't break diff polling
* docs: clarify why .catch() is required on stateReady await
The normalizeMessages function in transform.ts was missing a block that
normalizes tool call IDs for Claude models. This block exists in the
upstream OpenCode repo but was inadvertently removed in our fork.
The normalization replaces any characters that are not alphanumeric,
underscore, or hyphen with underscores in toolCallId fields. This
prevents Claude from rejecting messages with tool call IDs containing
invalid characters.
Wrap originalClose() and child-process cleanup in try/finally so that
delete processes[key] always executes. Previously, if originalClose()
threw, the process map entry would be left stale, risking incorrect
PID references in later lifecycle operations.
The setTimeout fallback in terminateWorker can keep the event loop open
even when the worker exits promptly, delaying shutdown paths triggered
by signals. Unref the timer so normal fast shutdown is not blocked by
this fallback.
* perf: filter tool diagnostics to only edited files
Tools (edit, write, apply_patch) stored LSP diagnostics for ALL project
files in their metadata. In large projects this adds 100-500KB of
unrelated diagnostics per tool call, bloating session payloads by
50-77%.
The UI only renders diagnostics for the edited file(s), so the rest is
dead weight. Extract a filterDiagnostics utility that keeps only the
relevant files' diagnostics and use it in all three tools.
* add kilocode_change marker to diagnostics filter test
Previously, connect() called create() (which spawns a new child process
and writes processes[key]) before closing the existing client. The old
client's close handler would then delete processes[key], removing the
*new* process entry from the map — losing track of the new child.
Move the existing client close to before create(), matching the pattern
already used in add().
Add a comment noting that (transport as any)._process relies on a private
implementation detail of StdioClientTransport from @modelcontextprotocol/sdk@1.25.2.
Future SDK upgrades should verify this field still exists.
terminateWorker is registered on SIGHUP, SIGTERM, and used in onExit.
If a signal fires and then onExit triggers (or two signals arrive),
worker.terminate() and client.call('shutdown') would fire redundantly.
Cache the pending promise so subsequent calls return the same operation.