Separate verbose parse details into a new Warning.detail field so toast
messages stay short while 'kilo config check' and VS Code's Show Details
action expose the full diagnostic. Make the TUI config-warning toast
persistent (dismissed on first prompt submit) and fire VS Code's
checkConfigWarnings() independently so prior failures in the SSE handler
cannot suppress it. Add 'kilo config check' subcommand that prints all
warnings with color and exits non-zero.
Collect warnings during config loading instead of crashing when config
files have invalid JSON, schema errors, or frontmatter issues. Warnings
are exposed via a new GET /config/warnings pull-based endpoint.
- Add Config.Warning schema and warnings collection in stateInit()
- Wrap all loadFile/load calls with try/catch to collect warnings
- Thread warnings through loadCommand/loadAgent/loadMode/invalid
- Wrap well-known remote config block to prevent network/parse crashes
- Add GET /config/warnings server route with OpenAPI spec
- Regenerate SDK with config.warnings() method
- TUI: fetch warnings in bootstrap non-blocking phase, show as toast
- VS Code: fetch warnings on SSE connect, show as native notifications
- Add 5 new tests for warning collection (JSON, schema, markdown)
* fix(cli): guard against null theme in TUI resolveTheme and Proxy
Add null guards to prevent crashes when theme.theme is null/undefined:
- Add isValidTheme type guard with fallback to built-in kilo theme
- Wrap theme resolution memo in try/catch for resilience
- Use empty object as Proxy target instead of memo result
- Validate custom theme JSON and protect built-in theme keys
Closes#8109
* fix(cli): validate required theme keys in isValidTheme
Check for background, text, and primary keys to reject empty or
partial theme objects that would produce undefined field accesses.
When the VS Code extension host is hard-killed (crash, OOM, kill -9),
dispose() is never called and the kilo serve child process is orphaned.
Fix by using a stdin keepalive pipe: the extension keeps stdin open after
spawning the CLI. The CLI watches for EOF on stdin (only when
KILO_CLIENT=vscode) and triggers a clean shutdown. The OS closes the
write-end of the pipe on any parent death, so the CLI always exits
regardless of how the extension host terminates.
On clean dispose(), stdin is explicitly closed before sending SIGTERM,
so the shutdown path is consistent in both the crash and clean cases.
Closes#8050
Previous fixes in #6813 and subsequent PRs added windowsHide:true to most
spawn sites, but several call sites were missed or regressed:
- SDK server.ts (v1 + v2): kilo serve and TUI spawn
- ts-check.ts: Bun.spawn for tsc invocation
- github.ts: exec for browser open
- desktop-electron cli.ts: install script spawn and version check
- desktop-electron ipc.ts: open-path execFile
- desktop-electron apps.ts: wsl/where execFileSync calls
Without windowsHide:true, each of these subprocess invocations causes a
visible cmd.exe console window to briefly flash on Windows.
Closes#7896
Mark the orchestrator agent as deprecated so users see a warning badge
in the mode switcher and agent settings, signaling they should migrate
to other agent configurations for task delegation.
The subprocess restart approach did not solve the underlying Bun native
memory retention issue (oven-sh/bun#28318). Remove all restart plumbing
from rpc.ts, thread.ts, worker.ts, app.tsx, and sdk.tsx to keep the
diff clean. The diff-size and store-eviction fixes remain.
Bun Workers are threads within the same OS process — terminate() frees
the JSC context but mimalloc retains every page process-wide, so the
previous Worker-restart approach had zero effect on RSS.
Switch to Bun.spawn() with IPC which creates a separate child process.
Killing that process returns all its native memory to the OS.
The IPC relay pattern means the RPC client persists across subprocess
restarts — event listeners, fetch proxy, and SDK all continue working
without getter-indirection or rebinding.
Workaround for https://github.com/oven-sh/bun/issues/28318
Bun's JSC does not return freed native heap pages to the OS within a
single Worker lifetime. After large sessions, the only way to reclaim
that 2-3 GB of native allocator retention is to terminate the worker
and spawn a fresh one.
Workaround for https://github.com/oven-sh/bun/issues/28318
- Add getter-indirection layer so fetch/events transparently follow
worker replacement without rebuilding the TUI
- Add rejectAll() to RPC client to fail in-flight calls on termination
- Add rebindable event source that re-registers handlers on new client
- Wire /new command to fire-and-forget restart; sync layer re-bootstraps
via server.instance.disposed event from the new worker
- Guard against re-entry and skip in external server mode
User messages carry summary.diffs with full before/after file content
(the same giant strings as session_diff). The TUI never reads this
field. Strip it at both entry points (SSE handler + full sync) to
prevent multi-MB strings from accumulating in the Solid store.
The Solid store accumulated messages, parts, diffs, todos, status, and
permissions for every session visited during a TUI lifetime. Navigating
away via /new or the session list never freed the old session's data.
Add an evict() function that deletes all per-session entries from the
store maps and clears the fullSyncedSessions cache. Wire it into:
- A createEffect in app.tsx that fires when the route changes away
from a session (on() tracks prev vs current sessionID)
- The session.deleted SSE handler, which previously only removed the
session list entry but left orphaned per-session data
Defense-in-depth: destructure away before/after content from FileDiff
objects at both TUI store entry points (SSE handler + full sync). The
sidebar only reads file, additions, deletions — carrying full file content
in the Solid store is unnecessary and risks memory bloat.
- Check absolute patterns in isRequest via isAbsolute() to match the
existing metadata.filepath dual-dispatch logic, preventing a bypass
where tools pass absolute project-level config paths in patterns
- Export ConfigProtection.DISABLE_ALWAYS_KEY constant to replace the
'disableAlways' magic string across config-paths.ts, next.ts, and
permission.tsx so renames/typos are caught at compile time
Introduce WebSocket-based remote session relay so external clients can
subscribe to sessions, send messages, answer questions, and respond to
permissions in real time.
- RemoteWS manages the authenticated WebSocket lifecycle with reconnect
- RemoteSender dispatches inbound commands and forwards server events
- Backfill pending questions/permissions when a client subscribes
- Add question_reject command for dismissing questions remotely
- Consolidate /remote TUI command into kilo-commands
- Add session.viewed endpoint to the generated SDK
- RemoteProtocol.Preview schema for diagnostic logging
- kilo.ai/docs/github/#usage-examples was a 404 (page never created).
Replaced with kilo.ai/docs/automate/integrations in github.ts:245.
- vercel.link redirects to a login page that blocks bots — added to
lychee.toml exclusions.
The kilo provider always autoloads with free models and apiKey="anonymous",
causing useConnected() to return true even for first-time users without any
API key. This prevented the onboarding tip from ever appearing.
First-time users with no connected provider now see a targeted tip
on the home screen: 'Using a free model — run /connect to add your
API key'. Once a provider is connected or the user has sessions,
normal random tip behavior resumes.
Display global config path (from Global.Path.config) and project
directory in the status dialog so users can quickly verify which
config files are being loaded. Paths are shortened with ~ for
readability, matching existing TUI conventions.