Skip to content

feat(jetbrains): add Kilo Swarm board support - #14253

Merged
kirillk merged 8 commits into
mainfrom
gentle-iceberg
Sep 17, 2026
Merged

kirillk merged 8 commits into
mainfrom
gentle-iceberg

Conversation

@kirillk

@kirillk kirillk commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Issue

No tracking issue exists for JetBrains Swarm support; I searched open and closed issues and found none. Raising this as an exception rather than filing a retroactive issue — happy to open one if maintainers prefer.

Context

Kilo Swarm had no JetBrains client support, but it was already partially live there. The board gate is a CLI config key, and the plugin only layers KILO_CONFIG_CONTENT on top of global config, so anyone who enabled Swarm in VS Code already had board_post / board_read running inside JetBrains sessions — rendered as raw JSON in the generic tool card, with no toggle to control it and no way to read or reset the board.

This brings the plugin to VS Code parity: a settings toggle, readable board messages in the transcript, and a board viewer.

Implementation

Config. shared_agent_board is a top-level CLI config key (it was retired out of experimental.*, where retireExperimentalFlags now deletes and warns on the old path). It is wired through ConfigDto / ConfigPatchDto and the parser, and surfaces as a toggle on the Agent Behavior page, mirroring where VS Code puts it. Default is on, matching BoardEnabled.resolve, and the toggle always writes an explicit boolean so an opt-out survives default-on resolution. This converts AgentBehaviorConfigurable from a navigation-only stub into a draft/apply page; the sub-page links are unchanged and the toggle sits below them behind its own separator.

Rendering. BoardToolParser + BoardToolView render board_post / board_read as route plus message via the markdown pipeline. canRender returns false for pending, running, or unparseable payloads so anything unexpected falls back to the generic ToolView rather than showing a broken card.

Viewer. SessionBoardDialog pages backward through GET /kilocode/session/:id/board, opens a participating subagent in the existing read-only tab, and resets under a revision guard where HTTP 409 reloads instead of retrying blindly. These routes are called over raw HTTP because they are newer than the generated client built from the pinned CLI release.

Three things worth reviewer attention:

  • Entry-point gating is a probe, not a local check. The header icon and menu action stay hidden until the board actually holds a message. Emptiness cannot be derived from the transcript, because a subagent's board_post is recorded in its own session — the root transcript can stay empty while the board is not. The probe is event-driven (session load, busy→idle, config change, dialog close) and never polled, and message traffic skips it once the board is known non-empty so a busy turn does not fire one request per message.
  • The dialog does not persist its size. getDimensionServiceKey is deliberately absent, because a remembered width defeats the frame-relative clamp on reopen. Manual resizes are therefore not remembered — say the word if that trade is wrong.
  • Sizing rules are pure functions (boardWidth, boardWrap) rather than inline logic. That is not gold-plating: a headless test has no IDE frame, so asserting the clamp through the live dialog silently passes without checking anything. I hit exactly that and it is why these are extracted.

Requires CLI ≥ 7.6.0. The board endpoints first shipped in 7.6.0; the repo pin is already 7.7.2, and I confirmed the routes are present in that release binary. No packages/opencode/ changes, so there is no upstream merge-conflict surface.

Screenshots / Video

Screen Shot 2026-09-17 at 11 40 47 AM Screen Shot 2026-09-17 at 12 10 59 PM
before after
board_post rendered as raw JSON in the generic tool card; no toggle; no viewer Board message rendered as session → ALL with an INFO badge; Kilo Swarm toggle under Agent Behavior; board viewer titled Kilo Swarm — <session name> with a collapsible info banner

How to Test

Manual/local verification

Executed by the agent:

  • ./gradlew typecheck from packages/kilo-jetbrains/ — clean.
  • ./gradlew test from packages/kilo-jetbrains/ — full suite green across shared, frontend, backend.
  • Targeted suites: SessionBoardDialogTest (16), BoardDialogSizeTest (7), BoardToolParserTest (16), SessionBoardActionsTest (13), AgentBehaviorSettingsUiTest (6), AgentBehaviorSettingsStateTest (7), KiloBackendSessionManagerBoardTest (9), plus parser/app-service round trips for the config key.
  • bun run script/check-md-table-padding.ts and check-kilocode-change — pass.
  • Bundle verification: key parity for session.board.* across all 18 locales against the base bundle, no duplicates, no unescaped apostrophes in {0} strings, and one sentence terminator per locale intro.

