ProviderTransform.options() does not merge arbitrary provider options into
the merged options object. Read chunkTimeout from item.options as a fallback
so provider.<id>.options.chunkTimeout works as documented.
Addresses kilo-code-bot review finding in PR #9795.
Add chunkTimeout propagation to Vercel AI SDK streamText timeout.chunkMs
for silent stream dropout detection. The config option already existed in
the provider schema and worked at the fetch wrapper level, but was not
passed through to the streamText call. This adds dual-layer protection
at both the fetch layer and the SDK stream consumer layer.
Also updates docs to document chunkTimeout with failure scenario and
recommended values (15-30s).
Update documentation and changesets to specify that BYOK badges
apply specifically to Kilo Gateway models and do not apply to models
selected through other providers.
Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
Remove v2 HTTP API error-schema notes and `opencode login` default
that do not apply to the Kilo fork. Rename OPENCODE_PERMISSION to
KILO_PERMISSION to reflect the correct environment variable.
Introduce a `settle` helper function to improve the stability of visual
regression tests. The function ensures that fonts are loaded, animation
frames have processed, and specific dynamic elements (like code blocks)
have finished rendering before snapshots are taken. This helps reduce
flakiness caused by asynchronous rendering in Storybook.
Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
The wizard always re-requests migration data via requestMigrationData on
mount, so the data attached to migrationState was never read. Send only
the needed flag and source.
Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
Each wizard mount generated a fresh operationId and left its cached
discovery in the per-provider map forever. Prune entries from abandoned
operations when a new data request arrives, and delete an operation's
entry once its migration finishes.
Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
Split scanTaskStore into a history mode (legacy) that trusts taskHistory
and only stats each conversation file, and a discover mode (Roo) that
enumerates and parses task directories. This restores the prior legacy
behavior of not surfacing on-disk tasks missing from history and avoids
parsing every conversation file during detection.
Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
Provider plugins (llmgateway, nvidia, openrouter, vercel, zenmux) now
verify the provider ID before applying attribution headers, preventing
custom-defined providers sharing the same endpoint URL from being
mutated by built-in plugin logic.
Additional changes:
- Add auth-v2.json migration path for multi-account store continuity
- Remove obsolete @ai-sdk/xai patch and ConsoleCommand registration
- Add native LLM session recordings for anthropic, openai-oauth, and zen
- Update CLI help snapshots to reflect Kilo branding
- Expand i18n with usage-exceeded dialog strings (it, nl, uk)
The models snapshot is now embedded directly in the compiled binary,
eliminating the need for a separate models-snapshot.json file alongside
the CLI executable.
- Delete script/kilocode/models-snapshot.ts build-time preparation
- Delete src/kilocode/provider/models-snapshot.ts runtime loader
- Remove snapshot copy steps from vscode, jetbrains, and opencode builds
- Remove snapshot existence checks from PrepareLocalCliTask/CheckCliTask
- Remove snapshot-related .gitignore and .prettierignore entries
- Simplify watch-cli, local-bin, and build scripts
- Update smoke test error messages to say "embedded" not "sidecar"
- Simplify models-snapshot tests to validate parsing only
- Remove models-snapshot from extract-source-links skip list
Introduce a `metadata` method on SessionProcessor.Handle that buffers
metadata emitted before tool-call registration, then applies it on the
running transition. This decouples metadata emission timing from
tool-call lifecycle.
Downgrade virtua from 0.49.1 to 0.42.3 and migrate the virtualizer API:
- Replace `findItemIndex(scrollOffset)` with `findStartIndex()`
- Replace `bufferSize` prop with `overscan` (count-based)
Additional changes:
- Change Permission.reply return type from Promise<boolean> to Promise<void>
- Make Ruleset type readonly and remove unnecessary array spreads
- Update nvidia provider headers to reference Kilo branding
- Reorder SDK event type definitions for installation events
- Reduce promise facade allowlist in check script
The JetBrains Runtime bundled with IntelliJ ships libfontmanager.so
but expects libfreetype.so.6 and libfontconfig1 on the host system.
The container image currently lacks these, causing UnsatisfiedLinkError
in any test that initialises Swing font metrics.
Install them explicitly until the updated container image (which will
include these packages in the Dockerfile) is available.
Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
The container image has bun pre-installed but not the project's
node_modules. Gradle's generateOpenApiSpec task runs 'bun dev generate'
which needs drizzle-orm and other packages from node_modules.
Add an explicit 'bun install' step after checkout.
Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
Adapt Kilo-specific code to breaking changes introduced by the v1.15.9
merge:
- Migrate LLM test helpers to use LLMEvent factory constructors instead
of raw event object literals
- Update provider cost extraction from `usage.raw` to native
`usage.providerMetadata` keyed by provider name
- Preserve AI SDK raw usage in providerMetadata for billing continuity
- Replace `findStartIndex()` with `findItemIndex(scrollOffset)` in
virtualizer calls
- Replace `overscan` prop with pixel-based `bufferSize` in virtual lists
- Change provider `all` data shape from array to Map
- Remove `useCommandPalette` dependency, replaced by mode stack pattern
- Fix ToolResultValue schema to avoid circular inference with tsgo
- Add missing test layer dependencies (HttpClient, RepositoryCache,
Command)
- Remove obsolete models-snapshot files
- Fix Permission reply routing tests to expect NotFoundError instead of
boolean false
- Normalize config references and fix nested type narrowing
Nothing imports @hono/zod-validator anywhere in the repo, and its peer dependency hono was removed in #10993, leaving an unmet peer. opencode was the only consumer, so the root catalog entry is dropped too.
Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
These auto-generated SST type files reference a root sst-env.d.ts that does not exist and import the "sst" package, which was removed in #10993. Kilo is CLI-only with no hosted platform, so these are dead artifacts. The upstream-merge tooling already lists sst-env.d.ts in skipFiles, so they won't be re-added.
Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
Switch the JetBrains unit job to run inside
ghcr.io/kilo-org/build/jetbrains:24.04, which already contains Bun,
Java 21, and the Gradle 9.4.1 distribution pre-cached in
GRADLE_USER_HOME. The three manual setup steps (bun, java, gradle)
are no longer needed and are removed.
A 'Mark workspace as git-safe' step is added after checkout because
container jobs mount the workspace from the host with different file
ownership. Git exit-128 errors otherwise occur when Gradle spawns a
'git tag' subprocess (build.gradle.kts:85).
Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
There is no top-level docs/ directory in the repo (docs live under
packages/kilo-docs/, already excluded), so the '!docs/**' entry never
matched anything. Remove the dead filter line.
The layout (auto | stretch toggle) in the Display settings tab was dead
code: the backend schema marks it @deprecated with the note 'Always uses
stretch layout', and the engine ignores the user choice entirely.
Remove the setting from:
- DisplayTab.tsx (UI row + LAYOUT_OPTIONS array)
- Config type (layout field)
- settings-io.ts KNOWN_KEYS allowlist
- All 20 language i18n files (80 translation keys total)
Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
kilo-console's ProjectConsoleRoute.tsx imports @opencode-ai/ui/file,
@opencode-ai/ui/context/file, and @opencode-ai/ui/session-review.
The removal was a false positive from depcheck which failed to detect
the workspace package references. Count now 33 deps removed.
- Add packages/containers/jetbrains/Dockerfile extending bun-node with
Java 21 (apt-get, multi-arch safe) and Gradle 9.4.1 pre-cached in
GRADLE_USER_HOME so the wrapper skips the network download
- Register the new image in build.ts (after bun-node, before rust) and
update README
- Fix containers.yml branch trigger: dev → main so images are actually
published from the default branch on every relevant push
Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
Convert the remaining `Effect.sleep(50)` synchronization hacks in
`prompt.test.ts` to either `Effect.yieldNow` or `waitFor` against
`SessionStatus`, mirroring the pattern already used elsewhere in the
file. Same motivation as the previous commit: stop making correctness
depend on how quickly CI schedules fibers.
- "concurrent loop callers all receive same error result": yieldNow
before resolving the gate, so the queued caller joins the run on a
scheduler turn instead of a 50ms window.
- "loop waits while shell runs ..." / "shell completion resumes
queued loop callers": yieldNow before asserting `llm.calls === 0`
while the shell is still running. The shell is already gated on
`waitFor("shell busy", ...)`.
- "cancel interrupts loop queued behind shell": waitFor shell busy
before forking the loop, then yieldNow before cancelling.
- "shell rejects when another shell is already running": waitFor
shell busy before issuing the second shell call.
The `Effect.sleep(20)` at line 93 is the poll interval inside the
`waitFor` helper itself and is left unchanged.
Storybook addons (addon-docs, addon-a11y, addon-themes) are configured
features, not unused dependencies. Their removal disabled documentation,
accessibility, and theme panels rather than pruning dead code. This restores:
- kilo-ui: addon-docs, addon-themes, addon-a11y
- kilo-vscode: addon-docs, addon-a11y
- .storybook/main.ts configs to their original addon arrays
- knip.json: reverts story file entry point additions (addons serve as
implicit entry points for story files)
Remaining removals (34 deps) are unaffected.
- command.ts: check migrate() return value, count failures, and surface
error count in the completion notification instead of silently ignoring
failed session imports (fixes bot WARNING)
- service.ts: read history_item.json for accurate title, workspace, and
timestamp — falls back to parsing api_conversation_history.json when
the file is absent
- service.ts: add rooveterinaryinc.roo-code and rooveterinaryinc.roo-code-nightly
to the list of Roo Code extension IDs to probe
Currently the `kilo agent create` command generates extremely long agent
descriptions. This is because the system prompt for the `generate` agent
tells it to include multiple verbose examples of when the agent should
be used. This is unnecessary and increases system context bloat without
improving the parent agent's ability to choose a subagent.
This change updates the system prompt to exclude the guidance about
examples, and instead includes official guidance from Anthropic's
documentation on best practices for agent descriptions.
The schedule trigger fires on every repo where the file exists,
including contributor forks. Forks don't have access to
OPENCODE_WATCH_SLACK_WEBHOOK, so the Slack step fails. The previous
vars.OPENCODE_WATCH_ENABLED guard evaluated to true on forks (empty
!= 'false'), so it didn't protect anything.
Add the same github.repository == 'Kilo-Org/kilocode' guard used by
18 other workflows in this repo.
Adds a new VS Code command 'kilo-code.new.importRooCodeSessions' that
discovers and imports conversation history from a Roo Code installation.
- Extend migrate() in legacy-migration/sessions/migrate.ts with optional
overrides (custom dir + pre-resolved LegacyHistoryItem) so the existing
parsing pipeline can be reused without modification for any extension's
tasks directory.
- Add packages/kilo-vscode/src/roo-import/service.ts which locates Roo
Code global storage as a sibling of the current extension's storage, scans
the tasks/ directory, and derives session metadata (title from first user
message, timestamp from numeric task ID).
- Add packages/kilo-vscode/src/roo-import/command.ts with a VS Code quick-pick
+ progress-notification flow.
- Register the command in extension.ts and package.json.
Closes#11243
htmlparser2 was added by upstream but lost during rebase conflict
resolution in packages/opencode/package.json. It is actively
imported at packages/opencode/src/tool/webfetch.ts:3
Story files in webview-ui/src/stories/ were flagged as unused by knip
after storybook addon references were removed from .storybook/main.ts.
Adding them as explicit knip entry points resolves the CI failure.
packages/kilo-ui and packages/kilo-vscode still referenced
@storybook/addon-docs, @storybook/addon-a11y, @storybook/addon-themes
in their addons array after the deps were removed.
MiniMax models (id contains "minimax") were in the variants() exclusion
list, so M3 exposed no reasoning control on any route despite supporting
the Anthropic `thinking` field (type: adaptive | disabled).
Remove minimax from the exclusion list and add instant/thinking variants,
mirroring the glm/kimi/qwen toggle added in #8484:
- @ai-sdk/anthropic (direct provider): map to native
thinking:{type:"disabled"} / thinking:{type:"adaptive"}
- @kilocode/kilo-gateway + @openrouter/ai-sdk-provider: reuse the
reasoning:{enabled} shape, translated by kiloProviderOptions.
Replace the obsolete "minimax returns empty object" test with coverage
for the direct-anthropic and kilo-gateway routes.
Refactor the description for the `settings.models.hidePromptTraining` setting across all supported languages to remove the redundant "across all Kilo clients" phrase, making the text more concise.
Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
- Add dialog to show docs URL when no display server is detected on Linux
- Simplify dialog to show 'No display detected' with docs URL
- Remove unnecessary browser open attempt in headless environments
- Add kilocode_change markers for new dialog component
The previous implementation tried to open a browser even on headless systems,
which would fail silently. The new dialog clearly communicates the situation
and provides the docs URL for manual access.
Corrects the model identifier to `kilo/openai/gpt-5-nano`. Previously, running the triage agent failed because the prefix was missing, throwing an Model Not Found error. Resolved by explicitly using the `kilo/openai/gpt-5-nano` target path.
Extract isEnterKeyCommitNotIme so PromptInput, docks, selectors, and KiloClaw
inputs share one definition. Bun tests cover Enter, composing, and keyCode 229.
Treat keyCode 229 and isComposing like other stacks so Enter does not send, pick models, rename, or submit docks while an IME is confirming text.
Fixes#7216
* feat(vscode): add agent autonomy presets
* feat(work-style): simplify onboarding state to use config-only persistence and add skipped state
Replace globalState-based `onboardingShown` flag with a new `skipped`
enum value in the `agentWorkStyle` VS Code setting. This makes the
onboarding flow re-triggerable by setting the config back to `unset`
and removes the need for extension context in work-style handlers.
- Add `skipped` to WorkStyleState enum for dismissing without choosing
- Remove `onboardingShown` from messages and context provider
- Add `watchWorkStyleConfig` to reactively push changes to webview
- Extract `SidebarEmptyState` and `WelcomeEmptyState` components from
MessageList for cleaner separation of concerns
- Pass `emptyState` prop through ChatView to allow sidebar-specific
onboarding UI without coupling MessageList to WorkStyleProvider
* chore: update kilo-vscode visual regression baselines
* feat(work-style): refine onboarding preferences
* feat(webview): re-request work style on extensionDataReady if still loading
Add a listener for the extensionDataReady message so that the
WorkStyleProvider retries data fetching when the extension signals
readiness, preventing a stale loading state caused by race conditions
during webview initialization.
* chore: update kilo-vscode visual regression baselines
* feat(work-style): improve onboarding flow
* chore: update kilo-vscode visual regression baselines
* refactor(onboarding): extract work style display logic into pure state resolver
Decouple onboarding visibility from the persisted WorkStyleState by
introducing a dedicated `display` signal driven by
resolveWorkStyleOnboarding(). The onboarding panel now auto-persists
"skipped" on first render (one-off semantics) and dismisses itself on
newTaskRequest events. Unit tests cover the three resolution branches.
* style(welcome): widen responsive breakpoint for work-style empty state
* fix(vscode): make work style onboarding updates reliable
Persist skipped state only after onboarding is bypassed, apply presets through one rollback-aware extension operation, and use the human-in-the-loop identifier consistently.
* fix(work-style): remove duplicate payload post after apply handler
The apply handler already posts the updated work-style payload
internally, so the additional post here caused a redundant message
to be sent to the webview after each style application.
* refactor(onboarding): relocate settings access from toast into picker card
Move the "Go to Settings" call-to-action out of the post-save toast
and into a persistent inline link at the bottom of the WorkStylePicker.
Simplify the success toast to a short 2-second confirmation, tighten
choice descriptions for conciseness, drop the bash detail row, and
remove the small-viewport rule that hid mode details.
* chore: update kilo-vscode visual regression baselines
---------
Co-authored-by: Imanol Maiztegui <imanol.mzd@gmail.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
- Replace 'Review only these things:' with an explicit statement that
the six tracks are permitted topics but not all are spawned for every
review, removing the contradiction with the tiered sub-agent system
- Align track names (duplication, dead code) between Review Focus and
Required Workflow step 3 so both sections use the same identifiers
- Scope the deploy safety, duplication, and dead code rules to their
respective tracks with 'apply only when this track is active' so they
don't imply mandatory coverage when those tracks are not spawned
* fix(vscode): render streaming assistant rows outside virtualizer to prevent layout jumps
Replace the `keepMounted` index-based approach with a direct rendering
suffix for active assistant turns. Rows belonging to the streaming turn
are partitioned out of the virtualizer and rendered as plain DOM elements,
eliminating measurement instability caused by rapidly growing content
while preserving virtualized history for completed turns.
* Update packages/kilo-vscode/webview-ui/src/components/chat/MessageList.tsx
Co-authored-by: kilo-code-bot[bot] <240665456+kilo-code-bot[bot]@users.noreply.github.com>
* fix(vscode): complete direct row ID rename
* refactor(transcript-rows): restrict direct partition to only the latest visible turn
Simplify partitionRows so that older streaming turns are no longer
retained in the direct (non-virtualized) output once a newer turn
becomes visible. The lookup now checks only the final row's turn
against the direct set, ensuring completed assistant turns move back
into the virtualizer immediately when the conversation advances.
---------
Co-authored-by: kilo-code-bot[bot] <240665456+kilo-code-bot[bot]@users.noreply.github.com>
Small diffs (<100 LOC, ≤3 files) now use 1-2 sub-agents instead of always
spawning six, reducing cost for simple changes. Medium diffs (100-300 LOC)
use 3-4, and large/complex diffs retain the full six-agent workflow.
On Windows, PowerShell commands with non-ASCII characters (e.g., Chinese, Japanese, Cyrillic) produce garbled output (mojibake) because the console encoding defaults to the system OEM code page rather than UTF-8.
Switch from `-Command` to `-EncodedCommand` with a Base64-encoded UTF-16LE payload that explicitly sets `[Console]::InputEncoding`, `[Console]::OutputEncoding`, and `$OutputEncoding` to UTF-8 before executing the original command. This ensures both input and output are properly encoded as UTF-8 regardless of the system locale.
Fixes#4138Fixes#2422Fixes#2939
When `kilo console --port 4321` reuses an already-running daemon on a
different port (e.g. 4097), the new options were silently ignored. Now
`kilo console` checks if the running daemon matches the requested
host/port, and restarts if they differ.
The restart only occurs when explicit `--port` or `--hostname` options
are provided via CLI, not when using default values.
Print a yellow non-fatal warning when kilo console or kilo daemon
start/restart is invoked with an explicit --port outside 4097-4116.
Auto-discovery only scans that window, so an out-of-range port can
silently break later manual or auto-connect attempts.
Implementation stays entirely in Kilo-owned files to avoid touching
upstream shared code:
- Added packages/opencode/src/kilocode/cli/port-warning.ts helper
exporting warnPort(port), which skips port 0 (auto-pick) and emits
a yellow console.warn when the explicit port is outside
Daemon.PortRange.
- Hooks the helper from console.ts and daemon.ts (start/restart)
The command still succeeds; the warning is informational only.
Reads terminal.integrated.fontFamily and terminal.integrated.fontSize (with
editor font fallback) instead of the editor font CSS var. A config watcher
broadcasts fontChanged messages so live terminals update without a reload.
Nerd Font glyphs now render correctly in Agent Manager terminal tabs.
Keys are stripped from provider payloads before they reach the webview,
so the model-fetch request for an existing provider always went out
unauthenticated and gated endpoints returned 401. Retain providerID->key
extension-side in fetchProviderData, send providerID from the dialog when
the key field is untouched, and resolve the stored key in the fetch
handler — only when the requested URL matches the provider's configured
baseURL, so a stored secret can never be redirected to a different host.
Addresses the API-key half of #10139.
Split scroll-related concerns into smaller, focused units:
- Move user interaction detection (pointer, key, touch, wheel) into
a new `createUserActivity` hook with mark/consume/isRecent API
- Extract `distanceFromBottom` and `canScroll` as pure utility helpers
- Simplify `createAutoScroll` by replacing inline state and event
handlers with the composable user activity tracker
- Rename internal actions for clarity (scrollToBottom → follow/force)
* feat(vscode): add category labels and clarify command titles in package.json
Add "Kilo Code" category to all command contributions for better
organization in the VS Code command palette. Clarify the "Open in Tab"
command title to be more descriptive instead of generic "Kilo Code".
This improves discoverability and user experience when browsing available
commands in VS Code.
* chore: add command palette changeset
---------
Co-authored-by: Sylwester Liljegren <sylwester.liljegren@softronic.se>
Co-authored-by: Johnny Amancio <johnnyeric@gmail.com>
Remove logic that automatically unpaused scroll when the container
temporarily stopped overflowing. Previously, layout changes that reduced
content height below the viewport would reset the userScrolled flag,
causing the chat to jump back to the bottom when the user had
intentionally scrolled up.
The stop/pause function now accepts a force parameter to bypass the
canScroll check, ensuring an explicit user pause is never cleared by
transient layout changes.
Remove the `isNested` helper that previously allowed upward scrolling
inside nested scrollable regions (tool output, code blocks) without
disabling auto-scroll. Now any upward wheel input anywhere in the
transcript immediately pauses auto-scroll, reflecting the user's intent
to review earlier content even when a nested region would consume the
event.
The wheel handler is registered with `capture: true` so it fires before
nested elements can intercept the event, and `markUser` no longer
triggers on wheel events since the capture handler handles that intent.
The compat re-export and the build's CJS-redirect plugin only existed to work
around the 'G9' crash that's actually a Bun --splitting bug (now fixed by
splitting:false). With splitting off the morphsdk ESM barrel bundles cleanly,
so import it directly in warpgrep.ts and remove the indirection.
The real cause of the 'Exported binding G9 needs to refer to a top-level
declared variable' SyntaxError is a Bun 1.3.14 --splitting codegen bug
(oven-sh/bun#25621), not @morphllm/morphsdk. With splitting:true Bun emits
invalid cross-chunk re-exports (import{vn as G9}) that crash the compiled
baseline binary at startup. Disabling splitting produces a valid binary;
verified the 'as G9' artifact is gone from the compiled output.
The morphsdkCjsPlugin called require.resolve on the raw dist/.../client.cjs
path, which is not an exported subpath in the package's exports map, so Bun
aborted the release build with 'Cannot find module ...client.cjs'.
Resolve through the public specifier @morphllm/morphsdk/tools/warp-grep/client
instead — require.resolve uses the package's "require" condition, mapping it
straight to client.cjs.
The createRequire approach from #10955 did not prevent the Bun ESM
splitter from generating invalid output. Bun 1.3.14 with
`conditions: ["browser"]` resolves @morphllm/morphsdk via the "import"
condition (pre-split ESM barrel) even inside createRequire() calls,
pulling in 52 chunk-*.js files that cause:
SyntaxError: Exported binding 'G9' needs to refer to a top-level declared variable.
Fix: add a morphsdkCjsPlugin in script/build.ts using Bun's onResolve
API to redirect the module specifier to the absolute path of client.cjs
before the ESM splitter is invoked. client.cjs is a self-contained
~2300-line CJS bundle with no chunk-*.js imports.
morphsdk.ts is simplified to a plain re-export — the CJS redirection
happens at build time, no source-level workaround needed.
Two independent release validation failures introduced by kilo-opencode-v1.14.33 (morphsdk) and the new all-arch validation jobs.
**@morphllm/morphsdk pre-split ESM (intermittent SyntaxError on Windows)**
@morphllm/morphsdk/tools/warp-grep/client ships as a 805-byte ESM barrel that
re-exports from 13 internal chunk-*.js files (52 chunks total). When Bun 1.3.14
bundles the CLI with splitting+minify enabled, merging these external pre-split
chunks into its own chunk graph intermittently produces invalid minified ESM:
SyntaxError: Exported binding 'G9' needs to refer to a top-level declared variable.
Non-deterministic because Bun's parallel bundler uses different orderings per run;
same build sometimes succeeds, sometimes fails on Windows x64.
Fix: load morphsdk via createRequire (forces CJS bundle, client.cjs 2292 lines,
self-contained) instead of the ESM barrel. Wrapper lives in kilocode/compat/ with
a comment explaining how to detect and fix this pattern for future third-party deps.
Scanned all 40+ third-party packages imported by packages/opencode/src — morphsdk
is the only one with pre-split ESM; all others ship single-file ESM or CJS only.
**Alpine musl validation (libstdc++/libgcc missing)**
Bun's musl target (bun-linux-{arch}-musl) is not fully static — it links against
libstdc++.so.6 and libgcc_s.so.1 (GCC C++ runtime). Alpine base does not ship
these by default. The fix adds apk add --no-cache libstdc++ libgcc at the start of
the Alpine Docker smoke test, matching the runtime requirement that any Alpine user
of the CLI would also need.
The previous docs stated Security Agent was restricted to Teams and
Enterprise plans, which no longer matches the product behavior.
The personal and organization sidebars both expose Security Agent
unconditionally, with no plan gate or feature flag. This keeps the
docs aligned with that behavior.
Reason for change: docs were out of sync with UI/backend access rules.
Wrap the WorkingIndicator in a persistent slot container with a fixed
min-height so that toggling between the spinner and session actions
does not cause the chat content to jump. This stabilizes the scroll
position when live output finishes.
Extract helper utilities to identify interactive elements (buttons,
inputs, textareas, contenteditable) and nested scrollable containers.
Skip pausing auto-scroll when the user interacts with form controls
or question answer elements, preventing false positives that would
stop the chat from following new content.
Replace the custom event approach for resuming auto-scroll when the
permission dock disappears. Instead, observe the scroll container's
dimensions directly via createResizeObserver in the auto-scroll hook,
eliminating the coupling between ChatView and MessageList through
window events. The scroll ref is now tracked in the store to enable
resize observation alongside the existing content observer.
Remove lastScrollTop tracking that incorrectly classified virtualization
and layout-driven scroll position changes as user-initiated scrolling.
Now only explicit user input (detected via recentlyInteracted()) can
pause the auto-scroll following behavior, keeping chat pinned to the
bottom during programmatic or layout-driven updates.
Aligns the VSCode extension and kilo-ui with the JetBrains plugin, which
replaced the brain-circuit icon with the book-open-check icon for the
'may be used for training data' indicator in the model picker.
* Add Atomic Chat as a first-class local provider.
Ship the atomic-chat plugin with auto-discovery on localhost:1337, register the provider in models.dev fixtures, and document setup for VS Code and CLI.
* Remove dead code
Removed atomicChat provider mapping from legacy migration.
* Update Atomic Chat provider icon to theme-colored vector.
Replace the embedded raster icon with a currentColor SVG symbol so it matches provider icon styling and inherits UI color consistently.
* Allow optional API key for local Atomic Chat and LM Studio providers.
Load the atomic-chat plugin despite named constant exports, register local-server auth in the plugin, and let CLI and VS Code connect with an empty key on localhost.
* Gate Atomic Chat discovery behind explicit opt-in
* Update packages/plugin-atomic-chat/src/utils/should-probe-atomic-chat.ts
* Remove dead branch in atomic-chat model ref probe.
Drop the modelID check (never equals provider key) and the duplicate providerID branch.
* Fix PR CI: register atomic chat plugin and complete i18n.
Restore atomic-chat in default-plugins after main merge, add missing sidebar locale keys, and annotate shared upstream diffs.
* Format i18n locale files and ProviderConnectDialog with Prettier.
* Address PR review: docs order, narrow annotations, plugin fixes.
Reorder local-models providers per maintainer feedback; use inline kilocode_change markers in dialog-provider. Share ModelStatusCache, fix config discovery abort/timeout, throw on fetch failure, and resolve bot review warnings.
* Align dialog-provider with upstream keymap bindings after rebase.
Restore useBindings instead of useKeyboard so shared-file diff stays minimal and passes annotation checks.
* Update packages/kilo-vscode/webview-ui/src/components/settings/ProviderConnectDialog.tsx
* Deduplicate Atomic Chat /v1/models fetch on config discovery.
Use a single shared models endpoint request in enhanceConfig instead of separate health and discovery calls.
* Fix kilo-code-bot review: import provider key and log cache errors.
Export ATOMIC_CHAT_PROVIDER_KEY from local-providers for the webview, log cache warm failures, and throw on network errors in fetchModelsEndpoint.
* fix(plugin-atomic-chat): harden discovery and reduce chat toasts
Reuse auto-detect model list to avoid duplicate /v1/models calls, wire AbortSignal into fetch, replace silent catches with logging, and only surface validation errors in chat.params.
* fix(plugin-atomic-chat): refresh model cache on chat validation retries
Bypass the 15s model list cache after the first failed attempt so retryWithBackoff can observe newly loaded models.
* Update packages/plugin-atomic-chat/src/utils/index.ts
* fix(plugin-atomic-chat): repair categorizeError not_found branch
Restore return statement and map "not loaded" validation errors to not_found after bot commit broke the if block.
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: kilo-code-bot[bot] <240665456+kilo-code-bot[bot]@users.noreply.github.com>
* feat(marketplace): extract marketplace into standalone webview panel
* fix(marketplace): correct fetchData directory arg and add error logging
Replace swallowed catch block with console.warn for session status
sync failures, and pass `this.directory()` instead of `project` as the
second positional argument to `fetchMarketplaceData`.
Update Bun runtime and type definitions across the repository:
- package.json packageManager field
- @types/bun in root and script/upstream
- Nix flake pinned binary with updated hashes
- Container Dockerfile ARG
- Documentation and story fixtures referencing the version
Extract java-kotlin language analysis from the shared CodeQL Advanced
workflow into a dedicated codeql-kotlin.yml workflow. This allows the
Kotlin analysis (which requires Java, Gradle, and a manual build) to
run independently with path filtering on packages/kilo-jetbrains,
reducing unnecessary CI runs for unrelated changes.
- Add new codeql-kotlin.yml with path-scoped triggers
- Remove java-kotlin from the matrix in codeql.yml
- Simplify codeql.yml by removing conditional Java/Gradle setup steps
- Register codeql-kotlin.yml in check-workflows.ts active set
Replace the fragile covered/uncovered prefix dedup in SessionController with a stateless content-overlap glue: a text/reasoning delta only appends the portion that extends the current model tail. This eliminates the token-doubling (e.g. "importimport", "ReadRead a file") that occurred when multiple PartUpdated snapshots landed before the lagging PartDelta stream caught up.
Also adds a MdViewHybrid fast path that grows the trailing code view in place while inside an open fenced code block, skipping the full commonmark re-parse and block diff per token, with html() recomputed lazily when stale.
* feat(session-export): scaffold module with config constants
* feat(session-export): add zstd compression wrapper
* feat(session-export): event and envelope type definitions
* feat(session-export): eligibility check with kill-switch
* feat(session-export): org signal collector with auth resolver
* feat(session-export): worker SQLite schema and storage helpers
* feat(session-export): content-addressed chunker with zstd dedup
* feat(session-export): client-side light scrubber
* feat(session-export): IPC contract and inbox with back-pressure
* feat(session-export): persist scrubbed events with chunked payloads
* feat(session-export): worker entry point with inbox drain loop
* feat(session-export): main-thread capture module
* feat(session-export): workspace baseline and delta fibers
* feat(session-export): sync subscriber and tool io chunking
* feat(session-export): bootstrap wiring and compaction hook
* feat(session-export): wire capture hooks into sessions
* feat(session-export): uploader and buffer cap
* chore(session-export): annotate shared session hooks
* changeset(session-export): add release note
* fix(session-export): keep bootstrap non-blocking without instance context
* feat(session-export): respawn worker after failures
* test(session-export): add performance budget assertions
* test(session-export): add worker end-to-end smoke test
* fix(session-export): strip identity and high-risk baseline paths
* test(session-export): gate perf assertions for stable sweeps
* fix(session-export): bundle worker in single-file builds
* fix(session-export): make capture envelopes cloneable
* fix(session-export): drain worker on CLI shutdown
* feat(session-export): capture workspace baseline and deltas
* fix(session-export): preserve request metadata
* test(session-export): cover request metadata capture
* feat(cli): send indexed session export batches
* feat(cli): authorize session export uploads
* fix(cli): flush session export shutdown uploads
* feat(cli): optimize session export replay payloads
* fix(cli): include session export surface metadata
* fix(session-export): decrement chunk refs after upload
decRefChunks was never called, so chunk ref_count stayed at 1 (or
higher with dedup) and DELETE FROM chunk WHERE ref_count <= 0 never
matched. Chunks accumulated in the local SQLite buffer until the 50 GB
cap. Call decRefChunks alongside markUploaded so deleteUploaded can
reclaim the rows.
* fix(session-export): add periodic uploader flush timer
flushIntervalMs and retryBackoffMaxMs were both defined in config and
neither was referenced anywhere; scheduleFlush only ran on inbound
events or reconnect. After a 5xx or network failure the row was backed
off for 1 s, but if no further event arrived the retry never fired and
the events stranded until the CLI restarted. Drive a periodic flush
from a setInterval, unref the handle so it doesn't pin the process, and
clear it from a new dispose() hook the worker calls on shutdown.
* fix(session-export): stop emitting absolute workspace root in baseline
CaptureMetadata.root was the literal absolute filesystem path
(/Users/<name>/Projects/<repo>), shipped in every
workspace_baseline_completed event and not stripped by handlers'
identity filter. The field was set but never read anywhere downstream
— file paths in the baseline are already relative, so the root added
no replay signal. Remove the field outright.
* fix(session-export): cap pendingEvents result set
The SELECT had no LIMIT clause, so under a backlog (network outage,
crashed receiver) it would materialize the full pending table into a
JavaScript array before the byte-limit truncation applied. With a
50 GB buffer cap that is hundreds of MB of heap inside the worker per
drain. Add LIMIT 500 — the drain loop already re-queries until empty,
so no events are missed.
* fix(session-export): exponential retry backoff up to retryBackoffMaxMs
Both the 5xx and network-error branches always retried after the floor
delay regardless of how many attempts had already failed, and
retryBackoffMaxMs was unreferenced. During a sustained outage every
session re-tried at roughly 1 Hz against the dead receiver. Surface
upload_attempts on EventRow and compute the next delay as
min * 2^attempts capped at max.
* fix(session-export): evict superseded workspace snapshots on remember
createWorkspaceProvider retained every captured snapshot — in-memory
and inside the persisted state file — even after the session moved on
to a newer one. For a 1k-file repo over a 100-turn session that is
~2 GB of unreachable heap plus a state file that grows monotonically.
Drop the previous snapshot for the session on remember() when no other
session still references it.
* fix(session-export): anchor aws_secret_key scrubber to key name
The bare 40-char base64 pattern matched every 40-character hex string,
including all git commit SHAs. Tool outputs, diffs, and conversation
messages were silently rewritten as <<REDACTED:aws_secret_key>>,
destroying lineage information in training data. Require the key name
context — naked secrets in unstructured text are rare and the .env /
.aws/credentials high-risk path strip already covers the common case.
* fix(session-export): preserve root linkage in SyncSubscriber events
SyncSubscriber hardcoded rootSessionId = sessionId on every tool,
permission, and feedback event, so sub-agent sessions lost their root
linkage and a future training pipeline could not reconstruct the
agent topology from these side-channel events. Expose the rootSessionId
mapping from Capture and plumb it through the same pattern as
getTurnId.
* fix(session-export): atomic chunk GC after upload
markUploaded + decRefChunks + deleteUploaded were three separate SQL
statements; a crash between markUploaded and decRefChunks would leave
events flagged uploaded (never retried) and chunks with stale
ref_count (never reclaimed by deleteUploaded). Bundle the three calls
into a single transactional commitUploaded helper so either all three
land or none of them do.
* fix(session-export): wait on transient sqlite locks
* fix(session-export): snapshot current workspace directory
* fix(cli): preserve Kilo model export metadata
* fix(cli): send anon id for session export
* fix(cli): fallback to telemetry anon id
* chore(cli): annotate session export config test
* chore: remove session export docs
* fix(cli): harden session export uploads
* fix(cli): preserve stream lifecycle for session export
* fix(kilo-docs): exclude session export ingest link
* fix(cli): restrict session export workspace sync to git repos
* chore: remove session export changeset
* fix(cli): tighten session export payload types
* fix(cli): type session export model payloads
* fix(cli): simplify session export cleanup
* fix(cli): avoid session export shutdown race
* refactor(cli): use drizzle for session export storage
* fix(cli): finalize session export sqlite statements
* fix(cli): link compaction exports to root sessions
* fix(cli): stop exporting raw stream parts
* fix(cli): prune stale workspace snapshots
* refactor(cli): clarify chunk ref counting
* test(cli): clarify dropped upload assertions
* ci: avoid visual path filter action failure
* fix(cli): preserve in-flight workspace snapshots
* fix(cli): stop uploading baseline start events
* fix(cli): trim redundant export metadata
* fix(cli): trim workspace export bookkeeping
* fix(cli): fold terminal outcome into tool exports
* fix(cli): dedupe request context in export batches
* fix(cli): normalize compaction export payloads
* fix(cli): avoid duplicate tool result exports
* test(cli): align session export expectations
* fix(cli): run secretlint during session export scrubbing
* fix(cli): keep retried export batches contiguous
* fix(cli): include agent info in session exports
* fix(cli): flush pending session exports on serve startup
* fix(cli): drop exports when scrubbing fails
* fix(cli): narrow secretlint value extraction
* fix(cli): limit exported agent info
* test(cli): remove brittle agent export source assertion
* fix(cli): ignore corrupt session export workspace state
* fix(cli): keep session export close best effort
* fix(cli): avoid following session export symlinks
* fix(cli): preserve session export chunk ref counts
* fix(cli): retry transient session export uploads
* fix(cli): validate session export ingest endpoint
* fix(cli): tolerate missing export token details
* fix(cli): fail closed on session export org lookup
* fix(cli): decode session export permission replies
* fix(cli): finalize session export on stream close
* fix(cli): bound session export baseline timeout
* fix(cli): avoid persisting workspace file contents
* fix(cli): bound workspace snapshot capture
* fix(cli): validate session export worker messages
* fix(cli): revoke stale session export eligibility
* fix(cli): scope session export workspaces
* fix(cli): extend session export shutdown flush
* fix(cli): infer free Kilo models for export
* fix(cli): avoid duplicate chunk refs
* fix(cli): throttle session export uploads
* fix(cli): harden session export capture
* feat: disclose free model data collection (#10767)
* feat: disclose free model data collection
* chore(cli): document free model footer sorting
* fix(vscode): add free model data translations
* fix: simplify free model data label
* fix: simplify data collection badges
* fix: remove duplicate model info disclosure
* fix: restore composer data tooltip
* fix: align jetbrains data collection indicator
* fix: align free model data indicators
* fix(vscode): show data collection in model preview
* fix: limit data indicators to kilo gateway
* test(cli): relax prompt cancel timeout
* test(cli): annotate prompt cancel timeout
* test(cli): classify prompt queue runtime test
* fix(cli): defer session export startup
* refactor(vscode): extract editor actions from provider
* refactor(vscode): use direct import for DiffVirtualFile type
Replace inline dynamic import type assertion with the already-imported
DiffVirtualFile type at the top of the file.
Reorder statements in KiloBackendAppService and KiloBackendWorkspace so
that warning/error logging occurs before the reactive state is updated.
This ensures observers reacting to state changes can rely on warnings
already being recorded in logs.
* fix(cli): route share links through Kilo session API
Refactor shareSession and unshareSession to return Effect values using
EffectBridge.fromPromise and replace the ShareNext dependency in
SessionShare with KiloSession calls so CLI-generated share URLs point
to app.kilo.ai/s/ public links.
* test(session-share): replace EffectBridge usage with direct Storage service calls
Update session-share test to use Storage.Service directly instead of
wrapping Storage.write in EffectBridge.fromPromise, aligning test code
with the refactored service-based architecture. Add Storage.defaultLayer
to the test effect layer composition.
Migrate all Session.* promise-based helper usage (create, get, messages,
updateMessage, updatePart, setPermission, children, fork) to direct
Effect service consumption via Session.Service. This eliminates the
makeRuntime-backed legacy promise wrappers from session.ts and updates
all dependent modules and tests to use the Effect-based interface.
Key changes:
- Remove exported promise helpers (create, get, messages, etc.) from
session/session.ts along with the module-level makeRuntime instance
- Update kilo-sessions, remote-sender, allow-everything permission,
plan-followup, recall tool, and fork module to yield Session.Service
- Convert remapChildren from async function to Effect generator
- Add PlanFollowupRuntime.session helper for effectful session access
- Update all affected test files to use Effect.runPromise with
Session.Service.use pattern
- Remove session/session.ts from the promise facade allowlist and
update test allowlist counts accordingly
The bundled Shiki worker is created only from its injected webview resource URI, and Pierre does not construct blob-backed workers. Restrict worker-src to the webview source and lock the narrower policy in unit coverage.
The webview worker adapter is reached through an esbuild alias rather than a TypeScript import, so Knip must treat it as an explicit entrypoint for the VS Code CI check to pass.
Document the patch-bounded rendering requirement and correct Chrome trace attribution so future optimizations do not reintroduce full-file gray placeholders or mistake profiler delivery threads for execution threads.
Eager review rendering must not rebuild complete before/after contents when a tiny patch occurs inside a large source file. Preserve existing unified patches through Changes sources, parse them behind the deferred visibility gate, and use eager rendering only for patch-backed detail so gray placeholders clear without regressing session switching.
Diff syntax highlighting ran synchronously on the webview main thread because the Shiki worker was stubbed out (the webview CSP blocked workers), so scrolling large diffs froze the UI. Bundle the Shiki worker as a dist asset, load it from an injected webview URI, and allow it via a scoped worker-src CSP directive so highlighting runs off the main thread. With highlighting off-thread, normal-sized review files now render their diffs eagerly instead of going through Pierre's per-instance virtualizer, which avoids the re-render-all storms that rebuilt thousands of rows on every height change. Large files and an aggregate per-review budget keep eager DOM bounded.
Per-directory AGENTS.md files are dynamically loaded when the agent
reads files in that directory, not pre-loaded at session start. This
is injected as <system-reminder> tags during file access, providing
context-sensitive instructions for monorepos and large projects.
* refactor(opencode): remove legacy Provider promise wrappers in favor of Effect service usage
Replace static `Provider.list`, `Provider.getModel`, `Provider.getLanguage`,
`Provider.getSmallModel`, and `Provider.defaultModel` promise helpers with
direct `Provider.Service.use()` calls through `AppRuntime.runPromise`. This
eliminates the `makeRuntime` import and the wrapper functions that bypassed
the Effect dependency injection system.
- Remove legacy promise helpers from provider.ts
- Update kilo-sessions, roll-call, commit-message, enhance-prompt, and
task tool to use Provider.Service via AppRuntime directly
- Thread Provider.Interface into KiloTask.select for proper DI
- Add Provider.defaultLayer to test layers that exercise TaskTool
- Update commit-message tests to spy on CommitMessageRuntime.model()
* docs(sdk): regenerate v2 SDK types and update edit endpoint description
LanceDB infers the value column type from the first row in _createMetadataData().
Since vector_size (1024) was stored as a number, all subsequent string values
(embedding_provider, embedding_model_id) and booleans (indexing_complete) were
silently coerced to NaN/0/1, corrupting metadata.
On restart, _getStoredEmbeddingProfile() received NaN values, causing
needsRecreation=true every time — the database was dropped and recreated,
the file-hash cache cleared, and a full re-index triggered on every VS Code reopen.
Store all metadata values as strings to ensure consistent column type.
* feat(i18n): support Italian in desktop app
Add Italian translations for the app, shared UI, and Kilo-specific strings.
Register the Italian locale in the app language picker.
* feat(vscode): add Italian language support
* fix(i18n): polish Italian translations
* fix(i18n): complete Italian translations
* style(vscode): format Italian locale files
* fix(i18n): add DeepSeek Italian provider note
---------
Co-authored-by: Johnny Amancio <johnnyeric@gmail.com>
When a shell command has a descriptive title (the parameter
provided by the AI), include it in the permission approval dialog so users
can identify what the command will do, not just see the raw command string.
* fix(tui): dedupe solid-js and @opentui/solid to fix "No renderer found" startup crash
The bundled CLI shipped with two copies of solid-js and two copies of
@opentui/solid since 7.2.1. Each duplicated copy of solid-js and
@opentui/solid creates its own RendererContext token via createContext(),
so the context provided by render() (using one copy) is invisible to
useRenderer() calls resolved against the other copy. A reactive setStore
early in boot triggers a cascade that crosses the copy boundary and
explodes with "Error: No renderer found", preceded by the warning
"You appear to have multiple instances of Solid."
Two duplications were responsible:
1. solid-js: @opentui/solid@0.1.87 declares an exact "solid-js": "1.9.9"
dependency, so bun installed a nested solid-js@1.9.11 inside
node_modules/@opentui/solid/node_modules while the rest of the
workspace used the catalog solid-js@1.9.12 at the top level. Force
dedupe via a root override.
2. @opentui/{core,solid}: packages/kilo-gateway pinned dev/peer
versions to 0.1.75 while opencode used 0.1.87. With kilo-gateway's
tui module imported by opencode in 7.2.1+, both versions were
bundled side-by-side. Bump kilo-gateway's pins to 0.1.87 to match.
Verified locally with a --single build on macOS arm64: pre-fix the binary
crashes on startup with the Solid error; post-fix it boots cleanly. Bundle
shrinks ~16KB from removing the duplicated solid-js copy.
* fix: Upgrade stale version with catalog:
---------
Co-authored-by: Brendan DeBeasi <brendandebeasi@users.noreply.github.com>
Co-authored-by: Johnny Amancio <johnnyeric@gmail.com>
The foo/bar/baz good/bad examples didn't illustrate the rule (just
shorter nonsense vs. longer nonsense). Replace with prose that points
at the canonical 'Naming Enforcement' block, which already lists
realistic preferred names and anti-examples.
* docs: add free and budget model picks to model selection guide
Closes#7038. Links to Auto Model Free and Using Kilo for Free pages.
* docs: address review warnings for model pricing accuracy
* fix: remove stale models table
* Apply suggestions from code review
Co-authored-by: Johnny Amancio <johnnyeric@gmail.com>
Co-authored-by: kilo-code-bot[bot] <240665456+kilo-code-bot[bot]@users.noreply.github.com>
* feat: Add support for native agents from marketplace instead of modes
* chore: update kilo-vscode visual regression baselines
* fix: add guard for case when user has pre-existing agent with same name
* chore: Clean up unused code
* chore: continue clean up of old code related to modes
* chore: add notification message informing users about migration from modes to agents
* fix: fix broken layout of remove dialog and update leftover mode string
* fix: adjust strings with mode string instead of agent
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
config.get() returns the merged effective value, so a workspace-level pin
or the schema default would falsely look like a stored global default.
Switch to config.inspect().globalValue so the migration only acts on
values actually stored in user settings, and update the test stub to
model scoped values.
Looking up by provider+model id instead of relying on array index makes
the default robust to reordering of the AUTOCOMPLETE_MODELS list, and
fails loudly at module load if the referenced entry is ever removed.
Adds a test asserting the default resolves to a real entry.
The migration cleared pinned default settings before the manager's first
readSettings() call, but firing it as a void promise let the manager's
constructor (which kicks off load() synchronously) race with the migration.
Awaiting the migration in registerAutocompleteProvider ensures the cleared
state is visible when the manager loads.
Aligns the autocomplete model selector with the small/subagent/per-mode
model selectors by exposing a 'Not set (use server default)' option.
Users who pick it follow future default-model changes automatically;
users who explicitly pin a model keep their selection.
Also runs a one-time migration that clears
`kilo-code.new.autocomplete.{provider,model}` when they exactly equal
the current `DEFAULT_AUTOCOMPLETE_MODEL` (Codestral via Kilo Gateway).
Many users have these stored only because it was the only thing visible
in the dropdown; without this they would stay pinned to the current
default and miss future improvements.
* fix(vscode): improve suggest bar responsiveness
Add min-width and text wrapping for action buttons and introduce a
responsive layout that stacks the suggest bar vertically on narrow
viewports.
* chore: update kilo-vscode visual regression baselines
* docs(mistral): update Codestral documentation link
Replace outdated code_generation capabilities URL with current
vibe/code/overview path in the Mistral provider documentation.
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Adds a new "Mercury Next Edit via Kilo Gateway" autocomplete model so
users can get Next Edit predictions through their Kilo account without
needing a separate Inception API key. The gateway proxies to the
new /api/nextedit/completions endpoint added in cloud#3516.
Extract KiloModelsService from model-cache to enable dependency injection
and rewrite test files to use Layer-based test harnesses instead of
process-wide mock.module() calls that caused flaky cross-test pollution
on CI.
* refactor(opencode): remove raw LLM interface in favor of stream-based text helper
Introduce KiloLLM.text() to consume Effect streams and extract text
while propagating error/abort events. Replace all usages of the removed
LLM.raw() method and the legacy LLM.stream() async wrapper with
dedicated runtime helpers (CommitMessageRuntime.generate,
PlanFollowupRuntime.handover) that leverage the new stream-based
approach.
- Add KiloLLM.text() stream combinator in kilocode/session/llm.ts
- Remove Interface.raw and the exported LLM.stream() async function
- Refactor commit-message generation to use CommitMessageRuntime
- Refactor plan-followup handover to use PlanFollowupRuntime.handover
- Update all test LLM mocks to remove raw() stubs
- Add unit tests for KiloLLM.text() covering text joining, error
propagation, and abort handling
* feat(skill): migrate Skill module to Effect service pattern and remove legacy promise wrappers
Remove the makeRuntime-based promise helpers (Skill.all, Skill.get,
Skill.dirs) from the skill module and convert the builtin-skills test
suite to use Effect generators with the testEffect harness.
- Delete legacy runPromise wrappers from packages/opencode/src/skill/index.ts
- Rewrite builtin-skills.test.ts to use testEffect and Effect.gen
- Replace WithInstance.provide/tmpdir with TestInstance yield pattern
- Use Skill.Service directly within Effect generators for all assertions
* refactor(session-status): drop makeRuntime promise helpers and wire Service through Effect context
Replace the standalone `SessionStatus.list`, `.get`, `.set` promise
wrappers with direct `SessionStatus.Service` usage via Effect generators
and `AppRuntime.runPromise` at Kilo callsites that remain imperative.
- Remove makeRuntime-based exports from session/status.ts
- Update kilo-sessions.ts and plan-followup.ts to use AppRuntime.runPromise
with SessionStatus.Service
- Thread SessionStatus.Service as a dependency through SuggestTool and
ToolRegistry layers
- Replace spy-based mocks in suggestion tool tests with an in-memory
service stub for deterministic assertions
Match classic autocomplete's policy of never sending absolute fsPaths
upstream. Mercury only needs the path for language/context hints, and
the workspace-relative form is what recentlyViewedSnippets already uses.
* refactor(opencode): migrate ModelCache and Config to effect-native services
Remove legacy async wrapper functions from Config module and convert
ModelCache from a stateful namespace with module-level Maps into a
proper Effect service with Context/Layer semantics.
Key changes:
- Delete Config's `makeRuntime`-based async wrappers (get, getGlobal,
update, warnings, etc.) — all callsites now use
`Config.Service.use(...)` through AppRuntime
- Rewrite ModelCache as an Effect service with HttpClient dependency
injection, replacing imperative Map-based caching with Effect-native
Ref cells and TTL logic
- Convert KiloSessions.init and KilocodeBootstrap.init into proper
Effect services with Layer-based dependency injection
- Wire ModelCache.Service into AppLayer, ProviderAuth, ModelsDev, and
HTTP API handler layers
- Update Permission.layer to depend on Config.Service directly instead
of calling Config async wrappers
- Add new test files for KiloSessions and ModelCache Effect integration
- Remove stale Config.get spyOn mocks from tests that no longer need
them (experimental-session-list, recall)
- Fix indexing-auth to use typed IndexingConfig parameter instead of
untyped record access
* fix(model-cache): resolve race conditions in concurrent fetch and cache invalidation
Introduce versioned cache cells with proper key derivation to prevent
stale responses from overwriting fresher data during concurrent fetches.
- Add version tracking to detect and discard outdated fetch results
- Derive cache keys from provider-specific options (baseURL, token, apiKey)
to isolate concurrent requests with different credentials
- Make ModelCache.clear async to properly await invalidation across layers
- Update OrganizationDeps.clear signature to allow Promise<void> return
- Add concurrency and ordering tests for fetch/refresh race scenarios
- Rename local variable from `state` to `entry` in kilo-sessions sync loop
* chore(opencode): remove duplicate imports and fix test layer composition
Remove duplicate `AppRuntime` imports introduced during merge and update
kilo-sessions tests to use Effect-native Auth service instead of static
module calls.
- Remove duplicate `AppRuntime` import in index.ts and instance.ts
- Add Auth.defaultLayer to test layer helper
- Refactor test to yield Auth.Service and use instance methods
- Reorder Effect.provide/Effect.ensuring for correct resource cleanup
* style(opencode): normalize kilocode_change marker comments to block format
Standardize inline `// kilocode_change` annotations across source and
test files to use consistent `// kilocode_change start` / `// kilocode_change end`
block delimiters, improving readability and grep-ability of custom
modifications.
Addresses the two remaining points from @markijbema's review.
Prompt templating now lives in the gateway
-------------------------------------------
The Mercury sentinel-prompt assembly moved out of the VSCode extension
into packages/kilo-gateway/src/edit-prompt.ts. Clients now send
structured editor context (currentFileContent, cursor position,
editable region, recently-viewed snippets, edit-diff history) and the
gateway builds the sentinel-tagged prompt. This keeps the entire
Mercury contract — endpoint, auth, prompt format, response parsing — in
one place that VS Code, JetBrains, and the TUI can all share, instead
of each editor re-implementing the templating.
- New EditBody is the structured context (was: a pre-built `content`
string). Updated the opencode HttpApi schema, the hono zod validator,
both handlers, and regenerated the SDK.
- Deleted the VSCode-side mercuryPromptTemplate.ts (+ spec); the tests
moved to packages/kilo-gateway/test/edit-prompt.test.ts.
- VSCode constants.ts now holds only the editable-region sizing; the
sentinel tokens live in the gateway.
FileIgnoreController
--------------------
NES must not send ignored files (.env, secrets, anything matched by
.gitignore/.kilocodeignore) to the server. The NES provider now:
- skips the request entirely if the active document fails
ignoreController.validateAccess(), and
- filters recently-viewed snippets through the same controller before
they go into the prompt.
It reuses the classic provider's FileIgnoreController instance (now
public) rather than building a second one.
Also: dropped the implicit nextEdit.debug config read in log.ts (debug
is env-only via KILO_NES_DEBUG) so no VSCode autocomplete config is
added — per the "config should move to the backend" guidance.
Validation: typecheck clean across kilo-gateway, opencode, kilo-vscode;
lint clean; gateway 46 tests, vscode next-edit 10 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two-agent review pass on the gateway-routed NES change. Fixes:
Correctness (🔴)
- MercuryEditProvider read the HTTP status off the parsed error body
(`error.status`, always undefined), so 401/402 never reached
onFatalError and NES had no credit-exhausted/auth backoff. Now reads
`response.status` from the SDK result.
- NextEditSuggestionManager applied "insert" suggestions without the
apply-time drift re-validation the "replace" path already had — edits
between the anchor and insertion point could land the insert in the
wrong place. Now re-checks the anchor line before inserting.
- The opencode Effect edit handler collapsed every upstream failure to
HTTP 400; now passes the real status through (mirrors the FIM handler)
so 401/402/429/5xx are distinguishable under the experimental backend.
Conciseness / DRY (🔴)
- Deleted dead module editCompletionParser.ts (+ spec): the gateway
unwraps the fence server-side now, so the VSCode-side parser was unused.
- Hoisted the triplicated extractFencedBody into a single exported
function in kilo-gateway/src/edit.ts; both the hono and Effect handlers
import it. Added a shared EditUpstreamResponse type to replace three
inline copies.
Robustness (🟡)
- extractFencedBody now keeps the body when the closing fence is missing
(truncated/max_tokens output) instead of dropping the suggestion.
- EditHistoryTracker seeds snapshots on document open so the first edit
in a freshly-opened file is captured (was previously dropped).
- Single-line inserts that span non-blank lines below the cursor (a
multi-line→single-line collapse) now route to the decoration path
instead of emitting a ghost item VSCode can't render.
Cleanup (🟡)
- Removed unused constants (MERCURY_EDIT_MODEL_ID, INCEPTION_API_BASE_URL,
INCEPTION_EDIT_PATH).
- getProviderKey typed to DirectAutocompleteProviderID (matches FIM).
- resolveEditTarget keys on kind==="edit" defensively, so a future
FIM-only Inception model can't resolve to the edit endpoint.
- AutocompleteModelDef doc comments made endpoint-neutral (not "FIM").
- Declared the internal accept command in contributes.commands.
Tests
- New packages/kilo-gateway/test/edit.test.ts: resolveEditTarget routing
(incl. the mercury-edit-2 FIM model must NOT reach the edit endpoint)
and extractFencedBody variants (lang tag, sentinels, truncation, blank
lines, no-fence).
- typecheck clean across kilo-gateway, opencode, kilo-vscode; lint clean;
all unit tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addresses @markijbema's architectural feedback on #10536: moves the HTTP
edit-completion call to the gateway, removes the standalone API-key
setting, and aligns with the provider/model selection design introduced
in #10559.
Wire-level changes
------------------
* New `/kilo/edit` endpoint added to the opencode HttpApi contract
(`packages/opencode/src/kilocode/server/httpapi/groups/kilo-gateway.ts`)
and mirrored into `packages/kilo-gateway/src/server/routes.ts` for the
hono surface. SDK regenerated; `client.kilo.edit({ content, provider,
model, maxTokens })` is now available.
* `packages/kilo-gateway/src/edit.ts` — `EditTarget` resolver mirroring
the FIM pattern. Only the Inception provider is wired today (Mistral
doesn't expose a comparable surface); Kilo Gateway has a placeholder
branch that returns 400 until a server-side proxy exists.
* `packages/kilo-gateway/src/server/edit.ts` — `createEditHandler` reads
the Inception BYOK key from `Auth.get("inception")` and falls back to
`INCEPTION_API_KEY` from env, exactly like the FIM handler.
* The gateway unwraps Mercury's triple-backtick fence (and any
`<|code_to_edit|>` sentinels) server-side so the VSCode response is
just the rewritten code.
* `AutocompleteModelDef` gains an optional `kind: "fim" | "edit"`
discriminator; new entry `inception/mercury-next-edit` (label "Mercury
Next Edit") sets `kind: "edit"` and shares the wire model
`mercury-edit-2`.
VSCode-side
-----------
* `MercuryEditProvider` no longer does its own HTTP — it's now a thin
wrapper around `client.kilo.edit(...)` via `KiloConnectionService`.
* Dropped `kilo-code.new.autocomplete.nextEdit.apiKey` and
`.nextEdit.baseUrl` settings. Auth and routing live in the gateway.
* The `AutocompleteServiceManager` dispatch now switches on the model's
`kind` field (set by `getAutocompleteModel(provider, model)`) instead
of string-comparing a model id, matching Mark's provider+model split.
* The `NextEditInlineCompletionProvider`,
`NextEditSuggestionManager`, prompt template, parser, editable-region
selector, edit-history tracker, recently-viewed-snippets adapter, and
decoration-based jump-to-edit UX remain in the VSCode extension since
they need editor-specific APIs (`InlineCompletionItem`,
`TextEditorDecorationType`, keybinding context keys).
Bot review nits resolved
------------------------
* `INLINE_COMPLETION_ACCEPTED_COMMAND` renamed `kilocode.*` →
`kilo-code.*` to match the project convention.
* `kilo-code.next-edit.acceptOrJump` and `.dismiss` now declared in
`contributes.commands` so VS Code can resolve them in the palette.
* `disposeLog()` wired into `AutocompleteServiceManager.dispose()` so
the dedicated "Kilo Code · Next Edit" OutputChannel doesn't leak.
* Per-keystroke "skip — no API key resolved" log removed (the entire
API-key code path is gone).
Tests
-----
* `bun run check-types:extension` clean
* `bun run lint src` clean
* `bun test src/services/autocomplete/next-edit/__tests__/` — 23/23
pass
Docs
----
* The partner walkthrough at
`packages/kilo-vscode/docs/mercury-next-edit-testing.html` and the
20-test playground under
`packages/kilo-vscode/docs/nes-examples/` survive from the prior
iteration. The walkthrough's "Install the PR locally" section still
applies (the model dropdown choice is now "Mercury Next Edit
(Inception)" — the API-key step is gone since BYOK is plumbed through
the gateway's Auth store).
Known follow-ups (not in this commit)
-------------------------------------
* `FileIgnoreController` plumbing through the NES context builder so
`.env`-style files don't get sent. Hook point identified in
`NextEditInlineCompletionProvider.buildRequestContext`.
* Settings UI changes in the webview to expose Mercury Next Edit as a
selectable provider/model pair alongside the FIM entries.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace `urls: string[]` with provider-specific shapes: kilo and inception
each carry a single `url`, mistral carries no urls (its dual-endpoint
fallback is encapsulated in requestMistralFim). Removes the non-null
assertion on `urls[0]` and the dead "No FIM endpoint configured" branch.
Also tightens `getProviderKey` to take `DirectAutocompleteProviderID`,
which now follows naturally from the narrowed union and prevents the
`DIRECT_FIM_ENV["kilo"]` undefined-lookup footgun.
Previously `getAutocompleteModel` inferred the provider from a bare model
id like "codestral-2508" or "mercury-edit-2" when no provider was set.
That meant a user with a legacy `model` setting could be silently routed
to a direct BYOK provider without an explicit opt-in. Default to Kilo
Gateway whenever provider is unset; direct providers now require an
explicit `provider` setting.
* refactor(opencode): migrate agent promise helpers to kilocode module and adopt Effect-based tests
Remove legacy promise-based helpers (`get`, `list`, `defaultAgent`, `remove`)
from the core agent module and update callsites to use the dedicated
`@/kilocode/agent` module directly. Migrate kilocode-specific tests from
`WithInstance.provide` patterns to the `testEffect` helper with Effect
generators for cleaner, more idiomatic test code.
- Remove `makeRuntime` import and exported promise helpers from agent.ts
- Update HTTP API handlers to import from `@/kilocode/agent` instead of
re-exported `Agent` namespace
- Rewrite agent tests to use `load()` helper or Effect service access
- Convert agent-global-config-dirs and agent-skill-permissions tests to
`testEffect` pattern
- Reorder SDK type definitions (BackgroundProcessLogs/WorkspaceWarpError)
* chore(auth): remove legacy promise helpers and replace with direct Effect service access
Eliminate the exported promise-based convenience functions (`get`, `all`,
`set`, `remove`) from the Auth module and replace all callsites with
explicit `AppRuntime.runPromise` or `makeRuntime` invocations that access
`Auth.Service` directly through Effect's service pattern.
- Delete `makeRuntime`-backed promise helpers from auth/index.ts
- Update CLI entrypoint, kilo-sessions, indexing, and server instance to
use `AppRuntime.runPromise(Auth.Service.use(...))` for auth access
- Inject `Auth.Service` as a dependency into the ModelsDev layer and
consume it via Effect generator instead of promise wrapper
- Create a local `makeRuntime` instance in model-cache.ts for non-Effect
callsites that still need promise-based auth access
- Rewrite tests to manipulate auth.json directly on disk with proper
save/restore semantics instead of relying on removed helpers
* refactor(suggestion): convert suggest tool to Effect-native with injected Command dependency
Transform the suggestion tool from async/promise-based implementation to
idiomatic Effect generators with explicit dependency injection of the
Command service rather than importing and calling module-level helpers.
- Convert `resolvePrompt` from async function to Effect generator that
accepts a `Command.Interface` parameter
- Refactor `SuggestTool` definition to yield `Command.Service` from the
Effect context and thread it through to `resolvePrompt`
- Add `Command.Service` as a dependency to the tool registry layer and
provide `Command.defaultLayer` in both production and test wiring
- Remove unused `makeRuntime` import and exported `get` helper from
command/index.ts
- Add explicit type annotations to Auth delegate in server instance
- Rewrite suggestion tests to use `testEffect` helper with a mock
`Command.Service` layer instead of spying on module exports
* feat(git): migrate WorktreeFamily to Effect service and wire Git.Service as dependency
Convert WorktreeFamily.list from an async function using legacy promise
helpers to an Effect generator that yields Git.Service from context,
eliminating the need for the removed `run` promise wrapper in git/index.
- Replace `WorktreeFamily.list()` async function with Effect.fn generator
that obtains Git.Service and InstanceState from the Effect context
- Remove legacy `makeRuntime`/`run`/`runPromise` exports from git module
- Update RecallTool to thread Git.Service through to WorktreeFamily calls
via EffectBridge
- Add Git.Service as a required dependency in tool registry and HTTP
server route layers
- Update all test layers to provide Git.defaultLayer
* chore(mcp): replace legacy promise helpers with Effect-native AppRuntime calls
Remove exported promise-based `status`, `connect`, and `disconnect`
helpers from MCP module and convert the network recovery callsite in
SessionNetwork to use AppRuntime.runPromise with Effect.gen directly.
* refactor(auth): adopt makeRuntime helper for Auth service resolution in kilo modules
Replace AppRuntime.runPromise with locally scoped makeRuntime instances
in kilo-sessions and kilocode/indexing modules, removing the dependency
on the global AppRuntime singleton for Auth service access.
* fix(test): simplify cleanup error handling in provider test
Replace try-catch block with promise .catch() for file unlink operation
during test teardown.
On a fresh CI runner, multiple Turbo tasks starting in parallel can race
to extract the Gradle wrapper zip, producing ClosedFileSystemException.
Run ./gradlew --version before turbo test:ci to ensure the wrapper is
fully extracted before any parallel task touches it.
Add the JetBrains frontend migration wizard for the legacy migration core. The wizard renders as a MODAL_LAYER blocking overlay inside every visible SessionUi and is driven by a shared KiloMigrationService app service.
- Shared: MigrationDto.kt with full DTO set, KiloMigrationRpcApi with status/detect/migrate/skip/finalize/cleanup
- Backend: KiloBackendLegacyMigrationStoreService (PropertiesComponent status persistence), MigrationRpcMapper (pure bidirectional mapper), KiloMigrationRpcApiImpl (channelFlow streaming off Dispatchers.IO), KiloMigrationRpcApiProvider; extend KiloBackendMigrationManager with status/mark
- Frontend service: KiloMigrationService with idempotent check, in-flight guard, progress collection from streamed events, skip/finish/force re-import; MigrationSelectionBuilder mirrors VS Code preselection rules
- SessionRootPanel: new Blocker class at MODAL_LAYER with setBlocker/setBlocked helpers; existing scroll overlay at PALETTE_LAYER unchanged
- Swing wizard: MigrationWizardPanel (What's New + Migrate screens, retained component tree), MigrationItemRow, MigrationStatusIcon, SessionMigrationProgressPanel, SessionMigrationSummaryPanel, MigrationOverlayPanel
- SessionUi: wired to MigrationUiController via coroutine state collection on Dispatchers.Main; check() called from addNotify() and AppChanged READY
- Tests: SessionRootPanelTest, SessionUiLayoutTest, KiloMigrationServiceTest, SessionUiMigrationTest, FakeMigrationRpcApi, FakeMigrationUiController
Use session.configModel() instead of session.selected() in NewWorktreeDialog
so the overridden comparison (line 124) and the reset handler (line 444) both
use the config-based model. This prevents stale overridden states from
session.selected() from leaking into the worktree dialog's override detection
and reset behaviour.
Partially fixes#9349.
Both the copy and export handlers now pass `{ throwOnError: true }` to
`sdk.client.session.messages()` and drop the `?? []` fallback, so a failed
fetch (404/400/network) throws into the existing catch block and surfaces an
error toast instead of silently writing/returning an empty transcript.
- Add test:ci root script as alias for bun turbo test:ci
- Wire typecheck as a dependency of kilo-jetbrains#test:ci in turbo.json so compile runs before tests
- Exit with Gradle's code on Linux/macOS so failures block the repo-wide turbo test:ci run; Windows stays non-blocking due to inherent IntelliJ test flakiness there
Remove accidentally committed Gradle build cache files from the
JetBrains package, add the .gradle directory to .gitignore, and
update user-facing strings from "opencode.json" to "kilo.json".
Expand extractFilePathFromHref test coverage to handle file:// URLs on
Unix, Windows drives, and encoded characters. Previously file:// URLs
were expected to return undefined; now they resolve to local paths.
Exclude @kilocode/kilo-jetbrains from turbo typecheck filter to avoid
build errors in that package.
Use providerID variable instead of provider.id in dialog-provider to
fix incorrect property access after destructuring. Update cancel method
in TaskPromptOps stubs to return Effect.void matching the expected
interface. Remove obsolete workspace-restore test file and add missing
fixture import.
Reformat multiline arrays, JSX props, CSS transitions, and test
indentation to comply with the project's prettier configuration.
Also reorder auto-generated SDK type imports alphabetically and
relocate type definitions to match updated openapi.json output.
Port VS Code v5 migration logic to JetBrains backend: provider mapping,
MCP/custom-mode/settings conversion, session parser with deterministic SHA-1
IDs, HTTP backend adapter, and LegacyMigrationEngine orchestration. No UI,
no EDT dependencies — callers own threading and sequencing. 72 tests passing.
Track child session IDs discovered from task tool part metadata and subscribe
to their permission events so subagent permission prompts surface in the root
session UI instead of hanging silently. Also makes runtime auto-approve
non-persistent (no longer writes PropertiesComponent).
Move parseProviders, parseCommands, and parsePathState out of
KiloBackendWorkspace and KiloBackendModelStateManager into
KiloCliDataParser so no caller directly navigates JSON.
parseCommands intentionally ignores the template field, fixing
JetBrains startup when lazy CLI command templates serialize as {}.
Reorganize KiloCliDataParserTest into three @Nested groups
(SseEvents, HttpResponses, RequestBuilders) and add unit coverage
for all three new parser methods.
- retryPrompt() now uses msg.info.providerID/modelID instead of
model.model so login-resume preserves the original message's model
- Add AccountOverlayChanged to expected events in HistoryLoadingTest,
ListenerLifecycleTest, and WorkspaceWatchingTest (replay now always
fires the initial acctState on addListener)
- Update SessionUiLayoutTest overlay tests to set a logged-in profile
since the overlay is now hidden when not logged in
- Expose appRpc in SessionUiTestBase so subclasses can mutate app state
Move subagent navigation into the existing palette: a
"View subagents" command entry, a dedicated picker panel, and a
Down-arrow shortcut from the empty composer.
Add BaseSessionQuestionPanel in session/ui/shared as a shared rounded card shell for QuestionView and LoginRequiredView. The panel provides a consistent surface/outline, header and description text areas with editor-font styling, an optional top slot for navigation, and body/footer slots for view-specific content.
The export options dialog checkbox rows only set visual focus on mouse click but don't toggle the checkbox state. Update onMouseUp handlers to both set focus and toggle the corresponding boolean value.
When a session hit a PAID_MODEL_AUTH_REQUIRED (401) error, the login-resume
prompt was sent with null providerID/modelID because user messages never carry
those fields. This caused the backend to use its default model instead of the
originally selected one, resulting in silent sub-agent activity that appeared
as a UI hang. retryPrompt() now uses model.model parsed via parseModel(), the
same approach as promptDto().
Add UiStyle.Fonts tokens (display, heading, large) backed by JBFont
helpers that scale with the platform default font. Replace ad hoc
deriveFont(Font.BOLD, size * multiplier) calls in LoggedOutProfileUi
and the direct JBFont.h1().asBold() call in LoggedInProfileUi with the
new shared tokens.
Fix UiStyle.Colors.cardBg() which was hardcoded to Color.WHITE/BLACK,
breaking custom themes. Now uses JBColor.lazy resolving TextField.background
to follow the active theme's input surface color.
Replace Kotlin UI DSL with bare Swing in LoggedOutProfileUi. Introduce
LoginState sealed interface with attempt-based cancellation to discard
stale completions after cancel. Add ZXing QR code generation from the
verification URL. New auth card shows title, URL row with copy/browse,
QR image, spaced device code, countdown timer (M:SS), and cancel.
Login errors compact HTML payloads and offer Try Again. Backend and
frontend tests cover auth mapping, QR generation, pending UI, cancel
invalidation, and error retry.
Convert the first provider env/config/filtering tests to Effect-aware instance fixtures while keeping behavior unchanged and documenting neutral timing.
Reduce avoidable setup costs in slow opencode tests while preserving reviewed coverage and recording the benchmark evidence for follow-up test-suite work.
Pasting into the prompt textarea left its layout stale until the next edit, so the visible content did not reflect the pasted text. Mark the layout dirty on paste and notify the content-change handler once the renderer is idle so the prompt updates immediately.
Add an animated spinner to the subagent session footer to indicate
when a subagent is actively working, matching the running status
indicator shown in the main session prompt.
Users installing with pnpm or --ignore-scripts now see a clear explanation
of why the native binary is missing and exact steps to fix it. The shell
script wrapper provides more actionable guidance than the previous Node.js
fallback that only suggested running the postinstall script without context
about why it wasn't run automatically.
The TUI's copy and export actions only included ~100 messages because they
read from the in-memory sync store which caps at 100 for display performance.
Now both actions fetch all messages directly from the server API.
Per AGENTS.md, every Kilo-specific change in shared opencode files must
carry kilocode_change markers so upstream merges can resolve mechanically.
Adds whole-file annotations to the new helper and test, single-line
annotations to the two modified lines in run.ts.
Atoms in `args["--"]` are raw passthrough per yargs `populate--`
semantics: the user typed `--` to opt out of further parsing, so
the assembler must not synthesize quote bytes around them. Re-quoting
raw atoms breaks leading-dash inputs like
kilo run -- "- Who are you?"
by emitting `"- Who are you?"` (literal quotes) into the model prompt.
Atoms before `--` keep the existing wrap-quote behavior from #4979 so
shell-bound multi-word positionals still preserve their boundaries.
Extracted the message assembler to `cli/cmd/run-message.ts` so it can
be unit-tested without pulling in the full handler's dependency tree.
Fixes a bug where projectors were stored with Definition object references
as Map keys but lookups were failing due to object identity mismatches.
Now uses versioned type strings as keys, ensuring events are correctly
matched to their projectors during sync processing.
This fixes issues where certain events would not be properly projected
to the read model, causing stale or missing data in views.
The picker called setFetchedModels(undefined) unconditionally after
"Add Selected", wiping the remaining un-picked models. Users had to
re-fetch to add a subset twice in a row.
Filter the picked IDs out of fetchedModels and clear the selection.
Only close the picker when every fetched model has been added.
Refs: #10139
The DialogAlert component was rendering '\n' literally instead of
converting it to actual line breaks. This fix:
- Imports For from solid-js for proper list rendering
- Uses flexDirection='column' to stack lines vertically
- Replaces escaped \n with actual newlines before splitting
This affects the Teams tab message which displays:
'You're not a member of any teams.\nVisit https://app.kilo.ai...'
Adds a test that verifies cancel() on a session with no active tail
does not leave stale entries in the internal maps (versions, targets,
latest, activeSince). Also adds _hasInternalState() debug helper to
verify map cleanup.
String.length counts code points, not display columns, so CJK
characters and emoji that occupy two terminal cells caused
misaligned cursors, broken mention triggers, and incorrect
history restoration offsets.
Use Bun.stringWidth for now, we need an alternative for this.
Fix#26716Close#26922
The cancel() function was always incrementing and storing a version in the
versions map, even for sessions with no queued prompts. Since cleanup only
happens in enqueue's release phase, sessions without active tails left
permanent map entries.
Now cancel() checks if the session has an active tail before modifying state.
If no tail exists, it cleans up any existing versions/targets/latest/activeSince
entries instead of creating new ones.
- Replace session.selected() with session.configModel() in NewWorktreeDialog to reflect the configured model rather than the selected session model.
- Add configModel to SessionContext and implement an effect that clears session overrides matching the old config model when the global config model changes, ensuring sessions pick up the new config model.
PR #25418 replaced Instance.disposeAll with the disposeAllInstances helper
re-exported from test/fixture/fixture. Match the pattern used by the
sister tests in test/kilocode/.
The src/util barrel was split into per-file modules in main; every other
test in test/kilocode/ now imports Log from @opencode-ai/core/util/log
directly. Match the pattern.
The new shutdown timeout test in telemetry.test.ts shares a file with
tests that statically import `Telemetry` (and thus `client.ts` and
`posthog-node`). Bun's `mock.module()` invalidates the dependency
cache for subsequent dynamic imports so the test passes today, but the
behavior depends on test ordering and mock-cache-invalidation timing
rather than test isolation. Extract the shutdown test into its own file
where `mock.module("posthog-node", ...)` runs before any client.ts
import, so the mock is the source of truth from module load.
When the PostHog endpoint is unreachable (offline, firewall, DNS adblock
resolving us.i.posthog.com to 0.0.0.0), `kilo --help` and other short-
lived commands hang in the finally-block telemetry shutdown for tens of
seconds before the process exits.
Two issues:
1. `Client.shutdown()` called `await client.flush()` before
`await client.shutdown()`. The explicit flush() is unbounded:
posthog-node 4.4.0 retries each request up to 3x with 3s delays plus
10s per attempt before throwing PostHogFetchNetworkError, blocking
process exit before shutdown's outer cap kicks in.
2. `Telemetry.shutdown()` took no timeout, so the CLI bootstrap had no
way to bound the wait at the call site. PostHog's
`shutdown(shutdownTimeoutMs = 30000)` defaults to 30 seconds.
Fix:
- Drop the explicit `flush()` call. PostHog's `shutdown(timeoutMs)`
drains the queue internally and is bounded by `shutdownTimeoutMs`.
- Add an optional `timeoutMs` parameter to both `Client.shutdown` and
`Telemetry.shutdown`, threaded through to PostHog.
- Pass `2000` from the CLI bootstrap so short-lived commands exit
quickly when telemetry is unreachable, while keeping a generous
budget for the working-endpoint case.
Test: `Telemetry.shutdown timeout (#9788)` mocks `posthog-node` so the
fake `flush()` would hang for 60s if called. The test asserts (a)
`flush()` is never invoked and (b) the supplied `timeoutMs` (50ms) is
threaded through to PostHog's `shutdown(timeoutMs)`. Total elapsed time
stays well under 1s.
Closes#9788
Git's default rename detection in 'diff --cached --name-only' collapses a
file move (A/foo -> B/foo) into a single destination entry. The snapshot
revert iterates this list and deletes files that aren't in the pre-step
tree — so B/foo was removed but A/foo was never restored, losing data.
Pass --no-renames so both the source and destination paths appear in
the patch, matching diffFull()'s behavior. Revert now deletes the new
location and restores the original.
Fixes#9741
Inside a git submodule `git worktree list --porcelain` reports the
gitdir (`<repo>/.git/modules/<sub>`) rather than the actual working
tree, so the worktree-family filter for the experimental session
listing endpoint dropped every session whose directory was the real
submodule path. The CLI `kilo session list` was unaffected because
it filters on `project_id` only.
Append `Instance.worktree` to the parsed dirs so the working tree is
always in scope. Normal repos and linked worktrees already include it,
so the [...new Set(...)] dedup keeps those cases unchanged.
Closes#9267
2026-04-25 15:18:06 +00:00
3507 changed files with 439526 additions and 139822 deletions
Show JetBrains session connection status inline above the prompt, cap expanded connection details, and keep empty sessions in the shared chat scroll area.
Accept `env` as an alias for `environment` in local MCP server configuration. Configurations using the more common `env` key (matching Docker, npm, and VS Code conventions) are now normalised on load instead of failing strict validation.
<!-- Reference an existing issue with `Fixes #123`, `Closes #123`, or equivalent linked issue wording. -->
Fixes #
## Context
<!-- Brief description of WHAT you’re doing and WHY. -->
@@ -6,11 +12,13 @@
<!--
Some description of HOW you achieved it. Perhaps give a high level description of the program flow. Did you need to refactor something? What tradeoffs did you take? Are there things in here which you’d particularly like people to pay close attention to?
Some description of HOW you achieved it. Perhaps give a high level description of the program flow. Did you need to refactor something? What tradeoffs did you take? Are there things in here which you’d particularly like people to pay close attention to? Keep this focused on context reviewers cannot infer from the diff; skip file-by-file summaries, placeholders, and other filler.
-->
## Screenshots
## Screenshots / Video
<!-- Required for visual changes. Include the relevant before/after or resulting state. -->
| before | after |
|---|---|
@@ -18,18 +26,49 @@ Some description of HOW you achieved it. Perhaps give a high level description o
## How to Test
### Manual/local verification
<!--
A straightforward scenario of how to test your changes will help reviewers that are not familiar with the part of the code that you are changing but want to see it in action. This section can include a description or step-by-step instructions of how to get to the state of v2 that your change affects.
A "How To Test" section can look something like this:
- Sign in with a user with tracks
- Activate `show_awesome_cat_gifs` feature (add `?feature.show_awesome_cat_gifs=1` to your URL)
- You should see a GIF with cats dancing
Describe local CLI, extension, or docs verification. Example:
- Opened the updated settings page and confirmed the new copy appears
-->
-
### Reviewer test steps
<!--
Provide steps reviewers can retrace. Example:
1. Sign in with a user with tracks
2. Activate `show_awesome_cat_gifs` feature by adding `?feature.show_awesome_cat_gifs=1` to your URL
3. Confirm you see a GIF with cats dancing
-->
-
### Blocked checks and substitute verification
<!--
If a relevant command could not be completed, name the command, explain the blocker or failure, and describe the substitute verification performed. Example:
-`bun run test:integration` could not complete because the required API key is unavailable; substitute verification was testing the affected flow manually in the development extension
-->
-
<!--
Every PR marked ready for review must include testing evidence. A bare "Not tested" or "N/A" answer is not sufficient. For each piece of evidence make clear if the test was executed by the agent or by a human
Docs-only, config-only, and similar changes still need concrete evidence. For examples, see [Testing Evidence for Pull Requests](https://kilo.ai/docs/contributing/development-environment#testing-evidence-for-pull-requests). Draft PRs may be incomplete until marked ready for review.
-->
## Checklist
- [ ] Issue linked above, or exception explained
- [ ] Tests/verification described
- [ ] Screenshots/video included for visual changes, or marked N/A
- [ ] Changeset considered for user-facing changes
- [ ] I personally reviewed the diff and can explain the changes, including any AI-assisted work.
## Get in Touch
<!-- We'd love to have a way to chat with you about your changes if necessary. If you're in the [Kilo Code Discord](https://kilo.ai/discord), please share your handle here. -->
const closeComment = `Closing this pull request because it has had no updates for more than ${DAYS_INACTIVE} days. If you plan to continue working on it, feel free to reopen or open a new PR.`
if (dryRun) {
core.info(`[dry-run] Would close PR #${issue_number} from ${pr.author?.login || 'unknown'}: ${pr.title}`)
continue
}
try {
// Add comment
await withRetry(
() => github.rest.issues.createComment({
owner,
repo,
issue_number,
body: closeComment,
}),
`Comment on PR #${issue_number}`
)
// Close PR
await withRetry(
() => github.rest.pulls.update({
owner,
repo,
pull_number: issue_number,
state: "closed",
}),
`Close PR #${issue_number}`
)
closedCount++
core.info(`Closed PR #${issue_number} from ${pr.author?.login || 'unknown'}: ${pr.title}`)
// Delay before processing next PR
await sleep(requestDelayMs)
} catch (error) {
skippedCount++
core.error(`Failed to close PR #${issue_number}: ${error.message}`)
# required to fetch internal or private CodeQL packs
packages:read
# only required for workflows in private repositories
actions:read
contents:read
strategy:
fail-fast:false
matrix:
include:
- language:actions
build-mode:none
- language:javascript-typescript
build-mode:none
# CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift'
# Use `c-cpp` to analyze code written in C, C++ or both
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name:Checkout repository
uses:actions/checkout@v6
# Initializes the CodeQL tools for scanning.
- name:Initialize CodeQL
uses:github/codeql-action/init@v4
with:
languages:${{ matrix.language }}
build-mode:${{ matrix.build-mode }}
config-file:./.github/codeql/codeql-config.yml
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
const closeComment = `To stay organized pull requests are automatically closed after ${PR_DAYS_INACTIVE} days of inactivity. If the pull request is still relevant please reopen it or create a fresh new one.`
if (dryRun) {
core.info(`[dry-run] Would close PR #${issue_number} from ${pr.author?.login || 'unknown'} (last activity: ${pr.activity.date.toISOString()} via ${pr.activity.source}): ${pr.title}`)
continue
}
try {
// Add comment
await withRetry(
() => github.rest.issues.createComment({
owner,
repo,
issue_number,
body: closeComment,
}),
`Comment on PR #${issue_number}`
)
// Close PR
await withRetry(
() => github.rest.pulls.update({
owner,
repo,
pull_number: issue_number,
state: "closed",
}),
`Close PR #${issue_number}`
)
closedPrCount++
core.info(`Closed PR #${issue_number} from ${pr.author?.login || 'unknown'} (last activity: ${pr.activity.date.toISOString()} via ${pr.activity.source}): ${pr.title}`)
// Delay before processing next PR
await sleep(prDelayMs)
} catch (error) {
skippedPrCount++
core.error(`Failed to close PR #${issue_number}: ${error.message}`)
}
}
let page = 1
let stop = false
const staleIssues = []
while (!stop) {
core.info(`Fetching page ${page} of open issues...`)
const closeComment = `To stay organized issues are automatically closed after ${ISSUE_DAYS_INACTIVE} days of no activity. If the issue is still relevant please reopen it or create a fresh new one.`
if (dryRun) {
core.info(`[dry-run] Would close issue #${issue.number} (last activity: ${issue.activity.toISOString()} via updated): ${issue.title}`)
continue
}
try {
await withRetry(
() => github.rest.issues.createComment({
owner,
repo,
issue_number: issue.number,
body: closeComment,
}),
`Comment on issue #${issue.number}`
)
await withRetry(
() => github.rest.issues.update({
owner,
repo,
issue_number: issue.number,
state: "closed",
state_reason: "not_planned",
}),
`Close issue #${issue.number}`
)
closedIssueCount++
core.info(`Closed issue #${issue.number} (last activity: ${issue.activity.toISOString()} via updated): ${issue.title}`)
await sleep(issueDelayMs)
} catch (error) {
skippedIssueCount++
core.error(`Failed to close issue #${issue.number}: ${error.message}`)
description: Review an upstream merge PR and write reports
---
Review $1.
Branch off so we have all the code for the reviewed PR. Then run the following reviews in parallel, using one subagent per review, and save each result in the named markdown file at the repository root. A human will read those files, so if in doubt add a finding for human verification.
When all subagents are done, commit the report files and create a draft PR with the reviewed PR branch as base.
Do not include exhaustive per-file checklists in the report files. Summarize the scope and methodology, then list only findings, notable non-findings, command outputs, and limitations.
## KILOCODE_CHANGE_MARKERS.md
Review carefully, file by file, whether we accidentally removed any `kilocode_change` marker.
Get the full list of files changed in the reviewed PR. Check each changed file. For each file, compare both our `main` branch and the upstream-merged version in the reviewed PR. Determine whether any marker removal, marker move, or Kilo-specific change makes sense, and comment on that.
Do not include a full "Files Checked" section. Mention the number of changed files checked, but only list files that have findings or need human verification.
## INFRASTRUCTURE_CHANGE.md
Review whether this PR adds, removes, or changes any infrastructure, such as GitHub Actions, CI config, release/deploy scripts, Docker/build infrastructure, package manager/workspace infrastructure, repository automation, issue templates, changelog automation, or generated SDK/build automation.
We want to merge upstream code but keep our own infrastructure, so flag anything infrastructure-related. When in doubt, add a finding and say a human should check manually.
Do not include a full "Files Checked" section. Only list infrastructure-relevant files and findings.
## OPENCODE_MENTIONS.md
Check whether the merge now mentions OpenCode somewhere user-facing instead of Kilo, or links to OpenCode web properties.
Focus on UI strings, docs, help text, package metadata shown to users, URLs, CLI output, config docs, generated SDK/OpenAPI descriptions, and error messages.
## UNNECESSARY_MARKERS.md
Check whether any merged files now use `kilocode_change` markers without any actual difference to upstream.
Use `script/upstream/find-reset-candidates.ts --dry-run` to see whether any files changed in the PR are actually now identical to upstream. If you find candidates, verify them with `script/upstream/reset-to-upstream.ts --dry-run`.
## BROKEN_PIPELINE_CHAINS.md
Review this PR for broken end-to-end chains where our custom functionality requires changes across multiple files or layers, but the merge may have removed or altered an intermediate step. The code may still compile, so these issues can be silent.
Look for cases like:
- A parameter that is set but never read.
- A field that is populated but never passed through.
- An event that is emitted but no longer handled.
- A config option that is defined but never propagated to where it is used.
- A type definition extended on one side but not consumed on the other.
For each `kilocode_change` in this PR, trace the full chain: where the value or behavior is introduced, where it needs to flow through, and where it is ultimately consumed. Verify every link in the chain still exists after the merge.
Pay special attention to:
- Props or parameters passed through multiple component or function layers.
- Values written to state, context, or storage that are read elsewhere.
- Message types, events, or IPC handlers where sender and receiver must match.
- Configuration or feature flags defined in one place and checked in another.
- Type definitions extended on one side but consumed on the other.
When in doubt, add a finding. A human will verify it. Compiling code is not proof the chain is intact.
## TESTS.md
Check whether this PR removed any Kilo-specific tests.
Kilo-specific tests may live in paths containing `kilo` or `kilocode`, or may include Kilo-specific assertions, fixtures, or `kilocode_change` markers.
The JetBrains plugin currently imports `com.intellij.util.SVGLoader` in `packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/SvgIconColorizer.kt` and calls `SVGLoader.load(ByteArrayInputStream(patch()), scale.toFloat())` from `SvgIcon.image(Graphics)`.
Inspection reports this as an internal API usage because `SVGLoader` and its `load(InputStream, float)` method are marked `@ApiStatus.Internal`.
I checked the local IntelliJ source reference via `$INTELLIJ_REPO=/Users/kirillk/products/intellij-community`:
- IntelliJ `SVGLoader.load(stream, scale)` delegates to internal `loadSvg(...)` in `com.intellij.ui.svg`.
- The actual renderer uses `com.github.weisj:jsvg:2.1.0` and renders into a `BufferedImage` with antialiasing, bicubic interpolation, and pure stroke control.
- Copying `SVGLoader.kt` directly would keep references to other internal IntelliJ APIs (`IconLoader`, `ScaleContext`, `ImageUtil`, `SvgAttributePatcher`, `createJSvgDocument`, `renderSvgWithSize`, cache classes, etc.) and would be broader than needed.
## Implementation Plan
1. Add Kilo-owned SVG rendering code under the frontend UI package, for example:
2. Keep the copied implementation minimal and purpose-specific:
- Accept `InputStream` and `scale: Float`.
- Parse the SVG with `com.github.weisj.jsvg.parser.SVGLoader().load(stream)` or the equivalent public jsvg API available in version `2.1.0`.
- Determine image dimensions from the root SVG `width` and `height` already parsed by existing `SvgIconColorizer.size(...)`, or by passing dimensions into the loader if the public jsvg API does not expose reliable intrinsic size.
- Render into `BufferedImage(width * scale, height * scale, BufferedImage.TYPE_INT_ARGB)` using `Graphics2D` rendering hints matching IntelliJ’s `jsvg.kt`:
3. If jsvg does not expose intrinsic size cleanly from public API, adjust the API to avoid duplicating SVG parsing:
- Add `width` and `height` parameters to the Kilo loader.
- In `SvgIcon.image(Graphics)`, call the loader with the already-computed `size.first` and `size.second`.
- This is the likely smallest stable solution because current icons already require explicit `width` and `height` for `SvgIconColorizer.size(...)`.
4. Add the jsvg dependency explicitly to the plugin frontend module instead of relying on the IntelliJ-bundled library:
- In `packages/kilo-jetbrains/gradle/libs.versions.toml`, add `jsvg = "2.1.0"` and `jsvg = { module = "com.github.weisj:jsvg", version.ref = "jsvg" }`.
- In `packages/kilo-jetbrains/frontend/build.gradle.kts`, add `implementation(libs.jsvg)`.
- This follows the package instruction to bundle third-party libraries with the plugin rather than relying on IDE-bundled versions.
5. Update `SvgIconColorizer.kt`:
- Remove `import com.intellij.util.SVGLoader`.
- Replace `SVGLoader.load(ByteArrayInputStream(patch()), scale.toFloat())` with the Kilo-owned loader call.
- Keep the existing color patching, cache key, scale calculation, and size parsing intact.
6. Add a focused frontend unit test:
- New test file: `frontend/src/test/kotlin/ai/kilocode/client/ui/SvgIconColorizerTest.kt` or `SvgLoaderTest.kt`.
- Exercise the actual loader/colorizer with a small SVG resource or inline SVG bytes.
- Assert that rendering returns a non-empty image at scale `1f` and `2f`, and that scaled dimensions are correct.
- If testing the colorizer, paint the icon into a `BufferedImage` and verify at least one expected patched pixel color appears.
7. Run verification from `packages/kilo-jetbrains/`:
-`./gradlew typecheck`
- A targeted frontend test if Gradle supports one cleanly, otherwise `./gradlew test`.
- The requested DevKit internal API inspection is IDE-based; note in final response that the code no longer references `com.intellij.util.SVGLoader`, and typecheck/tests were run.
## Notes And Risks
- The user said “copy SaveLoaer”; I interpret this as “copy `SVGLoader`”. The implementation should copy the relevant rendering behavior, not IntelliJ’s full internal `SVGLoader` object, because the full source depends on multiple other internal APIs and cache infrastructure.
- This change is in `packages/kilo-jetbrains/`, a Kilo-owned package, so `kilocode_change` markers are not needed.
- jsvg package names and constructors should be confirmed during implementation against the downloaded dependency/API available to Gradle. If the API differs from IntelliJ source usage, inspect the source jar or compile error and adapt the minimal wrapper accordingly.
Stop runtime SVG recoloring in the JetBrains plugin and rely on IntelliJ's standard light/dark icon asset resolution. Ensure every SVG asset used by the plugin has a light/dark pair.
## Findings
- Runtime colorization exists only in `frontend/src/main/kotlin/ai/kilocode/client/ui/SvgIconColorizer.kt`.
- The only production caller is `frontend/src/main/kotlin/ai/kilocode/client/session/scroll/ScrollButtonIcon.kt`.
-`ScrollButtonIcon` colorizes two icons at runtime:
-`/icons/scroll-bottom.svg`
-`/icons/scroll-question.svg`
- Both already have dark variants:
-`scroll-bottom_dark.svg`
-`scroll-question_dark.svg`
- Other used frontend icons already have light/dark pairs:
-`plus.svg` / `plus_dark.svg` exists, though no current direct source reference was found.
-`kilo@20x20.svg` / `kilo@20x20_dark.svg` exists, though no current direct source reference was found.
-`src/main/resources/META-INF/pluginIcon.svg` is present and has no `pluginIcon_dark.svg`. It is not referenced in source/XML, but JetBrains treats this filename conventionally as plugin metadata/marketplace icon, so it should be paired for completeness.
- The SVG assets use literal colors and no `currentColor`, `<style>`, CSS classes, or CSS variables were found in `frontend/src/main/resources/icons/*.svg`.
## Implementation Plan
1. Replace `ScrollButtonIcon` runtime colorization with standard `IconLoader.getIcon(...)` loading.
- Remove imports for `colorizedSvgIcon`, `SessionUiStyle`, `UiStyle`, `JBUI`, and `Color` from `ScrollButtonIcon.kt`.
- Add `IconLoader` import.
- Load `bottom` with `IconLoader.getIcon("/icons/scroll-bottom.svg", ScrollButtonIcon::class.java)`.
- Load `prompt` with `IconLoader.getIcon("/icons/scroll-question.svg", ScrollButtonIcon::class.java)`.
- Keep `create(question: Boolean = false)` unchanged so callers continue to receive the appropriate icon.
- IntelliJ should automatically resolve `_dark.svg` variants when the look and feel is dark.
2. Delete `frontend/src/main/kotlin/ai/kilocode/client/ui/SvgIconColorizer.kt` after confirming no callers remain.
3. Remove now-unused scroll icon source palette constants from `SessionUiStyle.ScrollIcon`.
- Delete the `ScrollIcon` object if no references remain.
- This also removes stale comments about runtime colorization.
4. Add `src/main/resources/META-INF/pluginIcon_dark.svg` as the dark-mode pair for `pluginIcon.svg`.
- Use the same artwork/colors as `pluginIcon.svg` unless there is a desired brand-specific dark variant; the current icon already has a black background and yellow mark, so an identical dark pair is acceptable and satisfies IntelliJ's expected pair convention.
5. Re-scan the JetBrains package after edits.
- Confirm there are no `colorizedSvgIcon` or `SvgIconColorizer` references.
- Confirm every used SVG path has a sibling `_dark.svg` where IntelliJ expects one.
- Confirm no frontend SVG uses `currentColor`, `<style>`, CSS classes, or CSS variables.
6. Run the smallest relevant verification from `packages/kilo-jetbrains/`:
- The scroll icons will no longer follow arbitrary runtime theme colors; they will use the static light/dark SVG asset colors. This matches the requested behavior.
- If a non-default theme expects palette overrides, static literal SVG colors still follow normal IntelliJ SVG handling better than custom byte-patching, but the exact custom-color behavior may differ from the old colorizer.
When a question-like active view first appears in the JetBrains session UI, do not auto-scroll the transcript if the user is already away from the tail and the scroll-to-bottom button is visible. This applies to permission prompts and question-based views. Existing behavior should remain: if the transcript is already at the bottom, new questions/permissions keep the view at the bottom; explicit question navigation can still jump to the active card.
## Current Findings
-`SessionUi` wires `QuestionView` with:
-`follow = { scroll.following() }`
-`scroll = { scroll.followBottom(it) }`
-`QuestionView.show()` runs on first display and calls:
-`val tail = follow()` before rendering the card
-`scroll(tail)` after rendering
-`SessionScroll.following()` currently returns `component.viewport.view === messages && tail`.
- This can use stale internal follow state instead of the live scrollbar state. The user-visible condition is better represented by `SessionScroll.atBottom()`, which is also what `SessionController.beforeUpdate` uses for normal transcript updates.
- Existing `SessionScrollTest` already covers question and login-required middle-scroll preservation, but it does not cover permission first appearance and likely does not cover a stale-tail/live-not-at-bottom edge.
## Implementation Plan
1. Update question first-show follow sampling to use live bottom state.
- In `SessionUi.buildUi()`, change the `QuestionView``follow` lambda from `scroll.following()` to `scroll.atBottom()`.
- Alternatively, if a single semantic API is preferred, change `SessionScroll.following()` to return `component.viewport.view === messages && atBottom()` and keep the `SessionUi` call site unchanged. Prefer the smaller, clearer call-site change unless tests show other `following()` behavior should change too.
2. Add permission scroll regression tests in `SessionScrollTest`.
- Add `test permission appearing at bottom keeps scroll at bottom`.
- Add `test permission appearing while user is in middle preserves scroll position`.
- Emit `ChatEventDto.PermissionAsked("ses_test", PermissionRequestDto(...))` using the same DTO style as the existing recovered permission test.
- Assert the scrollbar value is preserved and `jumpButton().isVisible` stays true when initially away from bottom.
3. Strengthen question regression coverage if needed.
- Keep the existing `test question appearing while user is in middle preserves scroll position`.
- If the bug reproduces only when the jump button is visible but `tail` can be stale, add a targeted regression that creates the visible jump state before `QuestionAsked` and asserts first display does not jump.
- Avoid adding production-only test hooks; use existing `setValue`, `setValuePassive`, `drainScroll`, and event helpers.
- Do not change `QuestionView.goForward()`, `goBack()`, or `goReview()`, which intentionally call `scroll(true)` and are covered by existing tests that expect navigation to jump to the active question card.
- Do not change prompt growth or normal streaming follow behavior unless a failing test shows the same stale-state issue applies there.
5. Verification.
- Run the targeted JetBrains frontend session scroll tests from `packages/kilo-jetbrains/` using Gradle test filtering for `SessionScrollTest` if supported.
- Run `./gradlew typecheck` from `packages/kilo-jetbrains/` if the targeted tests pass or if Gradle filtering is unavailable.
- If Java 21 is unavailable, first check/report that blocker as instructed by the repo docs.
Fix JetBrains session scrolling so that when any inline prompt card appears while the scroll/jump button is already visible, the transcript scroll position is preserved and only the scroll button state changes. This applies to all current `BaseQuestionView`-backed session cards: questions, permissions, and login-required prompts. Preserve existing behavior that question navigation, review/back/next, and bottom-following content changes auto-scroll when they are supposed to.
## Findings
- The relevant files are `packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt`, `.../scroll/SessionScroll.kt`, `.../views/question/QuestionView.kt`, `.../views/permission/PermissionView.kt`, `.../views/LoginRequiredView.kt`, `.../views/base/BaseQuestionView.kt`, and `frontend/src/test/kotlin/ai/kilocode/client/session/SessionScrollTest.kt`.
-`QuestionView`, `PermissionView`, and `LoginRequiredView` do not inherit from `BaseQuestionView`; they compose it as the shared card shell. Scroll behavior should stay outside `BaseQuestionView` because it is a pure visual component with no session scroll context.
- The current uncommitted `SessionUi.kt` attempt changes `QuestionView` from `follow = { scroll.following() }` to `follow = { scroll.atBottom() }`.
- That fixes initial question appearance when the user is visibly away from bottom, but it is too broad because `QuestionView.follow()` is also used after custom editor growth, selection refresh, reply/reject, and other internal updates.
-`QuestionView` already intentionally calls `scroll(true)` in `goBack`, `goForward`, and `goReview`; those forced calls are the behavior that preserves auto-scroll when switching question pages.
-`PermissionView.show()` and `LoginRequiredView.show()` do not call scroll directly. They rely on `SessionController.beforeUpdate` / `afterUpdate`, which samples `scroll.atBottom()` before the state change and calls `scroll.followBottom(follow)` afterward. With the stale-tail fix, this path can preserve scroll for all non-question inline prompt cards.
- The uncommitted `SessionScroll.followBottom(false)` change that clears stale `tail` when the transcript is visible and not actually at bottom is directionally correct. It prevents later `following()` calls from resurrecting auto-follow after a non-follow decision.
- The current scroll button pending state is named `question` / `setQuestionPending` and is only enabled for non-plan `AwaitingQuestion`. To cover all `BaseQuestionView`-backed prompts, this should become a generic pending-input/attention state and include `AwaitingPermission` and `LoginRequired`.
- Existing behavior intentionally keeps plan follow-up questions using the normal scroll icon (`test plan followup question keeps scroll icon`), so preserve that exception unless product intent changes separately.
- The uncommitted `build.gradle.kts` and `script/build-sign-check.sh` changes are unrelated to this bug and should not be touched as part of this fix.
## Implementation Plan
1. In `QuestionView.kt`, split the scroll-follow decision into two callbacks.
- Keep the existing `follow` callback for question-internal updates.
- Add a second callback for initial question appearance, with a default that delegates to `follow` so existing tests/call sites remain compatible.
- In `show(q)`, sample the new initial callback before making the question visible or rebuilding the card, then call `scroll(tail)` after `syncPage()` as today.
- Leave `scroll(true)` in `goBack`, `goForward`, and `goReview` unchanged.
- Leave existing `scroll(follow())` calls in custom editor changes, option selection, reply, and reject paths unchanged.
2. In `SessionUi.kt`, wire the callbacks with distinct meanings.
- Use `follow = { scroll.following() }` for question-internal updates.
- Use the new initial callback as `{ scroll.atBottom() }` for initial question appearance.
- Keep `scroll = { scroll.followBottom(it) }`.
3. Generalize the scroll button pending state for all inline prompt cards.
- Rename `SessionScroll.setQuestionPending` and the backing `question` boolean to a generic name such as `setInputPending` / `pending` or `setAttentionPending` / `pending`.
- Rename `questionPending(state)` in `SessionUi.kt` to match the broader meaning.
- Return `true` for non-plan `SessionState.AwaitingQuestion`, `SessionState.AwaitingPermission`, and `SessionState.LoginRequired`.
- Keep returning `false` for plan follow-up questions so the existing plan-specific icon behavior is preserved.
- Reuse the existing question-style icon and tooltip for now, unless a separate product decision asks for a different permission/login-specific icon or string.
4. In `SessionScroll.kt`, keep the stale-tail reconciliation in `followBottom(false)`.
- When `follow` is false, increment `seq`, and if the active view is the transcript and `!atBottom()`, set `tail = false` before `updateJump()`.
- Do not change `following()` to use `atBottom()`, because `tail` is needed to preserve bottom-follow behavior across deferred layout/height growth.
5. Do not add scroll callbacks to `PermissionView`, `LoginRequiredView`, or `BaseQuestionView` unless tests prove the controller-level path is insufficient.
-`PermissionView` and `LoginRequiredView` should remain retained visual views that show/hide themselves and refresh their parent.
-`BaseQuestionView` should stay a scroll-unaware card shell.
- The shared behavior for those cards should come from the controller-level state-change follow decision plus the generalized pending-button state.
6. In `SessionScrollTest.kt`, update tests around question-style prompt appearance and preserved behavior.
- Keep or add an assertion that the jump button is visible before emitting a question in the middle-scroll test.
- Keep a regression for stale `tail` plus visible jump button if no public-only scenario can reproduce the failed state; keep the reflective helper local to this test file and avoid adding production test hooks.
- Keep existing behavior tests for question navigation from the middle, forced navigation resuming follow, custom question answer growth at bottom, custom question answer growth in the middle, and option selection not resuming follow.
- Add or keep permission bottom/middle tests to prove `AwaitingPermission` preserves scroll when away from bottom and follows when at bottom.
- Add or keep login-required bottom/middle tests to prove `LoginRequired` preserves scroll when away from bottom and follows when at bottom.
- Add explicit button-state assertions for permission and login-required while away from bottom: the button remains visible, switches to the pending-input icon/tooltip, and clicking it still scrolls to bottom.
- Keep the plan follow-up test proving plan prompts do not switch to the pending-input icon.
7. Add a patch changeset for the JetBrains plugin unless an existing branch changeset is intentionally covering this exact fix.
- Suggested release note: `Preserve JetBrains chat scroll position when inline prompts appear while the scroll button is visible.`
## Verification
- From `packages/kilo-jetbrains/`, run `./gradlew test --tests "ai.kilocode.client.session.SessionScrollTest"`.
- From `packages/kilo-jetbrains/`, run `./gradlew typecheck`.
- Do not run root `bun test`.
## Expected Outcome
- If the transcript is away from bottom and the jump button is visible, a newly appearing question, permission prompt, or login-required prompt changes the button state without moving `bar.value`.
- If the transcript is at bottom, a newly appearing question, permission prompt, or login-required prompt follows to the new bottom.
- Clicking the visible pending-input scroll button still jumps to the bottom for every inline prompt card.
- Question carousel next/back/review still jumps to the bottom even if the transcript was previously in the middle.
- Custom question editor growth still follows when the transcript was following and preserves position when it was not.
-`setRequestFocus(false)` / `setFocusable(false)` if we want the popup not to steal focus
-`setCancelOnClickOutside(true)` for outside-click dismissal
-`setCancelKeyEnabled(true)` for Escape dismissal
-`setCancelOnWindowDeactivation(true)` and `setCancelOnOtherWindowOpen(true)` are available and appropriate for cleanup
- IntelliJ platform icons found in `AllIcons`:
- Feedback button: `AllIcons.Ide.Feedback`
- GitHub action: `AllIcons.Vcs.Vendors.Github`
- Support/help action: `AllIcons.Actions.Help` or `AllIcons.General.ContextHelp`
- Discord: no platform icon found. Add a Discord icon by borrowing the existing VS Code/kilo-ui Discord SVG artwork and adapting it into the JetBrains plugin resources with IntelliJ-compatible SVG colors and dark variant if needed.
## Implementation Steps
1. Add localized JetBrains bundle keys.
- Add base English strings to `frontend/src/main/resources/messages/KiloBundle.properties`:
-`feedback.button=Feedback & Support`
-`feedback.dialog.message=We'd love to hear your feedback or help with any issues you're experiencing.`
-`feedback.dialog.github=Report an issue on GitHub`
- Reuse existing cancel text if present; otherwise add `common.cancel=Cancel` only if needed.
- Add matching keys to localized `KiloBundle_*.properties` files. If accurate translations are not already available from the VS Code i18n files, copy the VS Code translations for matching locales where practical and leave English fallback only if the JetBrains bundle mechanism supports it cleanly.
2. Extend `EmptySessionPanel` UI.
- Add a new retained `FeedbackButton`, styled to match VS Code conceptually:
- feedback icon + `Feedback & Support` text
- dashed link-colored border
- transparent background
- hand cursor
- hover state fills with link color and flips foreground/background for contrast
- Use IntelliJ theme APIs instead of raw colors:
- link color: `JBUI.CurrentTheme.Link.Foreground.ENABLED`
- hover link color: `JBUI.CurrentTheme.Link.Foreground.HOVERED` if useful
- editor/panel background from existing session style or `UiStyle.Colors.editorBackground()` / component background
- spacing via `UiStyle.Gap` / `JBUI.Borders`
- rounded arc via `UiStyle.Arc.component()` or `JBUI.getInt("Button.arc", 6)` at paint time
- Add the feedback button below the existing `Show History` button in the empty-state south area, keeping the current centered layout.
- Keep Swing mutations on EDT and preserve retained-component behavior.
3. Implement the feedback popup in frontend Swing code.
- Add a small popup builder method/class inside `EmptySessionPanel.kt` or a new nearby UI file if the file would become too large.
- Content should mirror VS Code:
- Kilo logo at top using existing `/icons/kilo-content.svg`
- message text
- three action buttons/rows for GitHub, Discord, and Customer Support
- every action should have an icon: `AllIcons.Vcs.Vendors.Github` for GitHub, the borrowed Discord SVG for Discord, and `AllIcons.Actions.Help` or `AllIcons.General.ContextHelp` for Customer Support
- optional Cancel button if it fits the JetBrains UX; Escape/outside click already dismisses
- Add the Discord icon asset under `frontend/src/main/resources/icons/` using the plugin's existing icon naming pattern, for example `discord.svg` and `discord_dark.svg` if the borrowed asset needs separate theme variants.
- Load the Discord icon with `IconLoader.getIcon("/icons/discord.svg", EmptySessionPanel::class.java)` or a small Kilo-owned icon object if multiple call sites need it.
- Use `BrowserUtil.browse(url)` to open links.
- Close the popup after opening a URL.
- Use `JBPopupFactory.createComponentPopupBuilder(content, null)` and configure:
-`.setModalContext(false)`
-`.setRequestFocus(false)`
-`.setFocusable(false)` unless keyboard tabbing inside popup is desired; if buttons need keyboard focus, use `.setFocusable(true)` with `.setRequestFocus(false)`
-`.setCancelOnClickOutside(true)`
-`.setCancelKeyEnabled(true)`
-`.setCancelOnWindowDeactivation(true)`
-`.setCancelOnOtherWindowOpen(true)`
-`.setResizable(false)`
-`.setMovable(false)`
- Show with `popup.showUnderneathOf(feedbackButton)`.
4. Add test coverage in `EmptySessionPanelTest.kt`.
- Assert feedback button text uses `KiloBundle.message("feedback.button")`.
- Assert feedback button has hand cursor and visible border semantics.
- Add an injectable browse callback or popup action callback only if needed for testing without launching browsers; keep it minimal and avoid production-only test hooks if component traversal can exercise enough behavior.
- Add a test that clicking the feedback button creates/shows popup content, if feasible in `BasePlatformTestCase`; otherwise test the retained popup content builder/action rows directly through package-internal methods.
- Assert the three action labels exist and URL callbacks map to the same URLs as VS Code.
- Assert the Discord action has an icon, so the plan cannot regress to a text-only Discord row.
5. Add release note.
- This is user-facing for the JetBrains plugin, so add a patch changeset under `.changeset/` unless the repo has a JetBrains-specific release-note mechanism that supersedes changesets.
- Suggested text: `Add Feedback & Support to the JetBrains empty session screen.`
6. Verify.
- Run the focused frontend/JetBrains tests first, preferably from `packages/kilo-jetbrains/`:
- Run JetBrains typecheck from `packages/kilo-jetbrains/`:
-`bun run typecheck` or `./gradlew typecheck`
- If touching only frontend UI and tests pass, no backend/SDK generation is needed.
## Notes
- Do not add Kotlin UI DSL, Compose, or JCEF.
- Do not modify shared opencode files; this work is entirely under `packages/kilo-jetbrains/` plus a changeset.
- Prefer platform icons where available, but explicitly borrow/add the Discord icon because `AllIcons` does not provide one and the popup should match the VS Code button set visually.
Keep JetBrains markdown layout compact while making markdown colors and backgrounds come from the active editor color scheme, and ensure existing theme/editor-setting listeners refresh all existing `MdView` instances after changes.
## Findings
-`SessionUi` already subscribes to `EditorColorsManager.TOPIC` and `LafManagerListener.TOPIC`, then calls `applyStyle(SessionEditorStyle.current())` on the session tree.
- Most markdown consumers already propagate that style through `MdView.applyStyle(style)` via `TextView`, `ReasoningView`, message lists, and session panels.
-`PlanExitView.applyStyle()` sets only font/code font/foreground and does not call `md.applyStyle(style)`, so internal markdown role colors can stay tied to the initial style.
-`MdViewHybrid.applyStyle()` restyles retained HTML panes and code block containers, but retained `CodeField` editors need explicit reapplication of `SessionEditorStyle.applyToEditor()` after creation so syntax highlighting and editor colors follow scheme changes.
-`MdCommon.defaults()` currently mixes editor colors, UI theme colors, and one literal inline-code color fallback. The literal fallback should be removed, and markdown roles should derive from `EditorColorsScheme`/syntax attributes wherever possible.
-`TextView` currently overrides markdown background with `SessionUiStyle.Transcript.bgColor()` (`UiStyle.Colors.bg()`), while prompt markdown already uses `style.editorBackground`. If markdown surfaces should consistently use editor background, normal text views need to use `style.editorBackground` too.
## Implementation Plan
1. Keep compact CSS unchanged.
- Do not reintroduce line-height, margin, padding, heading sizing, or other geometry rules.
- Keep the current role color/background selectors only.
2. Derive markdown role defaults from editor settings.
- Update `MdCommon.defaults(style)` to use `style.editorForeground` and `style.editorBackground` for primary text/background.
- Add small local helper functions for editor attributes, for example foreground/background from `style.editorScheme.getAttributes(key)` and color keys from `style.editorScheme.getColor(key)`.
- Use public IntelliJ editor keys for role colors:
- Links: `CodeInsightColors.HYPERLINK_ATTRIBUTES.foregroundColor`, fallback to platform link color if absent.
- Inline code foreground/background: `DefaultLanguageHighlighterColors.DOC_CODE_INLINE`, fallback to `STRING`, then editor foreground/background.
- Code block foreground/background: `DefaultLanguageHighlighterColors.DOC_CODE_BLOCK`, fallback to editor foreground/background.
- Quote/emphasis/list weak text: comment/doc-comment attributes, fallback to editor foreground or `UIUtil.getContextHelpForeground()` only when the scheme has no useful value.
- Borders/HR/table/code border: editor preview/border color keys such as `EditorColors.PREVIEW_BORDER_COLOR`, fallback to `UiStyle.Colors.contentBorder()`.
- Remove `JBColor(0x...)` or other literal runtime color fallbacks from `MdCommon`.
- Keep public `MdView` API unchanged; role colors stay internal unless a concrete external override need appears.
3. Ensure style propagation reaches every markdown instance.
- Update `PlanExitView.applyStyle(style)` to call `md.applyStyle(style)` before applying its explicit font/code-font/foreground overrides.
- Audit existing markdown callers after the change; keep using the existing `SessionEditorStyleTarget` propagation path rather than adding per-`MdView` theme listeners.
- Keep explicit foreground overrides in `TextView`, `ReasoningView`, and `PlanExitView` where they intentionally set body text role, but let internal markdown role colors refresh from the new style snapshot.
- In `MdViewHybrid.CodeView.style(opts)`, for retained `CodeField` blocks, reapply `style.applyToEditor(editor)` to the underlying editor if it exists.
- Reapply code editor background/scroll pane/viewport backgrounds from `opts.preBg` in the same path.
- Preserve retained component/editor reuse: do not rebuild code block panes just to update style.
5. Align markdown backgrounds with editor settings.
- Keep `MdCommon` default background as `style.editorBackground`.
- Change normal `TextView` markdown background to `style.editorBackground` if the intent is that all markdown surfaces use editor background, matching `PromptView` and session root behavior.
- Preserve `transparent` handling: when `md.opaque = false`, background should still be the editor-derived value for child/code surfaces, but the Swing component should remain non-opaque.
6. Update tests.
- Extend `MdViewTest` to verify markdown role CSS changes when applying a `SessionEditorStyle` backed by a customized editor scheme, especially inline code, code block, link, and border colors.
- Extend `MdViewHybridTest` to assert `applyStyle()` updates retained HTML panes and retained code editors without replacing them, including editor scheme/background changes.
- Add or extend `PlanExitViewTest` so `applyStyle()` refreshes the nested markdown style, not just foreground/font overrides.
- Keep existing compactness expectations: tests should not assert new spacing, padding, margin, line-height, or size rules.
## Verification
- From `packages/kilo-jetbrains/`, run `./gradlew frontend:test --tests '*MdView*'`.
- From `packages/kilo-jetbrains/`, run `./gradlew frontend:test --tests '*PlanExitViewTest*'` if the focused test is not covered by the MdView filter.
- From `packages/kilo-jetbrains/`, run `bun run typecheck`.
## Constraints
- Do not introduce JCEF, Compose, or Kotlin UI DSL.
- Do not add new theme/editor listeners in `MdView`; use the existing `SessionUi` listener and `SessionEditorStyleTarget` propagation path.
- Avoid hardcoded runtime colors in markdown styling; prefer editor scheme attributes/color keys, then platform/theme APIs as non-literal fallbacks.
- Preserve retained Swing behavior and `MdViewHybrid.sync()` component reuse.
- Keep the public `MdView` override API stable unless implementation proves a new external override is required.
Improve JetBrains markdown output so assistant/user transcript markdown visually matches the VS Code webview markdown style while keeping the existing Swing/JBHtmlPane + editor-backed code block architecture.
## Findings
- VS Code markdown styling is split across `packages/ui/src/components/markdown.css`, `packages/kilo-ui/src/components/markdown.css`, `packages/kilo-ui/src/styles/vscode-bridge.css`, and message-part overrides.
- Base VS Code markdown uses 14px sans text, 160% line height, tight first/last margins, same-size medium headings, 12px paragraph spacing, link-colored anchors, compact lists, weak list markers, weak blockquotes with a 2px left border, invisible HR spacing, bordered/padded code blocks, green inline code, and lightly bordered tables.
- The VS Code theme bridge maps markdown roles to editor/theme tokens: heading/link/list/image use `textLinkForeground`, text/strong/code-block use editor foreground, inline code uses charts/syntax green, quote/emphasis use description foreground, HR uses panel border.
- JetBrains markdown is rendered by `MdViewHybrid` and `MdViewHtmlPane`, with shared CSS from `MdCommon.rules()` and defaults from `MdCommon.defaults()`.
- JetBrains currently styles only broad tag font/color, links, code/pre colors, blockquote border/text color, and table border. It lacks VS Code-equivalent spacing, heading/strong/emphasis/list marker/table cell rules, inline-code foreground, blockquote geometry, HR spacing, and code block surface polish.
- JetBrains fenced code blocks are already stronger than VS Code in one respect: they use `EditorTextField` with real IDE syntax highlighting and streaming retention. Preserve this instead of switching to web/JCEF rendering.
## Implementation Plan
1. Expand JetBrains markdown style tokens.
- Add internal fields to `MdStyle` for heading, strong, emphasis, inline code foreground, list marker, HR, table/header, and code block border colors.
- Keep the public `MdView` override API stable unless a new external override is clearly needed.
- Compute defaults in `MdCommon.defaults(style)` from IntelliJ/editor theme sources and centralized Kilo semantic colors where no platform key matches.
- Use `JBColor.namedColor("Kilo.Markdown.*", fallback)` for Kilo-specific markdown palette fallbacks, so themes can override them and runtime code avoids scattered hardcoded colors.
2. Mirror VS Code markdown CSS in `MdCommon.rules()`.
- Add root/body wrapping rules: max width behavior, break-word wrapping, base line-height, and first/last-child margin trimming where supported by `JBHtmlPane` CSS.
- Add heading rules: same base size, medium/bold weight, role-specific color, line height, and bottom spacing.
- Add paragraph, list, list item, nested list, and marker rules. If Swing HTML does not support `::marker`, fall back to `li { color: ... }` plus child text color reset only if supported; otherwise keep list text normal and document the limitation in tests.
- Add strong/emphasis colors matching VS Code token roles.
- Add anchor styling matching VS Code: themed link color, no forced background, and underline behavior where `JBHtmlPane` supports it.
- Add blockquote geometry: 2px left border, 24px vertical margin, 8px left padding, weak text, and normal style.
- Keep HRs visually hidden but spaced consistently with VS Code if the renderer includes them. `MdViewHybrid` currently filters thematic breaks, so this mainly benefits `MdViewHtmlPane` and future reuse.
- Add inline-code foreground and medium font weight. Avoid inline code backgrounds unless the current `JBHtmlPane` configuration already draws them acceptably.
3. Polish JetBrains code block containers without losing IDE highlighting.
- Keep `EditorTextField` for fenced/indented blocks and `JBTextArea` fallback.
- Style `JBScrollPane` code blocks to match VS Code’s `markdown-code` wrapper feel: subtle background, subtle border, rounded-ish platform arc if feasible, 12px-ish padding, and thin horizontal scrollbar behavior.
- Use `SessionUiStyle.View.Code` for geometry constants. Add only minimal new constants there if current values cannot represent the VS Code spacing.
- Separate code block border color from table border internally so table styling can change without affecting code boxes.
- Continue applying `SessionEditorStyle.applyToEditor(ed)` so code blocks follow IDE syntax highlighting and editor font changes.
4. Add file/path affordance parity where safe.
- For markdown links whose `href` looks like a relative file path, keep existing link dispatch so the current caller can open files/URLs appropriately.
- Consider decorating inline code that looks like a path with a `file-link` class only when an `openFile` callback is available through the existing usage path. If the current `MdView` abstraction only has `openUrl`, do not widen it unless the call sites can pass file opening cleanly.
- At minimum, make inline code/path-looking content visually closer to VS Code by using the inline-code foreground and dotted underline for explicit file links where generated HTML contains link/code classes.
- Ensure style updates call `reloadCssStylesheets()` and reassign text only for retained `JBHtmlPane` blocks, not by rebuilding all blocks.
- Keep streaming fenced-code fast path and editor disposal behavior intact.
6. Add focused tests.
- Extend `MdViewTest` and/or `MdViewHybridTest` to assert `overrideSheet()` contains the new VS Code-equivalent rules for headings, strong/emphasis, links, inline code foreground, list/table/blockquote spacing, HR, and code block/table border separation.
- Add component tests for code block pane styling: background, viewport background, border color, padding, scrollbar policy, and retained editor instance after `applyStyle()`.
- Keep existing stress/leak tests green. Add a small stress assertion only if the implementation changes style application semantics.
- Add a changeset: `@kilocode/kilo-jetbrains` patch with user-facing wording such as `Improve markdown readability in JetBrains chat transcripts.`
## Verification
- Run targeted JetBrains markdown tests first from `packages/kilo-jetbrains/`: `./gradlew frontend:test --tests '*MdView*'` if the Gradle module supports it; otherwise run the closest supported targeted Gradle test command.
- Run `bun run typecheck` from `packages/kilo-jetbrains/`.
- If targeted Gradle filtering is unreliable, run `./gradlew test` from `packages/kilo-jetbrains/`.
## Constraints
- Do not introduce JCEF, Compose, or Kotlin UI DSL.
- Keep changes inside `packages/kilo-jetbrains/` and `.changeset/` unless a shared Kilo UI source of truth is explicitly required.
- No `kilocode_change` markers are needed for JetBrains or Kilo UI paths.
- Prefer IntelliJ theme APIs and centralized semantic tokens over scattered literal colors.
- Use Kilo/VS Code-aligned session icons in session views.
- Fix the reasoning header icon.
- Normalize session part collapse/expand chevrons so collapsed/expanded states do not jump between differently sized glyphs.
- Move the session-details toggle away from compaction and place it before the session title.
## Findings
- Session view icons are centralized in `packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/SessionViewIcons.kt` and loaded from `frontend/src/main/resources/icons/views/*.svg`.
- The JetBrains `views` SVGs already mirror the shared VS Code/UI icon paths from `packages/ui/src/components/icon.tsx` for the audited names, including `brain`, `chevron-down`, `chevron-right`, `checklist`, `console`, `warning`, etc.
- The reasoning view currently renders `SessionViewIcons.eye` in `ReasoningView.kt`; VS Code/shared UI uses the `brain` icon for reasoning/thinking surfaces, and `SessionViewIcons.brain` already exists.
- Standard collapsible session parts use `SessionViewIcons.chevronDown` when expanded and `SessionViewIcons.chevronRight` when collapsed in `AbstractSessionPartView.kt`; `QuestionResultView.kt` repeats this pattern manually. The down/right SVG paths have different visual extents.
- The session header currently places the details toggle next to the compact button in the right-side controls in `SessionHeaderPanel.kt`, making the two actions easy to confuse.
## Implementation Steps
1.**Keep icon sources aligned with VS Code/shared UI**
- Treat `packages/ui/src/components/icon.tsx` as the source for Kilo web/session glyph shapes.
- Re-check `SessionViewIcons.kt` entries against available JetBrains assets; only update or add SVGs if a session view uses a Kilo icon missing from `frontend/src/main/resources/icons/views/`.
- Preserve JetBrains SVG theming rules: no `currentColor`; use literal palette colors and dark variants where assets are added or changed.
2.**Fix reasoning icon**
- In `ReasoningView.kt`, change the reasoning header glyph from `SessionViewIcons.eye` to `SessionViewIcons.brain`.
- Add or update test coverage in `ReasoningViewTest.kt` by inspecting the rendered Swing label tree and asserting the reasoning icon is `SessionViewIcons.brain`.
3.**Normalize collapse/expand chevrons for session parts**
- Stop using the mixed `chevronRight`/`chevronDown` pair for collapsible session content.
- Use a single base Kilo chevron glyph for both states, matching the current custom chevron used by the session header (`/icons/chevron-down.svg` / equivalent `SessionViewIcons.chevronDown`).
- Add a shared rotated icon for the opposite state instead of switching to a differently sized right-facing asset. Prefer a small reusable helper or centralized icon field rather than importing header-specific UI into session views.
- Update `AbstractSessionPartView.kt` and `QuestionResultView.kt` to use the normalized chevron pair.
- Leave `QuestionView.kt` navigation chevrons alone unless auditing shows they are being used for collapse/expand; those are previous/next controls, not expand/collapse controls.
4.**Relocate and change header show/hide details toggle**
- In `SessionHeaderPanel.kt`, replace the custom header details chevron with platform `AllIcons` arrows/chevrons, e.g. collapsed = `AllIcons.General.ArrowRight`, expanded = `AllIcons.General.ArrowDown`.
- Move the details toggle out of the right-side controls and into `BorderLayout.WEST` of the header row.
- Rebuild the top header as:
- outer border layout
- west: details toggle button
- center: inner border layout
- inner center: session title
- inner east: horizontal stack/row with price/context and compact button
- Remove the details toggle from the right-side row so compaction remains visually separate from show/hide details.
- Keep existing tooltip/accessibility strings and expansion persistence behavior unchanged.
5.**Tests**
- Update `SessionHeaderPanelTest.kt` to assert:
- collapsed/expanded header details icons use the selected `AllIcons` constants;
- the details toggle persists expansion state as before;
- the details toggle is parented/laid out separately from the compact button.
- Update `AbstractSessionPartViewTest.kt` to assert collapsible parts keep the same icon dimensions across collapsed/expanded states and no longer use the mismatched right/down pair.
- Update `QuestionResultViewTest.kt` similarly because it has its own chevron implementation.
- Update `ReasoningViewTest.kt` for the brain icon.
6.**Verification**
- Run the smallest relevant JetBrains checks from `packages/kilo-jetbrains/`:
-`./gradlew test --tests "ai.kilocode.client.session.views.ReasoningViewTest" --tests "ai.kilocode.client.session.views.base.AbstractSessionPartViewTest" --tests "ai.kilocode.client.session.views.QuestionResultViewTest" --tests "ai.kilocode.client.session.ui.header.SessionHeaderPanelTest"`
-`./gradlew typecheck`
- If the filtered Gradle test syntax is not accepted by the project, run `./gradlew test` from `packages/kilo-jetbrains/` instead.
## Notes
- No shared upstream `opencode` files are involved; changes stay under `packages/kilo-jetbrains/`.
- A changeset may be needed because this is user-facing JetBrains UI polish; confirm existing changeset policy for the private JetBrains package during implementation.
description: Use when releasing the Kilo JetBrains plugin -- resolve a version ("next rc" or explicit), run the prepare workflow, edit and commit a filtered human-readable changelog on the release PR, then watch publish to completion.
---
# JetBrains Release
Use this skill when releasing the Kilo JetBrains plugin.
This skill drives the existing JetBrains release workflows. It must not move, delete, or recreate JetBrains release tags. It must always confirm the resolved version with the user before dispatching the prepare workflow because the prepare workflow creates an immutable `jetbrains/v<version>` tag.
## Preconditions
- Run from the repository root.
-`gh` must be authenticated for `Kilo-Org/kilocode` with permission to dispatch workflows, read PRs, and write contents. Merge permission is only required if the user asks the skill to merge the release PR automatically.
- Check auth with `gh auth status`. For GitHub CLI OAuth, refresh common release scopes with `gh auth refresh -s repo -s workflow`; `repo` covers private-repo contents and PR operations, and `workflow` allows workflow dispatch. If using a fine-grained token instead, grant repository permissions for Actions read/write, Contents read/write, and Pull requests read/write. Merging still requires normal repository collaborator permission or a token/user allowed by branch protection.
- Reference `packages/kilo-jetbrains/RELEASING.md` for manual recovery rules.
- Do not locally check out the generated release branch. The helper scripts update the release branch through GitHub to avoid disturbing the current worktree.
## Version Resolution
Resolve the user's version request:
```bash
bun .kilo/skills/release-jetbrains/script/resolve-version.ts --spec "next rc"
```
Accepted specs:
| Spec | Meaning |
|---|---|
| `next rc` | If the latest JetBrains tag is an RC, increment its `rc.n`; otherwise start the next patch RC at `rc.1`. |
| `next stable` | If the latest JetBrains tag is an RC, use its base version; otherwise use the next patch stable. |
| `x.y.z-rc.n` | Explicit RC release. |
| `x.y.z` | Explicit stable release. |
Show the resolved `version`, `kind`, and default `fromTagDefault` to the user and ask for confirmation before continuing.
## Prepare Workflow
After confirmation, dispatch and watch the prepare workflow:
```bash
bun .kilo/skills/release-jetbrains/script/dispatch-prepare.ts --kind rc --version 7.0.1-rc.7
```
Pass a generous Bash timeout, such as `1800000` ms, because the script blocks on `gh run watch --exit-status`. If the shell times out but the workflow is still running, re-attach with:
```bash
bun .kilo/skills/release-jetbrains/script/dispatch-prepare.ts --kind rc --version 7.0.1-rc.7 --run-id <run-id>
```
The script prints `prNumber`, `prUrl`, `runUrl`, and `branch` on success. Immediately show the `prUrl` to the user so they can open the release PR without asking for it later.
## Changelog Draft
Create a changelog draft after the prepare PR exists:
1. Read the PR body with `gh pr view <pr> --json body`.
2. Extract `JetBrains-From-Tag`, `JetBrains-Tag`, and `## Generated Notes`.
3. Fetch the release range tags if they are missing locally:
Keep JetBrains and CLI/runtime changes. Drop unrelated VS Code, docs, gateway, telemetry, i18n, desktop, and webview-only changes unless they affect the CLI bundled into the JetBrains plugin.
Rewrite terse commit or PR titles into user-facing bullets grouped under `### Added`, `### Fixed`, and `### Changed`. Keep the exact generated header format:
Ask the user to edit the file and confirm when done.
## Commit Changelog
After the user confirms the draft is ready, strip the `<!-- CONTEXT ... -->` block into a temporary cleaned file, then commit the cleaned section to the release branch:
```bash
bun .kilo/skills/release-jetbrains/script/update-changelog.ts --version 7.0.1-rc.7 --file /path/to/clean-section.md
```
The script updates `packages/kilo-jetbrains/CHANGELOG.md` on `jetbrains/release/v<version>` through the GitHub contents API and commits with:
```text
docs(jetbrains): edit changelog for v<version>
```
If `update-changelog.ts` fails with `gh: Not Found (HTTP 404)`, verify the release branch and changelog path with:
```bash
gh api "repos/Kilo-Org/kilocode/contents/packages/kilo-jetbrains/CHANGELOG.md?ref=jetbrains/release/v<version>"
```
Then either fix and retry the helper, or perform the equivalent contents API update using `ref` in the query string.
After the changelog commit succeeds, show the release PR URL again and tell the user that the PR needs manual approval and merge before publishing can continue.
## Approve And Publish
Ask the user to approve the release changelog and metadata. Before merging or publishing, verify the PR approval and required checks are green:
Do not merge or publish while required checks are failing unless the user explicitly gives a maintainer override.
If a required check fails from an apparent flake, rerun only the failed jobs and wait for the run to finish:
```bash
gh run rerun <run-id> --failed
gh run watch <run-id> --exit-status
```
By default, have the user merge the release PR manually in GitHub, then watch the publish workflow:
```bash
bun .kilo/skills/release-jetbrains/script/watch-publish.ts --pr <number> --version 7.0.1-rc.7
```
Only merge automatically when the user explicitly asks for it and `gh` has merge permission:
```bash
bun .kilo/skills/release-jetbrains/script/watch-publish.ts --pr <number> --version 7.0.1-rc.7 --merge
```
Pass a generous Bash timeout, such as `1800000` ms. If the shell times out, re-attach with:
```bash
bun .kilo/skills/release-jetbrains/script/watch-publish.ts --pr <number> --version 7.0.1-rc.7 --run-id <run-id>
```
If `watch-publish.ts --merge` reports that the PR is already merged, or a transient GitHub API `5xx` interrupts publish-run discovery, rerun without `--merge`:
```bash
bun .kilo/skills/release-jetbrains/script/watch-publish.ts --pr <number> --version <version>
```
Report the Marketplace channel and GitHub Release URL. RC versions publish to the `eap` channel; stable versions publish to the default Marketplace channel.
## Recovery
- If prepare created the tag but failed before creating a PR, rerun prepare for the same version. The existing workflow reuses the tag if it points to the same commit.
- If a tag points to an unexpected SHA, stop and inspect manually. Do not move or delete release tags casually.
- If release PR checks fail from an apparent flake, use `gh run rerun <run-id> --failed`, then `gh run watch <run-id> --exit-status` before publishing.
- If publish fails after merge, rerun the failed workflow only if Marketplace did not already accept the version.
- If Marketplace succeeds but GitHub Release upload fails, manually create or edit the GitHub Release for `jetbrains/v<version>` using the reviewed changelog.
thrownewError(`No new ${workflow} run appeared after dispatch`)
}
asyncfunctionruns() {
return(await$`gh run list --repo ${repo} --workflow ${workflow} --event workflow_dispatch --json databaseId,createdAt,status --limit 100`.json())as{
databaseId: number
createdAt: string
status: string
}[]
}
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.