Merge branch 'main' into fix/grok-4.5-reasoning-variants

This commit is contained in:
Christiaan Arnoldus
2026-07-15 13:25:32 +02:00
committed by GitHub
1899 changed files with 143443 additions and 39638 deletions
-5
View File
@@ -1,5 +0,0 @@
---
"kilo-code": patch
---
Fix the reasoning-variant and mode dropdowns being clipped inside the New Worktree dialog. The dialog's scroll-container overflow escape now covers all inline selector popovers, not just the model picker, so dropdowns render fully above the prompt input.
-5
View File
@@ -1,5 +0,0 @@
---
"@kilocode/cli": patch
---
Fix cloud session fork commands so they import cloud sessions before validating the local session.
@@ -1,5 +0,0 @@
---
"@kilocode/cli": patch
---
Harden config credential substitution against untrusted project config. Environment references (`{env:VAR}`) now resolve only in trusted config (global config, `KILO_CONFIG`, `KILO_CONFIG_CONTENT`, and org/MDM-managed config); a project-committed `kilo.json` / `opencode.json` can no longer use them. File references (`{file:...}`) still work in project config but are confined to the project root, so absolute paths, `../` traversal, and symlink escapes are rejected. This closes a path where a malicious repository could exfiltrate local secrets to an attacker-controlled `baseURL`.
-5
View File
@@ -1,5 +0,0 @@
---
"@kilocode/kilo-console": patch
---
Replace the hardcoded "K" letter in the Kilo Console navbar with the real Kilo logo, and drop the redundant "Kilo" wordmark since the logo already carries the name.
@@ -1,5 +0,0 @@
---
"@kilocode/kilo-web-ui": patch
---
Show trailing external-link icons for Kilo Console profile links that open in a new tab.
-5
View File
@@ -1,5 +0,0 @@
---
"@kilocode/cli": patch
---
Fix Amazon Bedrock models returning no output. A smithy dependency version-skew made the Bedrock event-stream decoder silently fail under the browser build condition, so every Bedrock request completed with an empty response.
@@ -1,5 +0,0 @@
---
"@kilocode/kilo-jetbrains": patch
---
Fix prompt submission in JetBrains IDEs when sending messages with file or git-change mentions.
@@ -1,5 +0,0 @@
---
"@kilocode/kilo-jetbrains": patch
---
Fix unreliable clicks on inline action buttons (Connect, OAuth, Disconnect, Enable) in the JetBrains provider, agent, and MCP settings lists so the whole button is clickable.
-5
View File
@@ -1,5 +0,0 @@
---
"kilo-code": patch
---
Fix multiline bidirectional prompt input rendering and file mention arrow navigation.
-5
View File
@@ -1,5 +0,0 @@
---
"@kilocode/kilo-jetbrains": patch
---
Show a focus outline around the JetBrains prompt input.
-5
View File
@@ -1,5 +0,0 @@
---
"kilo-code": minor
---
Render images inline in the chat view when the agent reads an image file. Images appear below the read tool card and can be clicked to open a full-size preview.
-5
View File
@@ -1,5 +0,0 @@
---
"@kilocode/kilo-jetbrains": patch
---
Reduce JetBrains plugin size by downloading the Kilo Core release on first connect.
-5
View File
@@ -1,5 +0,0 @@
---
"@kilocode/kilo-jetbrains": patch
---
Show subagent tool activity inline in JetBrains session transcripts.
-5
View File
@@ -1,5 +0,0 @@
---
"@kilocode/kilo-jetbrains": patch
---
Fix JetBrains prompt pickers so reasoning effort opens above the button and expanded model details still allow one-click model selection.
@@ -1,5 +0,0 @@
---
"@kilocode/kilo-jetbrains": patch
---
Hide the JetBrains editor floating toolbar from the Kilo prompt input.
-5
View File
@@ -1,5 +0,0 @@
---
"@kilocode/kilo-jetbrains": patch
---
Remove old JetBrains CLI binaries so they no longer accumulate in the IDE cache. Only the active version is kept, the downloaded archive is deleted after extraction, and reinstalling re-downloads a fresh binary.
@@ -1,5 +0,0 @@
---
"@kilocode/kilo-jetbrains": patch
---
Balance JetBrains shell command tooltip padding when a horizontal scrollbar is present.
-5
View File
@@ -1,5 +0,0 @@
---
"@kilocode/kilo-jetbrains": patch
---
Increase JetBrains todo checklist inner padding.
@@ -1,5 +0,0 @@
---
"@kilocode/kilo-jetbrains": patch
---
Use the standard transcript font for JetBrains prompt text and custom question responses.
-5
View File
@@ -1,5 +0,0 @@
---
"@kilocode/kilo-web-ui": patch
---
Use the same font stack as Kilo Cloud (Inter variable for sans, Roboto Mono variable for mono, JetBrains Mono variable as the alt-mono token) in Kilo Console. Fonts are now self-hosted as woff2 in `@kilocode/kilo-web-ui`, so Inter no longer relies on the OS having it installed.
-7
View File
@@ -1,7 +0,0 @@
---
"@kilocode/cli": minor
"@kilocode/sdk": minor
"@kilocode/kilo-memory": minor
---
Add opt-in project memory commands, tools, automatic capture, and public API support.
-5
View File
@@ -1,5 +0,0 @@
---
"kilo-code": minor
---
Integrate project memory into the VS Code extension: memory status and controls in the Context settings tab, task-header and assistant-message affordances, the `/memory` prompt command, and Show/Toggle Project Memory command-palette entries.
-5
View File
@@ -1,5 +0,0 @@
---
"kilo-code": patch
---
Allow Shift+Arrow selections in the prompt input to shrink back across file mentions.
-6
View File
@@ -1,6 +0,0 @@
---
"@kilocode/cli": minor
"kilo-code": minor
---
Add a reload action that reboots the per-directory instance, picking up config, skills, agents, commands, and MCP prompts changed on disk. Sessions and history are preserved. Surfaces: `/reload` in the CLI palette and editor chat, a reload button in the task header and settings panel, the `Kilo Code: Reload Config and Skills` command, and a `POST /instance/reload` HTTP endpoint. The endpoint returns 409 while a session is actively running.
-5
View File
@@ -1,5 +0,0 @@
---
"@kilocode/cli": minor
---
Support provider-aware model discovery and selection for remote Cloud sessions.
-5
View File
@@ -1,5 +0,0 @@
---
"@kilocode/cli": patch
---
Show the Remote badge in the TUI prompt status area when remote session relay is enabled.
-5
View File
@@ -1,5 +0,0 @@
---
"kilo-code": patch
---
Fix the model usage panel showing just "free" for auto-routed sessions. The routed model id (e.g. `tencent/hy3:free`) is now displayed correctly instead of being collapsed to its `:free` suffix.
@@ -1,5 +0,0 @@
---
"kilo-code": patch
---
Widen the Additional Writable Paths input in the Sandboxing settings so longer filesystem paths are easier to read while typing.
-5
View File
@@ -1,5 +0,0 @@
---
"@kilocode/cli": patch
---
Show live session spend in the TUI sidebar while an assistant turn is still running.
-5
View File
@@ -1,5 +0,0 @@
---
"@kilocode/cli": minor
---
Add vim modal editing to the CLI prompt input. Enable it with `"vim": true` in `tui.jsonc`, the `Toggle vim mode` command in the command palette, or the `/vim` slash command. Supports NORMAL-mode motions (h/j/k/l, w/b/e, 0/^/$, gg/G, counts), edits (x, dd, dw, cw, D, C, r, yy/p, u, Ctrl+r), insert transitions (i/a/A/I/o/O), and VISUAL / VISUAL-LINE mode (v/V with selection-extending motions, d/x/c/s/y, o to swap ends), with a mode indicator and matching cursor shape.
@@ -1,5 +0,0 @@
---
"kilo-code": patch
---
Preserve the selected mode when sending the first message in a new VS Code task so the chosen model is paired with the correct agent instructions.
-5
View File
@@ -1,5 +0,0 @@
---
"kilo-code": patch
---
Wait for microphone capture to start before showing voice input as recording.
+4
View File
@@ -35,3 +35,7 @@ packages/kilo-i18n/src/de.ts linguist-generated=false
# Auto-generated CLI reference docs
packages/kilo-docs/markdoc/partials/cli-commands-table.md linguist-generated=true
packages/kilo-docs/pages/code-with-ai/platforms/cli-reference.md linguist-generated=true
# Upstream generated migration artifacts
packages/core/migration/**/snapshot.json linguist-generated
packages/core/src/database/migration.gen.ts linguist-generated
+15 -4
View File
@@ -33,7 +33,10 @@ runs:
shell: bash
run: echo "dir=$(bun pm cache)" >> "$GITHUB_OUTPUT"
# Restoring and extracting the ~1 GB cache took 2m23s on Windows, while a # kilocode_change
# fresh install took 1m27s. Keep Windows off this cache until that reverses. # kilocode_change
- name: Restore Bun dependencies
if: runner.os != 'Windows' # kilocode_change
id: bun-cache
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
@@ -84,16 +87,24 @@ runs:
# Workaround for patched peer variants
# e.g. ./patches/ for standard-openapi
# https://github.com/oven-sh/bun/issues/28147
# https://github.com/oven-sh/bun/issues/28147 # kilocode_change
if [ "$RUNNER_OS" = "Windows" ]; then
bun install --linker hoisted ${{ inputs.install-flags }}
# kilocode_change start
if ! bun install --frozen-lockfile --linker hoisted ${{ inputs.install-flags }}; then
echo "::warning::Bun install failed on Windows; retrying with conservative extraction"
sleep 5
BUN_FEATURE_FLAG_DISABLE_STREAMING_INSTALL=1 \
bun install --frozen-lockfile --linker hoisted --network-concurrency 16 ${{ inputs.install-flags }}
fi
# kilocode_change end
else
bun install ${{ inputs.install-flags }}
bun install --frozen-lockfile ${{ inputs.install-flags }} # kilocode_change
fi
shell: bash
# Do not upload a Windows cache that Windows jobs intentionally never restore. # kilocode_change
- name: Save Bun dependencies
if: steps.bun-cache.outputs.cache-hit != 'true' && github.event_name != 'pull_request' && github.event_name != 'pull_request_target'
if: runner.os != 'Windows' && steps.bun-cache.outputs.cache-hit != 'true' && github.event_name != 'pull_request' && github.event_name != 'pull_request_target' # kilocode_change
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ steps.cache.outputs.dir }}
@@ -0,0 +1,21 @@
# kilocode_change - new file
name: "Setup Linux Sandbox"
description: "Build the Linux bubblewrap helper"
runs:
using: "composite"
steps:
- name: Setup Zig
run: |
curl --fail --location --retry 3 \
https://ziglang.org/download/0.14.0/zig-linux-x86_64-0.14.0.tar.xz \
--output "$RUNNER_TEMP/zig.tar.xz"
echo "473ec26806133cf4d1918caf1a410f8403a13d979726a9045b421b685031a982 $RUNNER_TEMP/zig.tar.xz" | sha256sum --check --status
tar -xJf "$RUNNER_TEMP/zig.tar.xz" -C "$RUNNER_TEMP"
echo "$RUNNER_TEMP/zig-linux-x86_64-0.14.0" >> "$GITHUB_PATH"
shell: bash
- name: Build bubblewrap helper
run: |
bun packages/opencode/script/kilocode/bubblewrap.ts --arch x64 --output "$RUNNER_TEMP/bwrap"
echo "KILO_BWRAP_PATH=$RUNNER_TEMP/bwrap" >> "$GITHUB_ENV"
shell: bash
+6
View File
@@ -39,6 +39,12 @@ jobs:
distribution: temurin
java-version: "21"
- name: Free disk space for CodeQL
shell: bash
run: |
sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/lib/android /usr/local/share/boost
docker system prune --all --force || true
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
with:
+15 -5
View File
@@ -57,14 +57,24 @@ jobs:
BUILD_LOG=$(mktemp)
trap 'rm -f "$BUILD_LOG"' EXIT
# Build with fakeHash to trigger hash mismatch and reveal correct hash
nix build ".#packages.${SYSTEM}.node_modules_updater" --no-link 2>&1 | tee "$BUILD_LOG" || true
HASH=""
MAX_ATTEMPTS=3
for ((ATTEMPT = 1; ATTEMPT <= MAX_ATTEMPTS; ATTEMPT++)); do
# Build with fakeHash to trigger hash mismatch and reveal correct hash
nix build ".#packages.${SYSTEM}.node_modules_updater" --no-link 2>&1 | tee "$BUILD_LOG" || true
# Extract hash from build log with portability
HASH="$(nix run --inputs-from . nixpkgs#gnugrep -- -oP 'got:\s*\Ksha256-[A-Za-z0-9+/=]+' "$BUILD_LOG" | tail -n1 || true)"
HASH="$(nix run --inputs-from . nixpkgs#gnugrep -- -oP 'got:\s*\Ksha256-[A-Za-z0-9+/=]+' "$BUILD_LOG" | tail -n1 || true)"
[ -n "$HASH" ] && break
if [ "$ATTEMPT" -lt "$MAX_ATTEMPTS" ]; then
echo "::warning::Attempt ${ATTEMPT}/${MAX_ATTEMPTS} produced no hash for ${SYSTEM}; retrying in $((ATTEMPT * 10))s"
sleep $((ATTEMPT * 10))
fi
done
if [ -z "$HASH" ]; then
echo "::error::Failed to compute hash for ${SYSTEM}"
echo "::error::Failed to compute hash for ${SYSTEM} after ${MAX_ATTEMPTS} attempts"
cat "$BUILD_LOG"
exit 1
fi
+2
View File
@@ -35,7 +35,9 @@ concurrency: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.version || inpu
permissions:
id-token: write
contents: write
issues: write # kilocode_change - label automated JetBrains CLI pin bump PRs
packages: write
pull-requests: write # kilocode_change - create automated JetBrains CLI pin bump PRs
jobs:
version:
+15 -4
View File
@@ -42,8 +42,6 @@ jobs:
needs: changes
if: github.event_name == 'workflow_dispatch' || needs.changes.outputs.jetbrains == 'true'
runs-on: blacksmith-4vcpu-ubuntu-2404
container:
image: ghcr.io/kilo-org/build/jetbrains:24.04
defaults:
run:
shell: bash
@@ -56,8 +54,21 @@ jobs:
- name: Mark workspace as git-safe
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
- name: Install dependencies
run: bun install
- name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version-file: package.json
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "21"
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
with:
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
- name: Run JetBrains unit tests
run: bun script/test-ci.ts
+82 -33
View File
@@ -30,6 +30,7 @@ jobs:
pull-requests: read
outputs:
settings: ${{ steps.matrix.outputs.settings }}
general: ${{ steps.matrix.outputs.general }}
steps:
- name: Checkout repository
if: github.event_name != 'workflow_dispatch'
@@ -57,14 +58,16 @@ jobs:
env:
GENERAL: ${{ github.event_name != 'pull_request' || steps.filter.outputs.general == 'true' }}
run: |
if [ "$GENERAL" = "true" ]; then
echo 'settings=[{"name":"linux","host":"blacksmith-4vcpu-ubuntu-2404","run":true},{"name":"macos","host":"macos-15","run":true},{"name":"windows","host":"blacksmith-4vcpu-windows-2025","run":true}]' >> "$GITHUB_OUTPUT"
if [ "$GENERAL" != "true" ]; then
echo 'general=false' >> "$GITHUB_OUTPUT"
echo 'settings=[{"os":"linux","index":1,"total":1,"host":"blacksmith-4vcpu-ubuntu-2404","run":false,"packages":false}]' >> "$GITHUB_OUTPUT"
exit 0
fi
echo 'settings=[{"name":"linux","host":"blacksmith-4vcpu-ubuntu-2404","run":false}]' >> "$GITHUB_OUTPUT"
echo 'general=true' >> "$GITHUB_OUTPUT"
echo 'settings=[{"os":"linux","index":1,"total":2,"host":"blacksmith-4vcpu-ubuntu-2404","run":true,"packages":true},{"os":"linux","index":2,"total":2,"host":"blacksmith-4vcpu-ubuntu-2404","run":true,"packages":false},{"os":"macos","index":1,"total":1,"host":"macos-15","run":true,"packages":true},{"os":"windows","index":1,"total":4,"host":"blacksmith-4vcpu-windows-2025","run":true,"packages":true},{"os":"windows","index":2,"total":4,"host":"blacksmith-4vcpu-windows-2025","run":true,"packages":false},{"os":"windows","index":3,"total":4,"host":"blacksmith-4vcpu-windows-2025","run":true,"packages":false},{"os":"windows","index":4,"total":4,"host":"blacksmith-4vcpu-windows-2025","run":true,"packages":false}]' >> "$GITHUB_OUTPUT"
unit:
name: unit (${{ matrix.settings.name }})
name: ${{ !matrix.settings.run && 'unit (unchanged)' || matrix.settings.total > 1 && format('unit ({0}, {1}/{2})', matrix.settings.os, matrix.settings.index, matrix.settings.total) || format('unit ({0})', matrix.settings.os) }}
needs: changes
strategy:
fail-fast: false
@@ -107,21 +110,9 @@ jobs:
uses: ./.github/actions/setup-bun
# kilocode_change start
- name: Setup Zig for Linux sandbox helper
- name: Setup Linux sandbox helper
if: matrix.settings.run && runner.os == 'Linux'
run: |
curl --fail --location --retry 3 \
https://ziglang.org/download/0.14.0/zig-linux-x86_64-0.14.0.tar.xz \
--output "$RUNNER_TEMP/zig.tar.xz"
echo "473ec26806133cf4d1918caf1a410f8403a13d979726a9045b421b685031a982 $RUNNER_TEMP/zig.tar.xz" | sha256sum --check --status
tar -xJf "$RUNNER_TEMP/zig.tar.xz" -C "$RUNNER_TEMP"
echo "$RUNNER_TEMP/zig-linux-x86_64-0.14.0" >> "$GITHUB_PATH"
- name: Build Linux sandbox helper
if: matrix.settings.run && runner.os == 'Linux'
run: |
bun packages/opencode/script/kilocode/bubblewrap.ts --arch x64 --output "$RUNNER_TEMP/bwrap"
echo "KILO_BWRAP_PATH=$RUNNER_TEMP/bwrap" >> "$GITHUB_ENV"
uses: ./.github/actions/setup-linux-sandbox
# kilocode_change end
- name: Configure git identity
if: matrix.settings.run
@@ -133,32 +124,31 @@ jobs:
if: matrix.settings.run
uses: actions/cache@v5 # kilocode_change
with:
path: node_modules/.cache/turbo
key: turbo-${{ runner.os }}-${{ hashFiles('turbo.json', '**/package.json') }}-${{ github.sha }}
path: .turbo/cache # kilocode_change
key: turbo-${{ runner.os }}-${{ hashFiles('turbo.json', 'bun.lock') }}-${{ matrix.settings.os }}-${{ matrix.settings.index }}-${{ github.sha }}
restore-keys: |
turbo-${{ runner.os }}-${{ hashFiles('turbo.json', '**/package.json') }}-
turbo-${{ runner.os }}-${{ hashFiles('turbo.json', 'bun.lock') }}-${{ matrix.settings.os }}-${{ matrix.settings.index }}-
turbo-${{ runner.os }}-${{ hashFiles('turbo.json', 'bun.lock') }}-
turbo-${{ runner.os }}-
- name: Run unit tests
- name: Run non-CLI unit tests
if: matrix.settings.run && matrix.settings.packages
run: bun turbo test:ci --filter='!@kilocode/cli' --filter='!@kilocode/kilo-jetbrains'
- name: Run CLI unit tests
if: matrix.settings.run
run: bun turbo test:ci --filter='!@kilocode/kilo-jetbrains'
run: bun turbo test:ci --filter='@kilocode/cli'
env:
KILO_EXPERIMENTAL_DISABLE_FILEWATCHER: ${{ runner.os == 'Windows' && 'true' || 'false' }}
KILO_TEST_PROFILE: ${{ runner.os == 'macOS' && github.event_name == 'pull_request' && 'darwin' || '' }} # kilocode_change
# kilocode_change start
- name: Run HttpApi exerciser gates
if: matrix.settings.run && runner.os == 'Linux'
working-directory: packages/opencode
run: bun run test:httpapi
# kilocode_change end
KILO_TEST_PROFILE: ${{ matrix.settings.os == 'macos' && 'darwin' || '' }}
KILO_TEST_SHARD: ${{ format('{0}/{1}', matrix.settings.index, matrix.settings.total) }}
- name: Publish unit reports # kilocode_change
if: always() && matrix.settings.run
uses: mikepenz/action-junit-report@bccf2e31636835cf0874589931c4116687171386 # v6.4.0
with:
report_paths: packages/*/.artifacts/unit/junit.xml
check_name: "unit results (${{ matrix.settings.name }})"
annotate_only: true
detailed_summary: true
include_time_in_summary: true
fail_on_failure: false
@@ -167,13 +157,52 @@ jobs:
if: always() && matrix.settings.run
uses: actions/upload-artifact@v7 # kilocode_change
with:
name: unit-${{ matrix.settings.name }}-${{ github.run_attempt }}
name: unit-${{ matrix.settings.os }}-${{ matrix.settings.index }}-${{ github.run_attempt }}
include-hidden-files: true
if-no-files-found: ignore
retention-days: 7
path: packages/*/.artifacts/unit/junit.xml
# kilocode_change end
# kilocode_change start
httpapi:
name: HttpApi exerciser
needs: changes
if: needs.changes.outputs.general == 'true'
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 15
defaults:
run:
shell: bash
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Setup Bun
uses: ./.github/actions/setup-bun
- name: Setup Linux sandbox helper
uses: ./.github/actions/setup-linux-sandbox
- name: Configure git identity
run: |
git config --global user.email "kilo-maintainer[bot]@users.noreply.github.com"
git config --global user.name "kilo-maintainer[bot]"
- name: Cache Turbo
uses: actions/cache@v5
with:
path: .turbo/cache
key: turbo-${{ runner.os }}-${{ hashFiles('turbo.json', 'bun.lock') }}-httpapi-${{ github.sha }}
restore-keys: |
turbo-${{ runner.os }}-${{ hashFiles('turbo.json', 'bun.lock') }}-httpapi-
turbo-${{ runner.os }}-${{ hashFiles('turbo.json', 'bun.lock') }}-
turbo-${{ runner.os }}-
- name: Run HttpApi exerciser gates
run: bun turbo test:httpapi --filter='@kilocode/cli'
# kilocode_change end
# kilocode_change start
jetbrains:
name: jetbrains
@@ -185,18 +214,38 @@ jobs:
# kilocode_change end
# kilocode_change start
unit-required:
name: unit (linux)
runs-on: blacksmith-4vcpu-ubuntu-2404
needs:
- unit
if: always()
steps:
- name: Verify unit matrix passed
run: |
echo "unit=${{ needs.unit.result }}"
test "${{ needs.unit.result }}" = "success"
required:
name: test (linux)
runs-on: blacksmith-4vcpu-ubuntu-2404
needs:
- changes
- unit
- httpapi
- jetbrains
if: always()
steps:
- name: Verify upstream test jobs passed
run: |
echo "unit=${{ needs.unit.result }}"
echo "httpapi=${{ needs.httpapi.result }}"
echo "jetbrains=${{ needs.jetbrains.result }}"
test "${{ needs.unit.result }}" = "success"
if [ "${{ needs.changes.outputs.general }}" = "true" ]; then
test "${{ needs.httpapi.result }}" = "success"
else
test "${{ needs.httpapi.result }}" = "skipped"
fi
test "${{ needs.jetbrains.result }}" = "success"
# kilocode_change end
+47 -26
View File
@@ -5,6 +5,10 @@ on:
pull_request:
types: [opened, synchronize, reopened]
permissions:
contents: read
pull-requests: read
jobs:
check-paths:
name: Check changed paths
@@ -42,9 +46,10 @@ jobs:
- name: Check baseline auto-commit permissions
id: autocommit-check
env:
BOT_PAT: ${{ secrets.BOT_PAT }}
MAINTAINER_APP_ID: ${{ secrets.KILO_MAINTAINER_APP_ID }}
MAINTAINER_APP_SECRET: ${{ secrets.KILO_MAINTAINER_APP_SECRET }}
run: |
if [ "${{ steps.fork-check.outputs.is_fork }}" != "true" ] && [ -n "$BOT_PAT" ]; then
if [ "${{ steps.fork-check.outputs.is_fork }}" != "true" ] && [ -n "$MAINTAINER_APP_ID" ] && [ -n "$MAINTAINER_APP_SECRET" ]; then
echo "can_autocommit=true" >> "$GITHUB_OUTPUT"
else
echo "can_autocommit=false" >> "$GITHUB_OUTPUT"
@@ -63,8 +68,8 @@ jobs:
uses: actions/checkout@v6
with:
lfs: true
# use BOT_PAT only when later baseline pushes are allowed; github.token is read-only on Dependabot PRs.
token: ${{ secrets.BOT_PAT }}
# Use github.token for LFS access. The maintainer app is used only for generated commits.
token: ${{ github.token }}
ref: ${{ github.head_ref }}
- name: Checkout (read-only)
@@ -171,25 +176,33 @@ jobs:
exit 1
fi
- name: Commit and push new baselines (if any)
- name: Check for baseline updates
if: needs.check-paths.outputs.can_autocommit == 'true' && steps.check-baseline-commit.outputs.is_baseline_update != 'true'
id: commit-baselines
env:
GH_TOKEN: ${{ secrets.BOT_PAT }}
id: baseline-changes
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add packages/kilo-docs/public/img/screenshot-tests/kilo-ui/
if git diff --cached --quiet; then
echo "No new baselines — nothing to commit."
echo "changed=false" >> "$GITHUB_OUTPUT"
else
git commit -m "chore: update visual regression baselines"
git lfs push --all origin
git push --no-verify
echo "changed=true" >> "$GITHUB_OUTPUT"
fi
- name: Setup Git Committer
if: steps.baseline-changes.outputs.changed == 'true'
uses: ./.github/actions/setup-git-committer
with:
kilo-maintainer-app-id: ${{ secrets.KILO_MAINTAINER_APP_ID }}
kilo-maintainer-app-secret: ${{ secrets.KILO_MAINTAINER_APP_SECRET }}
- name: Commit and push new baselines (if any)
if: steps.baseline-changes.outputs.changed == 'true'
id: commit-baselines
run: |
git commit -m "chore: update visual regression baselines"
git lfs push --all origin
git push --no-verify origin "HEAD:${GITHUB_HEAD_REF}"
echo "changed=true" >> "$GITHUB_OUTPUT"
- name: Fail if baselines changed
if: needs.check-paths.outputs.can_autocommit == 'true' && steps.commit-baselines.outputs.changed == 'true'
run: |
@@ -219,8 +232,8 @@ jobs:
uses: actions/checkout@v6
with:
lfs: true
# use BOT_PAT only when later baseline pushes are allowed; github.token is read-only on Dependabot PRs.
token: ${{ secrets.BOT_PAT }}
# Use github.token for LFS access. The maintainer app is used only for generated commits.
token: ${{ github.token }}
ref: ${{ github.head_ref }}
- name: Checkout (read-only)
@@ -357,25 +370,33 @@ jobs:
exit 1
fi
- name: Commit and push new baselines (if any)
- name: Check for baseline updates
if: needs.check-paths.outputs.can_autocommit == 'true' && steps.check-baseline-commit-vscode.outputs.is_baseline_update != 'true'
id: commit-baselines-vscode
env:
GH_TOKEN: ${{ secrets.BOT_PAT }}
id: baseline-changes-vscode
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/
if git diff --cached --quiet; then
echo "No new baselines — nothing to commit."
echo "changed=false" >> "$GITHUB_OUTPUT"
else
git commit -m "chore: update kilo-vscode visual regression baselines"
git lfs push --all origin
git push --no-verify
echo "changed=true" >> "$GITHUB_OUTPUT"
fi
- name: Setup Git Committer
if: steps.baseline-changes-vscode.outputs.changed == 'true'
uses: ./.github/actions/setup-git-committer
with:
kilo-maintainer-app-id: ${{ secrets.KILO_MAINTAINER_APP_ID }}
kilo-maintainer-app-secret: ${{ secrets.KILO_MAINTAINER_APP_SECRET }}
- name: Commit and push new baselines (if any)
if: steps.baseline-changes-vscode.outputs.changed == 'true'
id: commit-baselines-vscode
run: |
git commit -m "chore: update kilo-vscode visual regression baselines"
git lfs push --all origin
git push --no-verify origin "HEAD:${GITHUB_HEAD_REF}"
echo "changed=true" >> "$GITHUB_OUTPUT"
- name: Fail if baselines changed
if: needs.check-paths.outputs.can_autocommit == 'true' && steps.commit-baselines-vscode.outputs.changed == 'true'
run: |
+2
View File
@@ -27,6 +27,8 @@ storybook-static
.turbo
**/.serena
.serena/
**/.omo
.omo/
/result
refs
Session.vim
@@ -0,0 +1,111 @@
# Prompt Rollback + Redo — JetBrains Parity
Bring the VS Code "rollback / redo" (CLI `revert` / `unrevert`) feature to the JetBrains plugin, and fix the misleading VS Code hint string. Implementation-capable agent required — this plan touches source and tests.
## Context (verified)
- CLI already exposes everything needed; no CLI release or SDK regen required. Pinned CLI version is `7.4.1` (`packages/kilo-jetbrains/package.json`).
- `POST /session/{id}/revert` body `{ messageID, partID? }`, `POST /session/{id}/unrevert`.
- `Session.revert?: { messageID, partID?, snapshot?, diff? }``packages/sdk/js/src/gen/types.gen.ts:554`. Pushed via `session.updated` SSE.
- VS Code entry points: `RevertBanner.tsx`, `VscodeSessionTurn.tsx`, `session.tsx` context. Per-user-message rollback restores reverted prompt text into the input; redo banner shows count + per-file diff stats + Redo / Redo All + hint; reverted messages/parts hidden (`id >= revert.messageID`, partID partial-turn boundary).
- JetBrains split-mode packages differ from AGENTS.md's stated `ai.kilocode.jetbrains`; actual source roots are:
- shared → `packages/kilo-jetbrains/shared/src/main/kotlin/ai/kilocode/rpc/`
- backend → `packages/kilo-jetbrains/backend/src/main/kotlin/ai/kilocode/backend/`
- frontend → `packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/`
- All target files confirmed to exist: `SessionDto.kt`, `KiloSessionRpcApi.kt`, `KiloBackendChatManager.kt`, `KiloSessionRpcApiImpl.kt`, `KiloSessionService.kt`, `SessionController.kt`, `SessionModel.kt`, `SessionModelEvent.kt`, `SessionView.kt`, `MessageView.kt`, `MessageToolbar.kt`, `PromptPanel.kt`, `PromptEditorTextField.kt`, plus reuse targets `DiffStatBadge.kt`, `HoverIcon.kt`, `PermissionDiffView.kt`.
## Decisions
- **Reuse `DiffStatBadge`** (already the shared per-file +/- renderer used by `PermissionDiffView`) for the banner's per-file diff rows. No new diff-stat component.
- **Reuse the existing user-prompt hover toolbar (`MessageToolbar`)** for the per-message rollback control.
- **Reuse the existing `session.updated → model.setSession(...)` pipeline** for the revert boundary; no new SSE handling.
- **Stale non-English hint strings: reset all to the new English value** and flag for re-translation (applies to VS Code locales; JetBrains base bundle only, others fall back).
## Part A — VS Code string fix
File: `packages/kilo-vscode/webview-ui/src/i18n/en.ts:138`
- Change `revert.banner.hint` from `"Send a new message to make this permanent"` to `"You can redo these changes until you send a new message"`.
- In the 19 non-English locale files under `packages/kilo-vscode/webview-ui/src/i18n/` (ar, br, bs, da, de, es, fr, it, ja, ko, nl, no, pl, ru, th, tr, uk, zh, zht), overwrite `revert.banner.hint` with the identical new English string; leave a note that these need re-translation.
- Do not touch the other revert keys (`revert.banner.count_one`, `revert.banner.count_other`, `revert.banner.redo`, `revert.banner.redo.all`, `revert.disabled.agentBusy`) — only the hint changes meaning.
## Part B — JetBrains implementation
### 1. Shared (DTO + RPC contract)
- `shared/.../rpc/dto/SessionDto.kt`: add `val revert: SessionRevertDto? = null`; add `@Serializable data class SessionRevertDto(val messageID: String, val partID: String? = null, val snapshot: String? = null, val diff: String? = null)`. Field names must match CLI JSON for kotlinx auto-mapping through `session.updated`.
- `shared/.../rpc/KiloSessionRpcApi.kt`: add
- `suspend fun revert(id: String, directory: String, messageID: String, partID: String?)`
- `suspend fun unrevert(id: String, directory: String)`
### 2. Backend (RPC impl + HTTP)
- `backend/.../app/KiloBackendChatManager.kt`: add `revert(id, dir, messageID, partID?)``POST /session/$id/revert?directory=…` with `{messageID, partID?}` body, and `unrevert(id, dir)``POST /session/$id/unrevert?directory=…`. Mirror the existing `compact()` OkHttp pattern; add JSON body builders to `KiloCliDataParser`.
- `backend/.../rpc/KiloSessionRpcApiImpl.kt`: implement both, resolve worktree dir via `sessions.getDirectory(id, directory)`, wrap in `ready { }` like `compact`/`abort`.
### 3. Frontend service
- `frontend/.../app/KiloSessionService.kt`: add `suspend revert(...)` / `unrevert(...)` wrappers over the RPC, using `durable {}` per split-mode rules.
### 4. Frontend controller (`session/controller/SessionController.kt`)
- Add EDT-entry actions dispatched to the coroutine scope:
- `revert(messageID, partID?)`
- `unrevert()`
- `redo()` — mirror `RevertBanner.handleRedo`: find the next user message after the boundary and revert to it; `unrevert()` if none.
- `redoAll()``unrevert()`.
- Revert boundary already arrives via `is ChatEventDto.SessionUpdated -> model.setSession(...)` — no new SSE handling.
- On revert, restore the reverted user prompt text into the input (`session/ui/prompt/PromptPanel.kt` / `PromptEditorTextField`); on full redo (`redoAll` / final `unrevert`), clear it — matching VS Code and TUI.
### 5. Frontend model (`session/model/SessionModel.kt`, `SessionModelEvent.kt`)
- Add `SessionModelEvent.RevertChanged(revert: SessionRevertDto?)` with a stable `toString()`, fired from `setSession()` when the revert value changes. Reset in `loadHistory()` and `clear()`.
- Add derived helpers: `revert(): SessionRevertDto?` and `revertedCount()` (count of user messages with `id >= boundary`, mirroring `session.tsx`).
- Transcript hiding: expose whether a message id is hidden by the boundary (`id >= revert.messageID`, with partID partial-turn handling), applied both to newly-arriving messages and on `RevertChanged`.
- Follow the AGENTS.md "Adding a New Event" checklist (event subclass → model field + mutation + resets → controller handling → `-> Unit` stubs in exhaustive `when` blocks).
### 6. Frontend views
- New `session/ui/RevertBanner.kt` Swing view, built once and mutated via `update()` per retained-Swing rules. Listens to `RevertChanged` + `DiffUpdated`. Contents:
- arrow-left icon + count label (localized count.one/other),
- per-file diff rows reusing `ai.kilocode.client.ui.DiffStatBadge` fed from `model.diff` (`DiffFileDto.additions/deletions`),
- Redo button → `controller.redo()`, Redo All (only when count > 1) → `controller.redoAll()`,
- hint label (same new wording as Part A).
- Placed in `session/ui/SessionView.kt` at the bottom of the transcript, above the prompt input; shown only when `revert != null`.
- Per-user-message rollback control in `session/views/MessageView.kt`: user prompt bubbles already carry a hover `promptToolbar` (`MessageToolbar` with copy) + `promptHover` state. Add a rollback `HoverIcon` (arrow-left) alongside copy for user messages, wired through `SessionView → controller.revert(msgId)`. Disable/hide while the agent is busy or when there are no assistant messages after it, with the `revert.disabled.agentBusy` tooltip — mirroring `VscodeSessionTurn` `onRevert` + `data-revert-disabled`. Extend `MessageToolbar.kt` to host the extra action (or add a sibling control).
- Apply transcript hiding: `SessionView`/`TurnView` toggle `MessageView` visibility on `RevertChanged` (prefer visibility toggle over rebuild, per Swing lifecycle guidance).
### 7. i18n (`frontend/src/main/resources/messages/KiloBundle*.properties`)
- Add to base `KiloBundle.properties`: `revert.banner.count.one`, `revert.banner.count.other`, `revert.banner.redo`, `revert.banner.redo.all`, `revert.banner.hint` (new wording), `revert.disabled.agentBusy`.
- Other locale bundles fall back to base until translated; do not add stale translations.
### 8. Tests
- Backend: extend `KiloSessionRpcApiImplTest` / a `KiloBackendChatManager` test against `MockCliServer` asserting the exact HTTP path + JSON body for `revert`/`unrevert`, and that a session carrying `revert` deserializes into `SessionRevertDto`.
- Frontend controller (`SessionControllerTestBase` + `FakeSessionRpcApi`, add revert/unrevert call tracking): `revert()`/`unrevert()`/`redo()`/`redoAll()` hit the right RPC; `RevertChanged` fires; count logic; reverted messages hidden and restored; prompt-text restore on revert / clear on full redo.
- Frontend views (`BasePlatformTestCase`): `RevertBanner` renders count + per-file `DiffStatBadge` + redo buttons (Redo All only when count > 1); `MessageView` shows the rollback control on user messages and disables it while busy; retained-component assertions (`assertSame`, bounded count) per Swing lifecycle rules.
## Deliverables
| Area | Files |
|---|---|
| VS Code string | `webview-ui/src/i18n/en.ts` + 19 non-English locale hints reset |
| Shared | `SessionDto.kt` (+`SessionRevertDto`), `KiloSessionRpcApi.kt` |
| Backend | `KiloBackendChatManager.kt`, `KiloCliDataParser`, `KiloSessionRpcApiImpl.kt` |
| Frontend service/controller/model | `KiloSessionService.kt`, `SessionController.kt`, `SessionModel.kt`, `SessionModelEvent.kt` |
| Frontend views | new `RevertBanner.kt` (reusing `DiffStatBadge`), `MessageView.kt`, `MessageToolbar.kt`, `SessionView.kt`, `PromptPanel.kt` |
| i18n | `KiloBundle.properties` |
| Tests | backend RPC/chat-manager, frontend controller + view tests, `FakeSessionRpcApi` |
## Validation
- VS Code string change: `bun run typecheck` from repo root (or from `packages/kilo-vscode/`).
- JetBrains: `./gradlew typecheck` and `./gradlew test` from `packages/kilo-jetbrains/` (Java 21).
- Manual: `./gradlew runIde` (or split mode) to exercise rollback → redo → redo-all and reverted-transcript hiding.
## Risks / Notes
- Confirm no `kilocode_change` markers are needed for the VS Code i18n edits (Kilo-owned webview path — none required).
- Reset (not delete) is chosen for VS Code locale hints, so the fallback path is not relied upon; flag the 19 files for re-translation.
- Keep new Kotlin identifiers single-word per repo style; annotate EDT/model-touching methods with `@RequiresEdt`; wrap frontend RPC in `durable {}`.
@@ -0,0 +1,199 @@
# JetBrains Rollback Feature — Hardening Plan
Harden the new JetBrains rollback/redo feature: (1) communicate when only messages were
reverted because snapshots are disabled, and (24) close the code-duplication, testing,
end-to-end, and AGENTS-conformance gaps.
All work is inside `packages/kilo-jetbrains/`. No shared `packages/opencode/` files change,
so no `kilocode_change` markers are needed.
## Context an implementer needs
- Revert flow: user clicks the per-message Rollback button (`MessageToolbar`/`MessageView`)
`SessionController.revert()` aborts a busy turn then POSTs `/session/:id/revert`
→ CLI sets a revert marker → `SessionUpdated` event → `SessionModel.setRevert()`
`RevertBanner` shows + reverted messages hidden. `redo()`/`redoAll()` drive
`/session/:id/revert` (next user msg) and `/session/:id/unrevert`.
- Snapshot signal: `SessionRevertDto.snapshot` (`shared/.../rpc/dto/SessionDto.kt:22`) is
`null` exactly when the server did NOT restore files (snapshots disabled: `config.snapshot
=== false`, non-git worktree, or ACP). It is already parsed and reaches the frontend via
`SessionModel.revert()`. When `snapshot == null` the diff is also empty, so the banner shows
no file rows today. This is the reliable, no-new-API signal for "history only".
- Test harnesses already exist: frontend `SessionControllerTestBase` + `FakeSessionRpcApi`
(already tracks `rpc.reverts` and `rpc.unreverts`); backend `MockCliServer` +
`KiloBackendChatManagerTest`. Banner tests instantiate `RevertBanner(model, redo, redoAll)`
and read components via `components(banner)`.
## Key files
- `frontend/.../session/ui/RevertBanner.kt`
- `frontend/.../session/controller/SessionController.kt` (`revert`, `unrevert`, `redo`, `redoAll`, `capture`, `sessionProps`)
- `frontend/.../session/model/SessionModel.kt` (`revert()`, `revertedCount()`, `isRevertedMessage()`)
- `frontend/src/main/resources/messages/KiloBundle.properties` (+ translated `KiloBundle_*.properties`)
- `backend/.../app/KiloBackendChatManager.kt` (`revert`/`unrevert`), `backend/.../cli/KiloCliDataParser.kt` (`buildRevertJson`)
- `backend/.../app/KiloBackendSessionManager.kt` (`revertDto` duplication)
- Tests: `frontend/.../session/controller/TurnLifecycleTest.kt` (or new `RevertFlowTest.kt`),
`frontend/.../session/ui/SessionMessageListPanelTest.kt`,
`frontend/.../session/model/SessionModelTest.kt`,
`backend/.../testing/MockCliServer.kt`, `backend/.../app/KiloBackendChatManagerTest.kt`,
`backend/.../cli/KiloCliDataParserTest.kt`
## Resolved decisions
- **Do NOT hide/disable the Rollback button when snapshots are off.** This matches VS Code and
keeps history-only revert usable. We only add messaging.
- **Detect "files not restored" via `model.revert()?.snapshot == null`.** No new config/API,
no shared-code change.
- **Message it in the `RevertBanner`** with a distinct localized line, shown only when
`snapshot == null`.
- Proactively warning on the button before a revert (needs `config.snapshot`/`vcs` exposed to
the frontend) is **out of scope**.
---
## Phase 1 — Communicate history-only revert (snapshots disabled)
1. Add bundle key in `KiloBundle.properties` (place near the other `revert.banner.*` keys):
- `revert.banner.filesNotRestored=Snapshots are off — only the conversation was reverted; your files were not changed.`
- Add the same key to every translated `KiloBundle_*.properties` file. If translations are
not available, copy the English value (mirror how the other `revert.banner.*` keys were
seeded across bundles) so no key is missing.
2. In `RevertBanner.kt`:
- Add a retained `JBLabel` field `notice` styled like `hint` (`JBFont.small()`), created
once in `init` and added to `body` after `hint`.
- In `update()`, after computing `revert`, set
`notice.isVisible = revert.snapshot == null` and its text to
`KiloBundle.message("revert.banner.filesNotRestored")`.
- Color it via a theme API in `applyStyle` (use `UIUtil.getContextHelpForeground()` to match
the secondary `hint`; do not hardcode a color). Re-evaluate in `applyStyle` alongside
`hint.foreground`.
- Keep the existing empty-diff behavior (no file rows) — the notice replaces the missing
information rather than adding chrome.
3. Remove the redundant `isOpaque = false` on the `body` and `files` `Stack`s (see Phase 4);
do this while editing the file.
## Phase 2 — Close code-duplication gaps
4. In `KiloBackendSessionManager.kt`, collapse the two identical `revertDto(...)` overloads
(`SessionRevert` and `GlobalSessionRevert`) into a single private helper that maps the four
fields (`messageID`, `partID`, `snapshot`, `diff`) from a small shared shape. Simplest
approach: one private `fun revertDto(messageID: String?, partID: String?, snapshot: String?,
diff: String?)` returning `SessionRevertDto?` (null when `messageID` is null), and have both
call sites extract fields from their respective model type. Keep behavior identical; this is
a pure refactor covered by existing `KiloBackendSessionManagerTest` parsing assertions.
## Phase 3 — Fill testing + end-to-end gaps
### Frontend controller/model logic
5. `SessionModelTest.kt` — add direct model tests:
- `revertedCount()` returns the number of user messages from the marker to the end
(build entries `u1,a1,u2,a2`; marker at `u1` → 2; marker at `u2` → 1; marker id absent → 0).
- `isRevertedMessage()` true for the marker message and everything after, false before.
- `snapshot`-null marker still yields a non-zero `revertedCount()` (history-only case).
6. `TurnLifecycleTest.kt` (or new `RevertFlowTest.kt`) — add controller tests using `prompted()`:
- `redo()` reverts to the NEXT user message: seed `u1,a1,u2,a2`, apply marker at `u1`,
`edt { m.redo() }`, flush → assert `rpc.reverts` last call targets `u2`.
- `redo()` at the last user message calls unrevert: marker at `u2``redo()` → assert
`rpc.unreverts` recorded and no new revert call.
- `redoAll()` calls unrevert: assert `rpc.unreverts` contains `("ses_test","/test")`.
- `unrevert()` clears via RPC: assert `rpc.unreverts` populated.
### Frontend banner → controller wiring (end-to-end within the UI layer)
7. `SessionMessageListPanelTest.kt` — add wiring + history-only tests:
- Redo button click invokes the controller: build `RevertBanner(model, redo = { redo++ },
redoAll = { all++ })`, set a marker, `banner...first { text == redo }.doClick()` → assert
the redo lambda fired; same for Redo All when `revertedCount > 1`.
- History-only notice: marker with `SessionRevertDto("u1", snapshot = null)` → assert the
`revert.banner.filesNotRestored` label is visible and no file rows are present.
- Snapshot-present: marker with `snapshot = "snap1"` → assert the notice label is hidden.
### Full round-trip end-to-end (frontend)
8. Add one `RevertFlowTest` case that exercises the whole loop through `SessionControllerTestBase`:
- Seed a multi-turn session, `edt { m.revert(userMsgId) }`, flush → assert busy-abort
ordering + `rpc.reverts`.
- `emit(SessionUpdated(..., revert = SessionRevertDto(userMsgId, snapshot = "s1")))` →
assert `model.isRevertedMessage(userMsgId)` and reverted views hidden.
- Drive Redo All via `m.redoAll()`; `emit(SessionUpdated(..., revert = null))` → assert
messages restored (`isRevertedMessage` false) and marker cleared.
Use existing `emit`/`flush` helpers; no timeouts.
### Backend HTTP contract
9. `MockCliServer.kt` — add routing + capture fields (the current generic `/session/ses_*`
regexes are full-match and won't catch the `/revert` suffix, so new cases are safe):
- Fields: `lastRevertPath`, `lastRevertBody`, `lastUnrevertPath`, `lastUnrevertBody`
(`@Volatile var ... : String? = null`).
- Cases (place beside the `summarize`/`prompt_async` cases):
- `bare.matches(Regex("/session/ses_[^/]+/revert")) && method == "POST"` → store path+body,
`respond(output, 200, sessionCreate)`.
- `bare.matches(Regex("/session/ses_[^/]+/unrevert")) && method == "POST"` → store path+body,
`respond(output, 200, sessionCreate)`.
10. `KiloBackendChatManagerTest.kt` — mirror the existing `compact posts summarize request` test:
- `revert posts messageID and partID to revert endpoint`: call
`chat.revert("ses_abc", "/test/project", "msg1", "prt1")`; assert
`requestCount("/session/ses_abc/revert") == 1`, path starts with
`/session/ses_abc/revert?directory=`, body == `{"messageID":"msg1","partID":"prt1"}`.
- `revert omits partID when null`: body == `{"messageID":"msg1"}`.
- `unrevert posts empty body to unrevert endpoint`: body == `{}` and path prefix asserted.
11. `KiloCliDataParserTest.kt` — add `buildRevertJson` unit tests:
- `buildRevertJson("m1", null) == {"messageID":"m1"}`.
- `buildRevertJson("m1", "p1") == {"messageID":"m1","partID":"p1"}`.
- Escaping: an id containing a quote/backslash is JSON-escaped (reuse the parser's `escape`).
## Phase 4 — AGENTS conformance cleanup
12. Remove the unused bundle key `revert.disabled.agentBusy` from `KiloBundle.properties` and all
translated bundles (grep confirms it is referenced nowhere in Kotlin). Alternatively, if the
team wants VS Code parity, wire it as a disabled-state tooltip on the Rollback button — but
the recommended, lower-risk action is removal, since JetBrains intentionally aborts the busy
turn instead of disabling the button.
13. Remove redundant `isOpaque = false` on the transparent `Stack`s in `RevertBanner.kt`
(`body`, `files`) — `Stack` is already a no-color panel. Keep `isOpaque = false` on the
`BorderLayoutPanel` root only if it visibly overlays the session surface; otherwise remove it
too and rely on the default.
14. Add usage metrics for the new user-facing actions in `SessionController.kt`, using the
existing `capture(event, props)` + `sessionProps(id)` helpers:
- In `revert()` (on the success path, after `sessions.revert`): `capture("Session Rollback",
sessionProps(id))`.
- In `redo()`/`redoAll()`/`unrevert()`: `capture("Session Redo", sessionProps(id))` /
`capture("Session Redo All", ...)` as appropriate. Keep names consistent with existing
event naming; do not add PII.
## Out of scope
- Exposing `config.snapshot`/`vcs` to the frontend to proactively warn before a revert.
- Changing the retained-vs-rebuild strategy of `RevertBanner.files` (low-frequency update;
acceptable as-is).
- Replacing the full-project `ACTION_SYNCHRONIZE` in `synchronizeFromDisk` with a scoped VFS
refresh (only revisit if it shows up as a perf problem).
- Any VS Code webview parity changes beyond the shared i18n strings already present.
## Validation
Run from `packages/kilo-jetbrains/`:
- `./gradlew typecheck` — compiles all Kotlin including generated client.
- `./gradlew test` — runs frontend + backend suites, including the new tests. Requires Java 21;
only check Java if Gradle reports a Java error.
- Targeted while iterating: run the touched test classes (`TurnLifecycleTest`/`RevertFlowTest`,
`SessionMessageListPanelTest`, `SessionModelTest`, `KiloBackendChatManagerTest`,
`KiloCliDataParserTest`).
Manual smoke (optional, `./gradlew runIde`): trigger a rollback in a non-git directory (or with
`snapshot: false`) and confirm the banner shows the "files not changed" notice with no file rows;
trigger one in a git repo and confirm the notice is hidden and file rows appear.
## Changeset
Add `.changeset/<slug>.md` (`"@kilocode/kilo-jetbrains": patch`) describing the user-facing
change, e.g. "Clarify in JetBrains rollback that only the conversation was reverted when
snapshots are disabled." Test-only and refactor tasks do not need their own changeset.
## Suggested order
Phase 3 backend tests (911) and model tests (5) are independent and can be done first to lock
behavior. Then Phase 1 (user-facing messaging) with its banner tests (7). Then Phase 2 refactor,
Phase 4 cleanups, controller tests (6, 8), and the changeset last.
+49 -17
View File
@@ -38,36 +38,67 @@ Show the resolved `version`, `kind`, and default `fromTagDefault` to the user.
## CLI Pin Verification
Before dispatching prepare, verify the JetBrains plugin is pinned to the intended Kilo Core release. The plugin downloads the CLI version from `packages/kilo-jetbrains/package.json`, not from the JetBrains plugin version.
Before dispatching prepare, verify the JetBrains plugin is pinned to the intended Kilo Core release. The plugin downloads the CLI version from `packages/kilo-jetbrains/package.json`, not from the JetBrains plugin version. Prepare tags `origin/main`, so the authoritative pin is the value on `origin/main`, not a local edit.
Read the pinned CLI version:
Run the pin preflight:
```bash
bun -e 'const p=require("./packages/kilo-jetbrains/package.json"); console.log(p.version)'
bun .kilo/skills/release-jetbrains/script/check-pin.ts
```
Verify the matching GitHub Release exists and includes every runtime asset the backend may download:
The script prints:
| Field | Meaning |
|---|---|
| `pinMain` | CLI version that `origin/main` will lock into the release tag. |
| `pinLocal` | CLI version in the current worktree, useful for catching stale local checkouts. |
| `latestCli` | Latest stable `v*` Kilo CLI GitHub release. |
| `prevJetbrainsCli` | CLI pin used by the latest `jetbrains/v*` release tag, for reviewing the jump. |
| `pinnedMain` / `pinnedLocal` | Whether `kilo.cli.pinned=true`; `false` means repo CLI dev mode. |
| `assetsOk` / `missingAssets` | Whether the pinned CLI release has every runtime asset. |
| `drift` | `up-to-date`, `behind`, `worktree-behind-main`, `repo-mode-on-main`, `repo-mode-local`, or `assets-missing`. |
Interpretation:
| Drift | Action |
|---|---|
| `up-to-date` | Continue after user confirmation. |
| `behind` | Stop and show `pinMain`, `latestCli`, and `prevJetbrainsCli`; ask whether to cancel, bump + test, or proceed anyway. |
| `worktree-behind-main` | Explain that prepare tags `origin/main`; refresh the worktree or rely on `pinMain` in the confirmation. |
| `repo-mode-on-main` | Stop. `kilo.cli.pinned=false` is dev-only and must be reset to `true` on `main` before release. |
| `repo-mode-local` | Stop or reset local `kilo.cli.pinned=true`; release checks should run from a releasable local state. |
| `assets-missing` | Stop. The pinned CLI release is incomplete and would fail runtime download. |
Show the resolved JetBrains plugin version, release kind, default `fromTagDefault`, `pinMain`, `latestCli`, `prevJetbrainsCli`, and `assetsOk` to the user, then ask for confirmation before continuing. If the user wants a different CLI pin, use the bump workflow below and do not dispatch prepare until the bump is merged to `main`.
## Bump the CLI Pin
Use this only when the user wants to test or release with a different CLI than `origin/main` currently pins. The helper refuses versions whose GitHub release or runtime assets are missing.
Local test edit only:
```bash
cli_version="7.4.1"
gh release view "v${cli_version}" --repo Kilo-Org/kilocode --json assets \
--jq '.assets[].name' | sort
bun .kilo/skills/release-jetbrains/script/set-pin.ts --latest
# or
bun .kilo/skills/release-jetbrains/script/set-pin.ts --version 7.4.1
```
Expected assets:
Then test from `packages/kilo-jetbrains/`:
```text
kilo-darwin-arm64.zip
kilo-darwin-x64.zip
kilo-linux-arm64.tar.gz
kilo-linux-x64.tar.gz
kilo-windows-arm64.zip
kilo-windows-x64.zip
```bash
./gradlew typecheck
./gradlew test
```
If the pin is stale or the release assets are missing, stop and ask the user to update `packages/kilo-jetbrains/package.json` on `main` before dispatching prepare. The prepare workflow tags `origin/main`, so the pin must already be reviewed and merged before the release tag is created.
If the user confirms the tested pin should be released, open or update a pin bump PR to `main`:
Show the resolved JetBrains plugin version, release kind, default `fromTagDefault`, pinned CLI version, and CLI release asset status to the user, then ask for confirmation before continuing.
```bash
bun .kilo/skills/release-jetbrains/script/set-pin.ts --latest --pr
# or
bun .kilo/skills/release-jetbrains/script/set-pin.ts --version 7.4.1 --pr
```
After that PR merges to `main`, re-run `resolve-version.ts`, re-run `check-pin.ts`, confirm `drift=up-to-date`, then dispatch prepare. Do not dispatch prepare from a local-only pin edit; the prepare workflow tags `origin/main`.
## Prepare Workflow
@@ -200,6 +231,7 @@ Report the Marketplace channel and GitHub Release URL. RC versions publish to th
- 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 prepare tagged an unintended CLI pin, do not move the tag. Land the intended pin on `main`, resolve the next JetBrains version, and create a new release tag.
- 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.
@@ -0,0 +1,89 @@
#!/usr/bin/env bun
import { $ } from "bun"
import semver from "semver"
import { parseArgs } from "util"
import { latest, missing } from "./pin-common"
const repo = process.env.GH_REPO ?? process.env.GITHUB_REPOSITORY ?? "Kilo-Org/kilocode"
const { values } = parseArgs({
args: Bun.argv.slice(2),
options: {
help: { type: "boolean", short: "h", default: false },
},
})
if (values.help) {
console.log(`
Usage: bun .kilo/skills/release-jetbrains/script/check-pin.ts
Checks the CLI pin that a JetBrains release would lock. Prepare tags origin/main,
so this reads packages/kilo-jetbrains/package.json from origin/main and compares
it with the latest published Kilo CLI release plus the local worktree pin.
Exit codes:
0 Pin is release-ready.
2 Pin drift, repo CLI mode, or missing CLI assets require maintainer review.
`)
process.exit(0)
}
await $`git fetch origin main --tags`.quiet()
const pinMain = JSON.parse(await $`git show origin/main:packages/kilo-jetbrains/package.json`.text()).version as string
const pinLocal = (await Bun.file("packages/kilo-jetbrains/package.json").json()).version as string
const propsMain = await $`git show origin/main:packages/kilo-jetbrains/gradle.properties`.text()
const propsLocal = await Bun.file("packages/kilo-jetbrains/gradle.properties").text()
const pinnedMain = pinned(propsMain)
const pinnedLocal = pinned(propsLocal)
const latestCli = await latest(repo)
const prevJetbrainsCli = await previous()
const missingAssets = await missing(repo, pinMain)
const assetsOk = missingAssets.length === 0
const drift = (() => {
if (!pinnedMain) return "repo-mode-on-main"
if (!pinnedLocal) return "repo-mode-local"
if (pinLocal !== pinMain) return "worktree-behind-main"
if (!assetsOk) return "assets-missing"
if (latestCli && semver.lt(pinMain, latestCli)) return "behind"
return "up-to-date"
})()
console.log(JSON.stringify({
pinMain,
pinLocal,
latestCli,
prevJetbrainsCli,
pinnedMain,
pinnedLocal,
assetsOk,
missingAssets,
drift,
}, null, 2))
if (drift !== "up-to-date") process.exit(2)
async function previous() {
const text = await $`git tag --list ${"jetbrains/v*"}`.text()
const tag = text
.split(/\r?\n/)
.map((item) => item.trim())
.filter(Boolean)
.map((tag) => ({ tag, version: tag.replace(/^jetbrains\/v/, "") }))
.filter((item) => semver.valid(item.version))
.sort((a, b) => semver.rcompare(a.version, b.version))[0]?.tag
if (!tag) return null
const res = await $`git show ${tag}:packages/kilo-jetbrains/package.json`.nothrow().text()
if (!res.trim()) return null
return JSON.parse(res).version as string
}
function pinned(text: string) {
const value = text.split(/\r?\n/).flatMap((line) => {
const [key, value] = line.split("=", 2)
if (key.trim() !== "kilo.cli.pinned") return []
return [value?.trim().toLowerCase()]
})[0]
return value == null || value === "true"
}
@@ -0,0 +1,30 @@
import { $ } from "bun"
import semver from "semver"
export const assets = [
"kilo-darwin-arm64.zip",
"kilo-darwin-x64.zip",
"kilo-linux-arm64.tar.gz",
"kilo-linux-x64.tar.gz",
"kilo-windows-arm64.zip",
"kilo-windows-x64.zip",
]
export async function latest(repo: string) {
const list = (await $`gh release list --repo ${repo} --limit 100 --json tagName,isDraft,isPrerelease`.json()) as {
tagName: string
isDraft: boolean
isPrerelease: boolean
}[]
return list
.filter((item) => /^v\d+\.\d+\.\d+$/.test(item.tagName) && !item.isDraft && !item.isPrerelease)
.map((item) => item.tagName.slice(1))
.sort(semver.rcompare)[0] ?? null
}
export async function missing(repo: string, version: string) {
const res = await $`gh release view ${`v${version}`} --repo ${repo} --json assets --jq ${".assets[].name"}`.quiet().nothrow()
if (res.exitCode !== 0) return assets
const names = res.stdout.toString().split(/\r?\n/).map((item) => item.trim()).filter(Boolean)
return assets.filter((item) => !names.includes(item))
}
@@ -0,0 +1,123 @@
#!/usr/bin/env bun
import { $ } from "bun"
import semver from "semver"
import { parseArgs } from "util"
import { latest, missing } from "./pin-common"
const repo = process.env.GH_REPO ?? process.env.GITHUB_REPOSITORY ?? "Kilo-Org/kilocode"
const file = "packages/kilo-jetbrains/package.json"
const label = "jetbrains-cli-pin-bump"
const { values } = parseArgs({
args: Bun.argv.slice(2),
options: {
version: { type: "string" },
latest: { type: "boolean", default: false },
pr: { type: "boolean", default: false },
help: { type: "boolean", short: "h", default: false },
},
})
if (values.help) {
console.log(`
Usage: bun .kilo/skills/release-jetbrains/script/set-pin.ts (--latest | --version <x.y.z>) [--pr]
Without --pr, rewrites ${file} in the local worktree so you can test a CLI pin.
With --pr, opens or updates a PR against main using the GitHub API; prepare tags
origin/main, so the pin bump must merge there before a JetBrains release starts.
Examples:
bun .kilo/skills/release-jetbrains/script/set-pin.ts --latest
bun .kilo/skills/release-jetbrains/script/set-pin.ts --version 7.4.1
bun .kilo/skills/release-jetbrains/script/set-pin.ts --latest --pr
`)
process.exit(0)
}
if (values.latest && values.version) throw new Error("Pass either --latest or --version, not both")
const version = values.latest ? await latest(repo) : values.version?.replace(/^v/, "")
if (!version || !semver.valid(version) || semver.prerelease(version)) {
throw new Error("Pass a stable CLI version with --version x.y.z or use --latest")
}
const miss = await missing(repo, version)
if (miss.length > 0) {
throw new Error(`CLI release v${version} is missing required assets: ${miss.join(", ")}`)
}
if (values.pr) {
await pr(version)
process.exit(0)
}
const pkg = await Bun.file(file).json()
const previous = pkg.version as string
pkg.version = version
await Bun.write(file, `${JSON.stringify(pkg, null, 2)}\n`)
if (previous === version) {
console.log(`${file} already pins CLI v${version}`)
} else {
console.log(`Pinned JetBrains CLI ${previous} -> ${version} in ${file}`)
}
console.log("Test locally with: cd packages/kilo-jetbrains && ./gradlew typecheck && ./gradlew test")
console.log("When satisfied, run this script again with --pr so the bump lands on main before prepare tags it.")
async function pr(version: string) {
await $`git fetch origin main`.quiet()
const branch = `chore/jetbrains-cli-pin-v${version}`
const main = (await $`git rev-parse origin/main`.text()).trim()
const text = await $`git show origin/main:${file}`.text()
const pkg = JSON.parse(text)
const previous = pkg.version as string
if (previous === version) {
console.log(`origin/main already pins CLI v${version}; no PR needed.`)
return
}
pkg.version = version
const body = `${JSON.stringify(pkg, null, 2)}\n`
await ensure(branch, main)
const current = (await $`gh api ${`repos/${repo}/contents/${file}?ref=${branch}`}`.json()) as { sha: string }
await $`gh api --method PUT ${`repos/${repo}/contents/${file}`} -f message=${`chore(jetbrains): bump CLI pin to v${version}`} -f content=${Buffer.from(body).toString("base64")} -f branch=${branch} -f sha=${current.sha}`.quiet()
const title = `chore(jetbrains): bump CLI pin to v${version}`
const desc = [
`Bumps the JetBrains CLI pin from v${previous} to v${version}.`,
"",
"Prepare tags origin/main, so this PR must merge before dispatching a JetBrains release that should lock this CLI.",
"",
"After merging, re-run:",
"",
"```bash",
"bun .kilo/skills/release-jetbrains/script/check-pin.ts",
"```",
].join("\n")
const view = await $`gh pr view ${branch} --repo ${repo} --json url --jq .url`.quiet().nothrow()
if (view.exitCode === 0 && view.stdout.toString().trim()) {
await $`gh pr edit ${branch} --repo ${repo} --title ${title} --body ${desc}`
await tag(branch)
console.log(view.stdout.toString().trim())
return
}
const url = await $`gh pr create --repo ${repo} --base main --head ${branch} --title ${title} --body ${desc}`.text()
await tag(branch)
console.log(url.trim())
}
async function ensure(branch: string, sha: string) {
const ref = `repos/${repo}/git/refs/heads/${branch}`
const exists = await $`gh api ${ref}`.nothrow().quiet()
if (exists.exitCode === 0) {
await $`gh api --method PATCH ${ref} -f sha=${sha} -F force=true`.quiet()
return
}
await $`gh api --method POST ${`repos/${repo}/git/refs`} -f ref=${`refs/heads/${branch}`} -f sha=${sha}`.quiet()
}
async function tag(branch: string) {
await $`gh label create ${label} --repo ${repo} --color 1D76DB --description ${"JetBrains pinned CLI version bump"}`.quiet().nothrow()
const result = await $`gh pr edit ${branch} --repo ${repo} --add-label ${label}`.quiet().nothrow()
if (result.exitCode !== 0) {
console.warn(`Warning: failed to add ${label} label to ${branch}`)
}
}
+1 -1
View File
@@ -1 +1 @@
v1.15.13
v1.16.2
+1 -1
View File
@@ -1,6 +1,6 @@
---
description: translate English to other languages
model: opencode/claude-opus-4-7
model: opencode/claude-opus-4-8
---
run git diff and translate changed english doc and UI copy files to other international languages. Translate all languages in parallel to save time.
+3
View File
@@ -13,6 +13,9 @@
"packages/opencode/migration/*": "deny",
},
},
"reference": {
"effect": "github.com/Effect-TS/effect-smol",
},
"mcp": {},
"tools": {
"github-triage": false,
+98
View File
@@ -0,0 +1,98 @@
# OpenCode Session Runtime
OpenCode sessions preserve durable conversational history while assembling the runtime context an agent needs to act correctly in its current environment.
## Language
**System Context**:
The structured collection of contextual facts presented to the model as initial instructions and chronological updates.
_Avoid_: System prompt
**Session History**:
The projected chronological conversation selected for a provider turn after applying the active compaction and **Context Epoch** cutoffs.
_Avoid_: Session Context
**Context Source**:
One independently observed typed value within the **System Context**, represented by a stable key, JSON codec, infallible loader, pure baseline/update renderers, and an optional removal renderer for dynamic sources.
_Avoid_: Prompt fragment
**System Context Registry**:
The Location-scoped registry of ordered, scoped producers that contribute to the current **System Context**.
**Mid-Conversation System Message**:
A durable chronological instruction that tells the model the newly effective state of a changed **Context Source**.
_Avoid_: System update, system notification, raw text diff
**Context Epoch**:
The span during which one effective agent's initially rendered **System Context** remains immutable, ending at compaction or another baseline-replacing transition.
**Baseline System Context**:
The full **System Context** rendered at the start of a **Context Epoch**.
_Avoid_: Live system prompt
**Context Snapshot**:
The overwriteable model-hidden JSON state used to compare each **Context Source** with the value last admitted to a provider turn.
**Unavailable Context**:
An expected temporary inability to observe a **Context Source** value; the runtime retains its prior effective state and emits no update, or omits it until first successfully loaded.
**Safe Provider-Turn Boundary**:
The point immediately before a provider call, after durable input promotion and any required tool settlement, where context changes may be admitted chronologically.
## Relationships
- A **System Context** is an opaque carrier composed from zero or more **Context Sources**.
- **Session History** contains projected conversational messages and admitted **Mid-Conversation System Messages**; the active **Baseline System Context** remains separate provider-request state.
- The **System Context Registry** uses stable-keyed scoped contributions to assemble the current **System Context**; contributor removal naturally removes its sources at the next **Safe Provider-Turn Boundary**.
- A changed **Context Source** may produce one **Mid-Conversation System Message** containing its newly effective state.
- A **Mid-Conversation System Message** persists the exact combined rendered text sent to the model.
- The current **Context Snapshot** advances atomically with the corresponding durable **Mid-Conversation System Message**.
- A **Context Snapshot** stores one codec-encoded JSON value and, for removable dynamic sources, a pre-rendered removal message per stable **Context Source** key.
- Changes from multiple **Context Sources** admitted at one safe boundary combine into one **Mid-Conversation System Message**.
- Context changes are sampled and admitted lazily at a **Safe Provider-Turn Boundary**, never pushed asynchronously when their source changes.
- At a **Safe Provider-Turn Boundary**, newly promoted user input or settled tool results precede any combined **Mid-Conversation System Message**.
- The first provider turn renders the latest complete **Baseline System Context** and initializes its **Context Snapshot** without emitting a redundant **Mid-Conversation System Message**; unavailable initial context blocks the turn instead of persisting an incomplete baseline.
- Initial **System Context** preparation precedes the first durable input promotion so an unavailable baseline leaves that input pending and retryable; ordinary reconciliation remains after promotion.
- Compaction starts a new **Context Epoch** with a freshly rendered **Baseline System Context** and **Context Snapshot**; prior **Mid-Conversation System Messages** remain durable audit history but leave projected model history.
- A newly registered core or plugin-defined **Context Source** absent from the current snapshot emits its baseline rendering once at the next **Safe Provider-Turn Boundary**.
- **Context Source** keys are stable and namespaced; duplicate keys fail composition. `SystemContext.combine(...)` preserves caller order; the **System Context Registry** evaluates producers concurrently and combines them in stable contribution-key order so rendered context remains deterministic.
- Each **Context Source** loader returns one coherent typed value. `SystemContext.make(...)` hides that value type so differently typed sources compose uniformly. Its codec compares and stores that value; its pure renderers produce model-visible baseline, update, and removal text only when needed.
- `SystemContext.initialize(...)` observes a composed **System Context** once and produces a fresh **Baseline System Context** with its **Context Snapshot**.
- `SystemContext.reconcile(...)` observes a composed **System Context** once and returns exactly one next action: unchanged, updated, replacement ready, or replacement blocked.
- `SystemContext.replace(...)` represents an explicit baseline-replacing transition such as compaction or model/provider switch; it either produces a fresh generation or reports that replacement is blocked by unavailable admitted context.
- Context Epoch preparation retries until stable after optimistic revision mismatches so concurrent replacement requests cannot terminate an otherwise valid safe-boundary run.
- **Unavailable Context** uses stale-while-revalidate semantics and is distinct from a successfully loaded absence, which may emit removal text.
- Ordinary **Context Source** loaders return values directly; loaders that intentionally use stale-while-revalidate may explicitly return **Unavailable Context**.
- Nested project instruction discovery after successful reads remains a follow-up; when implemented, discovered instructions must be admitted durably at the next **Safe Provider-Turn Boundary**.
- Location-scoped services naturally re-resolve effective context when a moved session next runs in its destination location.
- Moving a Session clears its active **Context Epoch**, so the destination must initialize a complete baseline before another prompt can promote.
- Context Epoch initialization is fenced against the authoritative Session Location, so an old-Location runner cannot recreate source context after a concurrent move.
- Instruction discovery, source identity, persistence, and file loading belong to the instruction service; the **System Context** abstraction only composes effectful producers and renders loaded values.
- The first instruction-service slice observes global and upward project `AGENTS.md` files as one ordered aggregate **Context Source** at each **Safe Provider-Turn Boundary**.
- Built-in and instruction context producers register through the **System Context Registry** with stable contribution keys. Plugin-defined context registration and hot-reload lifecycle remain a follow-up built on the same scoped registry seam.
- Selected-agent available-skill guidance is a **Context Source** composed with Location-wide registry sources immediately before Context Epoch admission. It lists only names and descriptions permitted for that agent; skill bodies and locations are exposed only through the permission-checked `skill` tool.
- Switching the selected agent requests **Context Epoch** replacement. A switch admitted after the current **Safe Provider-Turn Boundary** applies to the next provider turn while leaving the already-prepared baseline durable. Epoch creation is fenced against the authoritative effective agent, and retries re-observe the current agent.
- A cross-agent replacement must complete before another provider turn; unavailable admitted context blocks that replacement instead of exposing the previous agent's privileged baseline.
- Local tool authorization and pending permission requests retain the effective agent of the provider turn that issued the call; a later agent switch cannot change that call's policy.
- Context source changes never wake idle sessions; the next naturally scheduled **Safe Provider-Turn Boundary** loads and compares current values lazily.
- Once admitted, a **Mid-Conversation System Message** remains durable even if the following provider attempt fails and is replayed unchanged on retry.
- **Mid-Conversation System Messages** remain durable Session-message history; normal user-facing transcript surfaces may hide them.
- The date **Context Source** initially preserves host-local calendar-date behavior; a configured user timezone may replace that default later.
- A **Context Epoch** begins with one immutable **Baseline System Context**.
- A **Context Epoch** durably records the effective agent that owns its **Baseline System Context**.
- A **Baseline System Context** is stored durably and reused verbatim across process restarts within its **Context Epoch**.
- A **Baseline System Context** durably preserves the exact joined text used for the active provider-cache prefix.
- Compaction or a model/provider switch starts a new **Context Epoch** because the baseline can be replaced without preserving the prior provider cache.
- A model/provider switch always starts a new **Context Epoch** while preserving chronological conversation history.
- A **Mid-Conversation System Message** lowers to the provider's native chronological instruction role when supported and to a wrapped chronological fallback otherwise.
- When the effective aggregate instruction set changes, its **Mid-Conversation System Message** includes the complete current ordered set and supersedes the prior aggregate value; when no ambient instructions remain, the message states that previously loaded instructions no longer apply.
- Ambient project instruction discovery honors `KILO_DISABLE_PROJECT_CONFIG`; global instructions remain eligible.
## Example dialogue
> **Dev:** "The date changed while the session was active. Should the **Mid-Conversation System Message** say what the old date was?"
> **Domain expert:** "No. Emit the newly effective date so the agent can act on the current **System Context**."
## Flagged ambiguities
- Legacy `experimental.chat.system.transform` can mutate the assembled baseline system prompt arbitrarily, but V2 plugins do not yet expose an equivalent hook. Decide separately whether to port it, replace dynamic uses with plugin-defined **Context Sources**, or narrow its semantics.
+1038 -173
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -2,7 +2,7 @@
exact = true
# Keep Kilo's longer supply-chain quarantine while allowing packages that must track coordinated releases.
minimumReleaseAge = 410520 # seconds (~4.75 days / ~114 hours)
minimumReleaseAgeExcludes = ["mermaid", "@mermaid-js/parser", "@opentui/core", "@opentui/core-darwin-arm64", "@opentui/core-darwin-x64", "@opentui/core-linux-arm64", "@opentui/core-linux-x64", "@opentui/core-win32-arm64", "@opentui/core-win32-x64", "@opentui/keymap", "@opentui/solid"]
minimumReleaseAgeExcludes = ["mermaid", "@mermaid-js/parser", "@ai-sdk/amazon-bedrock", "@opentui/core", "@opentui/core-darwin-arm64", "@opentui/core-darwin-x64", "@opentui/core-linux-arm64", "@opentui/core-linux-arm64-musl", "@opentui/core-linux-x64", "@opentui/core-linux-x64-musl", "@opentui/core-win32-arm64", "@opentui/core-win32-x64", "@opentui/keymap", "@opentui/solid"]
[test]
root = "./do-not-run-tests-from-root"
+8 -4
View File
@@ -183,13 +183,17 @@ else
fi
if [ -z "$requested_version" ]; then
url="https://github.com/Kilo-Org/kilocode/releases/latest/download/$filename"
specific_version=$(curl -s https://api.github.com/repos/Kilo-Org/kilocode/releases/latest | sed -n 's/.*"tag_name": *"v\([^"]*\)".*/\1/p')
if [[ $? -ne 0 || -z "$specific_version" ]]; then
# The npm latest tag tracks stable CLI releases independently of other GitHub release types.
if ! specific_version=$(curl -fsSL "https://registry.npmjs.org/-/package/%40kilocode%2Fcli/dist-tags" |
sed -n 's/.*"latest"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p'); then
echo -e "${RED}Failed to fetch version information${NC}"
exit 1
fi
if [[ ! "$specific_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo -e "${RED}Failed to fetch version information${NC}"
exit 1
fi
url="https://github.com/Kilo-Org/kilocode/releases/download/v${specific_version}/$filename"
else
# Strip leading 'v' if present
requested_version="${requested_version#v}"
+4 -4
View File
@@ -1,8 +1,8 @@
{
"nodeModules": {
"x86_64-linux": "sha256-5/NgXEBhaNEEqIz9GVCXxQaLJwkqWXAAZOPoa6GyHTI=",
"aarch64-linux": "sha256-qt7PKn0SAhDCcYtXRY9+1TBp9x3Far7kK24jhUzoMf8=",
"aarch64-darwin": "sha256-3FN/i3chx01S52lAv0tjxVtRpUlPkIfInphpq+69uWA=",
"x86_64-darwin": "sha256-+rOacJLx+qOCRGW4F+AjvFcc9XUE2biB0BRSy3dTWGI="
"x86_64-linux": "sha256-yNHrsBXfCWeuX13frVTjIL7U4k71wSimDtdTjoBndoY=",
"aarch64-linux": "sha256-zbRBPcPduV1a2EH4BiZWPJ+LyZMLCEPkQ3s+U7tDxtk=",
"aarch64-darwin": "sha256-NLLlE2t3vs1E5fDwyiFGoneJAdTb6uXrFuVa+LwBtlY=",
"x86_64-darwin": "sha256-BH31+FPgkt1xXJixkj+DgXIveJ49fWk4AIc3Hx+4XzM="
}
}
+30 -16
View File
@@ -7,14 +7,16 @@
"packageManager": "bun@1.3.14",
"scripts": {
"dev": "bun run --cwd packages/opencode --conditions=browser src/index.ts",
"dev:stats": "bun sst shell --stage=production -- bun run --cwd packages/stats/app dev",
"dev:local": "bun run packages/opencode/script/dev-local.ts",
"dev:storybook": "bun --cwd packages/storybook storybook",
"lint": "oxlint",
"typecheck": "bun turbo typecheck",
"upgrade-opentui": "bun run script/upgrade-opentui.ts",
"postinstall": "bun run --cwd packages/opencode fix-node-pty && bun run script/setup-git.ts",
"postinstall": "bun run --cwd packages/core fix-node-pty && bun run script/setup-git.ts",
"prepare": "husky",
"random": "echo 'Random script'",
"hello": "echo 'Hello World!'",
"sso": "aws sso login --sso-session=opencode --no-browser",
"test": "echo 'do not run tests from root' && exit 1",
"extension": "bun --cwd packages/kilo-vscode script/launch.ts",
"dev-setup": "bun run --cwd packages/opencode --conditions=browser src/index.ts dev-setup"
@@ -25,14 +27,15 @@
"packages/sdk/js"
],
"catalog": {
"@effect/opentelemetry": "4.0.0-beta.66",
"@effect/platform-node": "4.0.0-beta.66",
"@effect/opentelemetry": "4.0.0-beta.74",
"@effect/platform-node": "4.0.0-beta.74",
"@anthropic-ai/sandbox-runtime": "0.0.63",
"@npmcli/arborist": "9.4.0",
"@types/bun": "1.3.14",
"@types/cross-spawn": "6.0.6",
"@octokit/rest": "22.0.0",
"@opentui/core": "0.2.15",
"@opentui/solid": "0.2.15",
"@opentui/core": "0.3.2",
"@opentui/solid": "0.3.2",
"ulid": "3.0.1",
"@kobalte/core": "0.13.11",
"@types/luxon": "3.7.1",
@@ -50,11 +53,12 @@
"dompurify": "3.4.2",
"drizzle-kit": "1.0.0-rc.2",
"drizzle-orm": "1.0.0-rc.2",
"effect": "4.0.0-beta.66",
"effect": "4.0.0-beta.74",
"ai": "6.0.168",
"cross-spawn": "7.0.6",
"hono": "4.12.12",
"hono-openapi": "1.1.2",
"ipaddr.js": "2.4.0",
"fuzzysort": "3.1.0",
"luxon": "3.6.1",
"marked": "17.0.1",
@@ -69,7 +73,7 @@
"shiki": "3.20.0",
"solid-list": "0.3.0",
"tailwindcss": "4.1.11",
"virtua": "0.42.3",
"virtua": "0.49.1",
"vite": "7.3.5",
"@solidjs/meta": "0.29.4",
"@solidjs/router": "0.15.4",
@@ -77,8 +81,11 @@
"solid-js": "1.9.12",
"vite-plugin-solid": "2.11.10",
"@lydell/node-pty": "1.2.0-beta.12",
"@opentui/keymap": "0.2.15",
"@effect/sql-sqlite-bun": "4.0.0-beta.66"
"@opentui/keymap": "0.3.2",
"@effect/sql-sqlite-bun": "4.0.0-beta.74",
"@hono/standard-validator": "0.2.0",
"@hono/zod-validator": "0.4.2",
"sst": "4.13.1"
}
},
"devDependencies": {
@@ -94,13 +101,17 @@
"turbo": "2.9.14",
"@types/bun": "catalog:",
"@changesets/changelog-github": "^0.5.1",
"@changesets/cli": "^2.27.10"
"@changesets/cli": "^2.27.10",
"@actions/artifact": "5.0.1",
"sst": "catalog:"
},
"dependencies": {
"@kilocode/plugin": "workspace:*",
"@opencode-ai/script": "workspace:*",
"@kilocode/sdk": "workspace:*",
"typescript": "catalog:"
"typescript": "catalog:",
"@aws-sdk/client-s3": "3.933.0",
"heap-snapshot-toolkit": "1.1.3"
},
"repository": {
"type": "git",
@@ -125,7 +136,7 @@
"@types/bun": "catalog:",
"@types/node": "catalog:",
"effect": "catalog:",
"@effect/platform-node-shared": "4.0.0-beta.46",
"@effect/platform-node-shared": "4.0.0-beta.74",
"path-to-regexp": ">=8.4.0",
"picomatch": ">=2.3.2",
"defu": "6.1.6",
@@ -140,16 +151,19 @@
"@opentui/core": "catalog:",
"@opentui/solid": "catalog:",
"solid-js": "catalog:",
"@opentui/keymap": "catalog:",
"@smithy/util-buffer-from": "4.2.2"
"@opentui/keymap": "catalog:"
},
"patchedDependencies": {
"@npmcli/agent@4.0.0": "patches/@npmcli%2Fagent@4.0.0.patch",
"@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch",
"@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch",
"solid-js@1.9.10": "patches/solid-js@1.9.10.patch",
"virtua@0.49.1": "patches/virtua@0.49.1.patch",
"@ai-sdk/xai@3.0.92": "patches/@ai-sdk%2Fxai@3.0.92.patch",
"pacote@21.5.1": "patches/pacote@21.5.1.patch",
"@ai-sdk/google@3.0.73": "patches/@ai-sdk%2Fgoogle@3.0.73.patch",
"mammoth@1.12.0": "patches/mammoth@1.12.0.patch"
},
"version": "7.4.1",
"version": "7.4.8",
"peerDependencies": {}
}
@@ -2,7 +2,7 @@ import { defineConfig } from "drizzle-kit"
export default defineConfig({
dialect: "sqlite",
schema: "./src/**/*.sql.ts",
schema: ["./src/**/*.sql.ts", "./src/**/sql.ts"],
out: "./migration",
dbCredentials: {
url: "/home/thdxr/.local/share/kilo/kilo.db",
@@ -0,0 +1 @@
ALTER TABLE `session` ADD `metadata` text;
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More