Executed by the human author, in a sandbox IDE:

  • Opened the board from the session header, confirmed the message list, banner expand/collapse, and the reset flow.
  • Found a genuine HTTP 404 in an earlier revision caused by the then-pinned CLI 7.5.15 lacking the routes; this is what established the ≥ 7.6.0 requirement.

Reviewer test steps

  1. From packages/kilo-jetbrains/, run ./gradlew --no-configuration-cache runIdeSplitMode.
  2. Confirm Settings → Kilo Code → Agent Behavior shows a Kilo Swarm toggle below the sub-page links, on by default.
  3. In a session, prompt: Use board_post to send an INFO message to ALL saying "hello board", then board_read it back.
  4. Confirm the board_post card renders as <session> → ALL with an INFO badge instead of JSON.
  5. Confirm a board icon appears in the session header once the first message exists, and that View Kilo Swarm appears in the session context menu.
  6. Open the viewer: title reads Kilo Swarm — <session name>, the info banner toggles between Expand and Collapse, and Reset board prompts before clearing.
  7. Turn the toggle off and confirm the board tools and both entry points disappear.

Blocked checks and substitute verification

  • End-to-end sandbox verification of the final revision was not re-run by the agent; this environment cannot drive an interactive IDE. Substitute verification was the unit/integration coverage above plus the human author's sandbox runs during development. The last human sandbox pass predates the final wording and banner changes, which are covered by tests but not visually re-confirmed.
  • Translation fluency is unverified for most of the 18 locales. Mechanics (parity, escaping, placeholders, terminators) are checked programmatically, and terminology was grounded in each locale's existing bundle entries, but a native-speaker pass on th, ar, ko, and bs would be worthwhile before release.

Checklist

  • Issue linked above, or exception explained
  • Tests/verification described
  • Screenshots/video included for visual changes, or marked N/A — pending author attachment, see above
  • Changeset considered for user-facing changes — .changeset/jetbrains-kilo-swarm-board.md (minor)
  • I personally reviewed the diff and can explain the changes, including any AI-assisted work.

Get in Touch

Bring the JetBrains plugin to VS Code parity for Kilo Swarm, which was
previously reachable only through the CLI: the board tools were already
active in JetBrains sessions but rendered as raw JSON, with no toggle and
no way to read the board.

Wire the top-level shared_agent_board config key through the DTO and
parser layers and expose it as a toggle on the Agent Behavior settings
page, defaulting to on to match the CLI's BoardEnabled.resolve. Render
board_post/board_read as route plus message instead of raw JSON, falling
back to the generic tool card when a payload cannot be parsed. Add a
board viewer reached from the session header or the session menu that
pages backward, opens a participating subagent, and resets the board
under a revision guard.

The entry points stay hidden until the board actually holds a message,
probed event-driven rather than polled, because a subagent's post lands
in its own transcript and cannot be derived locally.
@kilo-code-bot

kilo-code-bot Bot commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (2 files, incremental since `6562943`)
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt - 0 issues; showBoard() now delegates to the new openBoard(). The previous WARNING stays resolved: the dialog's disposable is still registered under SessionUi, so closing the session tab disposes the non-modal board, and the post-close refreshBoard() callback remains guarded by if (!disposed).
  • packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/SessionBoardActionsTest.kt - 0 issues; previous SUGGESTION resolved. The test no longer hand-wires Disposer.register(ui, dialog.disposable); it calls openBoard(), the same path showBoard() uses, so the production lifetime wiring is what is asserted. The new test openBoard returns null when the board does not apply covers the early-return branch.

Verified the extraction is behavior-preserving: openBoard() keeps the same controller.id/board guards, the same Telemetry.send, and the same two Disposer.register calls, and showBoard() reduces to openBoard()?.show(). SessionBoardDialog is internal, so the internal fun openBoard(): SessionBoardDialog? signature is a valid same-module seam rather than a visibility leak, and showBoard() is a real production consumer.

Previous Review Summaries (6 snapshots, latest commit 6562943)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 6562943)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1
Issue Details (click to expand)

SUGGESTION

File Line Issue
packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/SessionBoardActionsTest.kt 183 The new test wires Disposer.register(ui, dialog.disposable) itself and never calls showBoard(), so it passes even if the production registration is removed; it verifies Disposer semantics rather than the fix it was added to guard
Files Reviewed (3 files, incremental since `eee5a486`)
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt - 0 issues; previous WARNING resolved at SessionUi.kt:1120 (dialog disposable now registered under SessionUi, and disposal goes through Disposer.dispose(ui) in SessionHost.disposeUi)
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/board/SessionBoardDialog.kt - 0 issues; previous SUGGESTION resolved (unused SessionBoardDialogHandle removed, no remaining references)
  • packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/SessionBoardActionsTest.kt - 1 issue

Fix these issues in Kilo Cloud

Previous review (commit eee5a48)

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt 1119 The now non-modal board dialog is not registered on the session disposable, so it can outlive a disposed SessionUi and retain it (plus its coroutine scope) until closed manually

SUGGESTION

File Line Issue
packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/board/SessionBoardDialog.kt 43 SessionBoardDialogHandle is declared and implemented but never referenced as a type; its KDoc is stale
Files Reviewed (27 files)
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/SessionUi.kt - 1 issue
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/board/SessionBoardDialog.kt - 1 issue
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/BoardToolView.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/TaskToolView.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/ToolSupport.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/settings/agents/AgentBehaviorSettingsUi.kt
  • packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/board/SessionBoardDialogTest.kt
  • packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/BoardToolViewTest.kt
  • 19 KiloBundle*.properties base/locale message bundles (board key renames to showMore/showLess/loadMore, new action.Kilo.Session.Board.*, settings.agentBehavior.swarm.*, translated settings.agentBehavior.saving / save.failed) - verified key parity across all bundles, no duplicates, no leftover old keys

Fix these issues in Kilo Cloud

Previous review (commit a573611)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (1 file)
  • packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/BoardToolViewTest.kt

Previous review (commit d5bee8e)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1
Issue Details (click to expand)

SUGGESTION

File Line Issue
packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/BoardToolViewTest.kt 53 Body assertions now read the source markdown via markdown() instead of the rendered body; line 68 is a tautology
Files Reviewed (3 files)
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/tool/BoardToolView.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/UiStyle.kt
  • packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/views/BoardToolViewTest.kt - 1 issue

Fix these issues in Kilo Cloud

Previous review (commit fa6d784)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1
Issue Details (click to expand)

SUGGESTION

File Line Issue
packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/UiStyle.kt 325 swarmAvatarCount() is added but has no callers
Files Reviewed (7 files)
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/board/BoardAvatars.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/UiStyle.kt
  • packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_ja.properties
  • packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_ko.properties
  • packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_zh_CN.properties
  • packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle_zh_TW.properties
  • packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/session/board/BoardAvatarsTest.kt

Fix these issues in Kilo Cloud

Previous review (commit de6397c)

Status: 6 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 6
Issue Details (click to expand)

SUGGESTION

File Line Issue
KiloBundle_zh_TW.properties 30 看枕 (pillow) used instead of 看板 (board) in 8 strings
KiloBundle_ko.properties 35 늫기 is not a word; Close should be 닫기
KiloBundle_ko.properties 30 가지물로 / 통향 / 대슴 typos
KiloBundle_ja.properties 30 なけれ missing ば in the negative conditional
BoardAvatars.kt 34 Module-scoped avatar cache is never evicted
BoardAvatars.kt 25 Numeric JBColor hex literals in runtime UI code
Files Reviewed (60 files)
  • .changeset/jetbrains-kilo-swarm-board.md
  • packages/kilo-docs/pages/getting-started/settings/index.md
  • packages/kilo-jetbrains/backend/src/main/kotlin/ai/kilocode/backend/app/KiloBackendSessionManager.kt
  • packages/kilo-jetbrains/backend/src/main/kotlin/ai/kilocode/backend/cli/KiloCliDataParser.kt
  • packages/kilo-jetbrains/backend/src/main/kotlin/ai/kilocode/backend/rpc/KiloSessionRpcApiImpl.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/.../actions/ShowSessionBoardAction.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/.../app/KiloSessionService.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/.../session/SessionActions.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/.../session/SessionUi.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/.../session/board/BoardAvatars.kt - 2 issues
  • packages/kilo-jetbrains/frontend/src/main/kotlin/.../session/board/SessionBoardDialog.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/.../session/model/SessionModel.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/.../session/ui/header/SessionHeaderPanel.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/.../session/views/ViewFactory.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/.../session/views/board/BoardToolParser.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/.../session/views/tool/BoardToolView.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/.../session/views/tool/ToolSupport.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/.../settings/agents/AgentBehaviorConfigurable.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/.../settings/agents/AgentBehaviorSettingsState.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/.../settings/agents/AgentBehaviorSettingsUi.kt
  • packages/kilo-jetbrains/frontend/src/main/resources/kilo.jetbrains.frontend.xml
  • packages/kilo-jetbrains/frontend/src/main/resources/messages/KiloBundle.properties
  • 18 KiloBundle_*.properties locale bundles - 3 issues (ko, ja, zh_TW)
  • 20 test sources under packages/kilo-jetbrains/{backend,frontend}/src/test/kotlin/...
  • packages/kilo-jetbrains/shared/src/main/kotlin/.../rpc/KiloSessionRpcApi.kt
  • packages/kilo-jetbrains/shared/src/main/kotlin/.../rpc/dto/BoardDto.kt
  • packages/kilo-jetbrains/shared/src/main/kotlin/.../rpc/dto/KiloAppStateDto.kt

Verified against the CLI (packages/opencode/src/kilocode/board/store.ts, httpapi/groups/kilocode.ts, tool/board.ts): the request params (directory, before, limit 1-50, reset revision), response keys (ownerSessionID, revision, messages, cursor, hasMore) and per-message keys (id, timestamp, from, to, fromLabel, toLabel, type, body, reply_to) all match the Windows plugin DTOs, so the board rendering path is contract-correct. Settings save follows the existing updateConfigAsync(change, done) draft-page pattern. Adding shared_agent_board to the plugin DTOs introduces no new Config.Info field in packages/opencode/, so no cloud schema mirror is needed.

Fix these issues in Kilo Cloud


Reviewed by deepseek-v4.1-flash · Input: 0 · Output: 0 · Cached: 0

Review guidance: REVIEW.md from base branch main

Correct Traditional Chinese 看枕 (pillow) to 看板 (board) and the
simplified 中断 to 中斷, the Japanese negative conditional なけれ to
なければ, and four Korean words that were not real words. Also drop the
space after the sentence-ending 。 in ja/zh_CN/zh_TW, where it is wrong
typography, and tighten a Korean particle.

Move the swarm avatar palette into UiStyle.Colors behind semantic named
color keys so themes can override it, per the plugin UI guidance against
numeric JBColor literals in runtime code, and resolve the fill at paint
time so a cached icon follows a Look and Feel change.

Key the avatar cache on the drawn glyph rather than the participant id.
Session ids never repeat, so the old key grew the map for the life of the
IDE; the glyph space is bounded and reuses entries instead.
Comment thread packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/UiStyle.kt Outdated
swarmAvatarCount() had no callers; remove it rather than leave
speculative API in UiStyle.Colors.

Also remove BoardToolView.bodyMarkdownText(), which existed only so a
test could read the body. Tests now assert through markdown(), the same
API the body sync and header popup use, per the plugin rule against
production methods whose only purpose is test access.
The previous revision asserted through markdown(), which re-derives from
the tool part rather than reading the body view, so the assertions passed
whether or not syncBody() ran and the collapse-cycle check compared a
value against itself.

Read the body's JBHtmlPane from the component tree instead, and compare
pane identity across collapse cycles so lazy creation and reuse stay
covered. Verified by temporarily stubbing syncBody(), which now fails all
three tests.
… body

The board stays useful while the session and its subagents keep working,
so it no longer blocks the IDE. showAndGet() throws on a non-modal dialog,
so the handle and caller move to show(), and the post-close board re-probe
moves onto the dialog's disposable.

Rename the banner toggle to Show more / Show less, and rename load_more to
loadMore so the key matches the bundle's camelCase convention.

Indent the board message body under the header title instead of the card
edge, which is what the task card already did. Its private border helper
moves to ToolSupport as toolBodyBorder so both cards share one definition
rather than drifting.

Drop a duplicate settings.agentBehavior.save.failed this PR had added on
top of the existing shared key, restore that key's original English, and
reuse the existing saving / save.failed keys instead of introducing new
ones. Translate both shared keys, which were English-only, and complete
the swarm settings and action strings across all 18 locales.
The board became non-modal, so it now outlives showBoard(). Nothing
disposed it when the session went away, so closing the session tab left
the window alive holding a disposed SessionUi, its service, and the
dialog's own coroutine scope. Register the dialog's disposable on the
session, which disposes the dialog and closes the window with the tab,
and skip the post-close re-probe when the session is the thing going away.

Also drop SessionBoardDialogHandle: nothing referenced it as a type and
its documented purpose no longer held once the dialog stopped being modal.
kirillk and others added 2 commits September 17, 2026 13:11
The previous test re-created the registration from showBoard() instead of
calling it, so removing the production statement left the test green. It
guarded a copy of the wiring, not the wiring.

Split the create-and-bind step into openBoard(), which showBoard() now
delegates to, and assert disposal through that call. Verified by removing
the production registration, which now fails the test.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants