From 514af4c36145610e22d3888b7b5836fab1351272 Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Thu, 11 Jun 2026 11:53:01 +0200 Subject: [PATCH 01/33] fix(vscode): speed up subagent transcript loading Paginate child histories and preserve reasoning updates that arrive while their initial transcript loads. --- .changeset/quick-subagent-transcripts.md | 5 ++ packages/kilo-vscode/src/KiloProvider.ts | 15 ++-- .../kilo-vscode/src/SubAgentViewerProvider.ts | 32 ++++----- .../src/kilo-provider/message-page.ts | 3 +- .../unit/kilo-provider-load-messages.test.ts | 70 ++++++++++++++----- 5 files changed, 83 insertions(+), 42 deletions(-) create mode 100644 .changeset/quick-subagent-transcripts.md diff --git a/.changeset/quick-subagent-transcripts.md b/.changeset/quick-subagent-transcripts.md new file mode 100644 index 0000000000..263ebac9ba --- /dev/null +++ b/.changeset/quick-subagent-transcripts.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Open subagent transcripts faster while preserving live reasoning and paginating long histories. diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index 1e227f9003..27a3839e8a 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -619,8 +619,9 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper } public loadMessages(sessionID: string): Promise { - // Sub-agent viewer: full transcript (no "load earlier" UI, no pagination). - return this.handleLoadMessages(sessionID, { limit: 0 }) + // Sub-agent viewers share the normal paginated transcript and preserve + // live deltas that arrive while the initial page is loading. + return this.handleLoadMessages(sessionID, { preserveStream: true }) } /** @@ -1482,7 +1483,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper private async handleLoadMessages( sessionID: string, - options: { mode?: MessageLoadMode; before?: string; limit?: number } = {}, + options: { mode?: MessageLoadMode; before?: string; limit?: number; preserveStream?: boolean } = {}, ): Promise { const mode = options.mode ?? "replace" if (mode === "replace" || mode === "focus") { @@ -1531,9 +1532,10 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper for (const message of messages) { this.connectionService.recordMessageSessionId(message.id, message.sessionID) } - // Authoritative snapshot: drop queued deltas. Prepend is older history - // and must not clobber live deltas. - if (mode === "replace" || mode === "reconcile") this.streams.drop(sessionID) + // Authoritative snapshots normally supersede buffered deltas. A newly + // opened sub-agent viewer has no earlier renderer state, so its buffered + // updates arrived during this fetch and must follow the snapshot. + if ((mode === "replace" || mode === "reconcile") && !options.preserveStream) this.streams.drop(sessionID) if (mode === "reconcile") this.lastReconciledAt.set(sessionID, Date.now()) this.postMessage({ type: "messagesLoaded", @@ -1544,6 +1546,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper hasMore: Boolean(page.cursor), since, }) + if (options.preserveStream) this.streams.flush(sessionID) // Recover any prompts missed while the webview was loading or during an SSE reconnection. this.recoverPendingPrompts() } catch (error) { diff --git a/packages/kilo-vscode/src/SubAgentViewerProvider.ts b/packages/kilo-vscode/src/SubAgentViewerProvider.ts index 277a2b606b..e1693cc18b 100644 --- a/packages/kilo-vscode/src/SubAgentViewerProvider.ts +++ b/packages/kilo-vscode/src/SubAgentViewerProvider.ts @@ -42,32 +42,30 @@ export class SubAgentViewerProvider implements vscode.Disposable { } const provider = new KiloProvider(this.extensionUri, this.connectionService, this.context) + // Start accepting this session's SSE events as soon as the panel subscribes. + // Reasoning deltas are not persisted until the reasoning part finishes. + provider.trackSession(sessionID) provider.resolveWebviewPanel(panel) - // Once the webview is ready, fetch the session and display it in read-only mode. - const readyDisposable = panel.webview.onDidReceiveMessage(async (msg) => { + // Navigate immediately when the webview is ready, then load metadata and + // the same paginated, row-virtualized transcript used by normal sessions. + const readyDisposable = panel.webview.onDidReceiveMessage((msg) => { if (msg.type !== "webviewReady") return readyDisposable.dispose() - // Small delay to let KiloProvider's own webviewReady handler finish first - await new Promise((resolve) => setTimeout(resolve, 50)) + provider.postMessage({ type: "viewSubAgentSession", sessionID }) + void provider.loadMessages(sessionID) try { const client = this.connectionService.getClient() - const { data: session } = await client.session.get({ sessionID }, { throwOnError: true }) - - // Register the session on the provider — this adds it to - // trackedSessionIds for live SSE updates and sends - // sessionCreated to the webview. - provider.registerSession(session) - - // Fetch the newest page before navigating so the tab opens on the latest turn. - await provider.loadMessages(sessionID) - - // Navigate to the sub-agent viewer - provider.postMessage({ type: "viewSubAgentSession", sessionID }) + void client.session + .get({ sessionID }, { throwOnError: true }) + .then(({ data: session }) => provider.registerSession(session)) + .catch((err: unknown) => { + console.error("[Kilo New] SubAgentViewerProvider: Failed to load session metadata:", err) + }) } catch (err) { - console.error("[Kilo New] SubAgentViewerProvider: Failed to load session:", err) + console.error("[Kilo New] SubAgentViewerProvider: Failed to load session metadata:", err) } }) diff --git a/packages/kilo-vscode/src/kilo-provider/message-page.ts b/packages/kilo-vscode/src/kilo-provider/message-page.ts index 504182a0c3..e9a13a0aeb 100644 --- a/packages/kilo-vscode/src/kilo-provider/message-page.ts +++ b/packages/kilo-vscode/src/kilo-provider/message-page.ts @@ -26,8 +26,7 @@ export async function fetchMessagePage( signal?: AbortSignal }, ) { - // limit: 0 is the server contract for "return every message" — used by - // the sub-agent viewer, which has no "load earlier" UI. + // limit: 0 is the server contract for "return every message". const full = input.limit === 0 const read = async (before?: string) => { const result = await retry(() => diff --git a/packages/kilo-vscode/tests/unit/kilo-provider-load-messages.test.ts b/packages/kilo-vscode/tests/unit/kilo-provider-load-messages.test.ts index a63808d556..3145932a2c 100644 --- a/packages/kilo-vscode/tests/unit/kilo-provider-load-messages.test.ts +++ b/packages/kilo-vscode/tests/unit/kilo-provider-load-messages.test.ts @@ -1,4 +1,5 @@ import { describe, it, expect } from "bun:test" +import type { PartUpdate } from "../../src/shared/stream-messages" // vscode mock is provided by the shared preload (tests/setup/vscode-mock.ts) const { KiloProvider } = await import("../../src/KiloProvider") @@ -115,6 +116,7 @@ type ProviderInternals = { contextSessionID: string | undefined sessionDirectories: Map trackedSessionIds: Set + streams: { push: (msg: PartUpdate) => void } stopCurrentSessionProcesses: (next?: string) => void handleEvent: (event: unknown) => void handleLoadMessages: (sid: string, opts?: { mode?: string; before?: string; limit?: number }) => Promise @@ -371,30 +373,64 @@ describe("KiloProvider.handleLoadMessages / slim payload", () => { }) }) -describe("KiloProvider.loadMessages / sub-agent viewer full history", () => { - it("loads all messages without the MESSAGE_PAGE_LIMIT cap (sub-agent viewer needs full turn history)", async () => { - // Regression: SubAgentViewerProvider used to call client.session.messages - // with no limit, loading every turn. After switching to provider.loadMessages - // it inherited the 80-message page cap and sub-agents with more than 80 - // turns would open truncated with no visible indicator. loadMessages() is - // the sub-agent viewer's single entry point — it must request the full - // transcript. - const big = Array.from({ length: 200 }, (_, i) => mkMessage(`m${i}`, i % 2 === 0 ? "user" : "assistant", i)) - const client = createClient({ messagesData: big }) +describe("KiloProvider.loadMessages / sub-agent viewer", () => { + it("uses the same paginated initial load as normal sessions", async () => { + const page = Array.from({ length: 80 }, (_, i) => mkMessage(`m${i}`, i % 2 === 0 ? "user" : "assistant", i)) + const client = createClient({ messagesData: page }) const { provider, sent } = makeProvider(client) await provider.loadMessages("s1") const loaded = sent.find( (msg) => typeof msg === "object" && msg && (msg as { type?: unknown }).type === "messagesLoaded", - ) as { messages: unknown[] } | undefined - expect(loaded).toBeDefined() - expect(loaded!.messages).toHaveLength(200) + ) as { messages: unknown[]; hasMore: boolean } | undefined + expect(loaded?.messages).toHaveLength(80) + expect(loaded?.hasMore).toBe(true) + expect(client.calls).toEqual([{ before: undefined, limit: 80 }]) + }) - // Server contract: limit: 0 (or undefined) returns everything. - expect(client.calls).toHaveLength(1) - const limit = client.calls[0]?.limit - expect(limit === undefined || limit === 0).toBe(true) + it("delivers reasoning updates received during the initial snapshot after messagesLoaded", async () => { + const pending = defer<{ data: unknown[]; response: { headers: Headers } }>() + const client = createClient({ messagesDeferred: pending }) + const { provider, internal, sent } = makeProvider(client) + const load = provider.loadMessages("s1") + + internal.streams.push({ + type: "partUpdated", + sessionID: "s1", + messageID: "m2", + part: { + id: "r1", + sessionID: "s1", + messageID: "m2", + type: "reasoning", + text: "Complete reasoning", + }, + }) + pending.resolve( + mkResult([ + mkMessage("m1", "user", 1), + { + ...mkMessage("m2", "assistant", 2), + parts: [ + { + id: "r1", + sessionID: "s1", + messageID: "m2", + type: "reasoning", + text: "", + }, + ], + }, + ]), + ) + await load + + const types = sent.map((msg) => (typeof msg === "object" && msg ? (msg as { type?: string }).type : undefined)) + const snapshot = types.indexOf("messagesLoaded") + const update = types.findIndex((type) => type === "partUpdated" || type === "partsUpdated") + expect(snapshot).toBeGreaterThanOrEqual(0) + expect(update).toBeGreaterThan(snapshot) }) }) From fee5cf441a6a6087167e686e5e071e59b7033b97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Catriel=20M=C3=BCller?= Date: Thu, 11 Jun 2026 10:44:33 -0300 Subject: [PATCH 02/33] fix: prevent Bun downgrades during upstream merges --- .kilo/plans/1781184434428-neon-island.md | 24 ++++++++ package.json | 2 +- script/upstream/README.md | 6 +- script/upstream/merge.ts | 17 ++++++ .../transforms/transform-package-json.test.ts | 61 ++++++++++++++++++- .../transforms/transform-package-json.ts | 51 ++++++++++++++++ 6 files changed, 158 insertions(+), 3 deletions(-) create mode 100644 .kilo/plans/1781184434428-neon-island.md diff --git a/.kilo/plans/1781184434428-neon-island.md b/.kilo/plans/1781184434428-neon-island.md new file mode 100644 index 0000000000..c175e33ae4 --- /dev/null +++ b/.kilo/plans/1781184434428-neon-island.md @@ -0,0 +1,24 @@ +# Prevent Bun downgrades during upstream merges + +## Goal + +Ensure `script/upstream/merge.ts` keeps the newer root `packageManager` Bun version across Kilo and upstream: accept an upstream upgrade, preserve Kilo when upstream is older, and fail before finalizing if the merged result is below either input. + +## Implementation + +1. Update `script/upstream/transforms/transform-package-json.ts` with a small, exported Bun package-manager reconciliation helper that: + - Applies only to the root `package.json`. + - Parses `bun@` values and compares them with the existing version comparison logic. + - Selects the newer value from Kilo/base and upstream, preserving the original full `packageManager` string. + - Treats malformed or missing values conservatively so an unparseable upstream value cannot replace a valid Kilo value. + - Records a transform change when it restores a newer Kilo version. +2. Invoke that helper in every root package reconciliation path: conflicted package transformation, pre-merge package transformation, and post-merge reconciliation. This makes an upstream `bun@1.3.15` upgrade flow through while preventing an upstream `bun@1.3.13` from replacing Kilo's `bun@1.3.14`. +3. Add a final guard in `script/upstream/merge.ts`, after successful package reconciliation and before finalization/push, that compares the working-tree Bun version with both the base commit and pristine upstream target. Abort with a clear error if the result is lower than the newest valid input, protecting against future transform/rerere regressions. +4. Extend `script/upstream/transforms/transform-package-json.test.ts` with focused cases for Kilo-newer, upstream-newer, equal, non-root, and malformed package-manager values. Cover the final comparison/guard through an exported pure assertion helper rather than mocking git. +5. Update `script/upstream/README.md` to document the newest-Bun-wins behavior and the no-downgrade final validation. + +## Validation + +- Run `bun test script/upstream/transforms/transform-package-json.test.ts`. +- Run the repository typecheck (`bun run typecheck`) to validate the updated transform and merge orchestration types. +- No changeset is needed because this only changes internal upstream-merge tooling. diff --git a/package.json b/package.json index 96965ca363..4a60f5b96d 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "AI-powered development tool", "private": true, "type": "module", - "packageManager": "bun@1.3.13", + "packageManager": "bun@1.3.14", "scripts": { "dev": "bun run --cwd packages/opencode --conditions=browser src/index.ts", "dev:storybook": "bun --cwd packages/storybook storybook", diff --git a/script/upstream/README.md b/script/upstream/README.md index 2024a11ad6..0423f0dd88 100644 --- a/script/upstream/README.md +++ b/script/upstream/README.md @@ -48,7 +48,7 @@ bun run merge.ts --version v1.1.50 --base-branch catrielmuller/kilo-opencode-v1. | `transforms/skip-files.ts` | Skip/remove files that shouldn't exist in Kilo | | `transforms/transform-i18n.ts` | Transform i18n files with Kilo branding | | `transforms/transform-take-theirs.ts` | Take upstream + apply Kilo branding for branding-only files | -| `transforms/transform-package-json.ts` | Enhanced package.json with Kilo dependency injection | +| `transforms/transform-package-json.ts` | Enhanced package.json with Kilo dependency injection and newest-Bun-wins reconciliation | | `transforms/transform-scripts.ts` | Transform script files with GitHub API references | | `transforms/transform-extensions.ts` | Transform extension files (Zed, etc.) | | `transforms/transform-web.ts` | Transform web/docs files (.mdx) | @@ -196,6 +196,10 @@ Now: The only remaining conflicts are files with **actual code differences** - files with `kilocode_change` markers that contain Kilo-specific logic. +### Bun Version Safety + +Root `package.json` reconciliation uses the newer valid `packageManager` Bun version from Kilo and upstream. An older upstream version cannot downgrade Kilo, while a newer upstream version is retained as an upgrade. Before the merge is finalized, `merge.ts` also validates the result against the pristine Kilo base and upstream commit and aborts if the merged Bun version is lower than either input. + ## CLI Options ### merge.ts diff --git a/script/upstream/merge.ts b/script/upstream/merge.ts index 0e1d9a5f1e..ca30c69d87 100644 --- a/script/upstream/merge.ts +++ b/script/upstream/merge.ts @@ -37,6 +37,7 @@ import { transformConflictedPackageJson, transformAllPackageJson, reconcileAllPackageJson, + assertBunPackageManager, } from "./transforms/transform-package-json" import { transformConflictedScripts, transformAllScripts } from "./transforms/transform-scripts" import { transformConflictedExtensions, transformAllExtensions } from "./transforms/transform-extensions" @@ -206,6 +207,20 @@ async function getAuthor(): Promise { .replace(/\s+/g, "") } +function manager(content: string): string | undefined { + const pkg: unknown = JSON.parse(content) + if (!pkg || typeof pkg !== "object" || !("packageManager" in pkg)) return undefined + return typeof pkg.packageManager === "string" ? pkg.packageManager : undefined +} + +async function validateBun(base: string, upstream: string): Promise { + const current = manager(await Bun.file("package.json").text()) + const ours = manager(await $`git show ${base}:package.json`.text()) + const theirs = manager(await $`git show ${upstream}:package.json`.text()) + assertBunPackageManager(current, ours, theirs) + logger.success(`Validated Bun packageManager: ${current ?? "missing"}`) +} + async function createBackupBranch(baseBranch: string): Promise { const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19) const backupName = `backup/${baseBranch}-${timestamp}` @@ -807,6 +822,7 @@ async function main() { // Exit early - don't continue to finalization steps process.exit(1) } else { + await validateBun(baseSha, targetVersion.commit) await git.stageAll() await git.commit(`merge: upstream ${targetVersion.tag}`) logger.success("Merge completed - all conflicts auto-resolved!") @@ -824,6 +840,7 @@ async function main() { if (reconcileCount > 0) { logger.success(`Reconciled ${reconcileCount} package.json file(s) post-merge`) } + await validateBun(baseSha, targetVersion.commit) await git.stageAll() const hasChanges = await git.hasUncommittedChanges() if (hasChanges) { diff --git a/script/upstream/transforms/transform-package-json.test.ts b/script/upstream/transforms/transform-package-json.test.ts index 60e6d46e09..59020c4366 100644 --- a/script/upstream/transforms/transform-package-json.test.ts +++ b/script/upstream/transforms/transform-package-json.test.ts @@ -1,5 +1,13 @@ import { expect, test } from "bun:test" -import { fixCatalog, fixMetadata, fixScripts, mergeWithNewestVersions } from "./transform-package-json" +import { + assertBunPackageManager, + fixCatalog, + fixMetadata, + fixPackageManager, + fixScripts, + mergeWithNewestVersions, + selectBunPackageManager, +} from "./transform-package-json" test("fixScripts preserves Kilo-only root scripts from base", () => { const ours = { @@ -125,3 +133,54 @@ test("mergeWithNewestVersions appends theirs-only keys at the end", () => { const result = mergeWithNewestVersions(ours, theirs, changes, "dependencies") expect(Object.keys(result)).toEqual(["a", "b", "c"]) }) + +test("selectBunPackageManager keeps the newer Bun version", () => { + expect(selectBunPackageManager("bun@1.3.14", "bun@1.3.13")).toBe("bun@1.3.14") + expect(selectBunPackageManager("bun@1.3.14", "bun@1.3.15")).toBe("bun@1.3.15") + expect(selectBunPackageManager("bun@1.3.14", "bun@1.3.14")).toBe("bun@1.3.14") +}) + +test("selectBunPackageManager preserves valid versions over malformed values", () => { + expect(selectBunPackageManager("bun@1.3.14", "bun@latest")).toBe("bun@1.3.14") + expect(selectBunPackageManager("bun@latest", "bun@1.3.15")).toBe("bun@1.3.15") + expect(selectBunPackageManager("bun@latest", "npm@11.0.0")).toBeUndefined() +}) + +test("fixPackageManager prevents root Bun downgrades", () => { + const pkg: Record = { packageManager: "bun@1.3.13" } + const ours = { packageManager: "bun@1.3.14" } + const changes: string[] = [] + fixPackageManager(pkg, "package.json", ours, changes) + expect(pkg.packageManager).toBe("bun@1.3.14") + expect(changes).toEqual(["packageManager: bun@1.3.13 -> bun@1.3.14 (Kilo newer)"]) +}) + +test("fixPackageManager accepts upstream Bun upgrades", () => { + const pkg: Record = { packageManager: "bun@1.3.15" } + const changes: string[] = [] + fixPackageManager(pkg, "package.json", { packageManager: "bun@1.3.14" }, changes) + expect(pkg.packageManager).toBe("bun@1.3.15") + expect(changes).toEqual([]) +}) + +test("fixPackageManager ignores nested package.json files", () => { + const pkg: Record = { packageManager: "bun@1.3.13" } + const changes: string[] = [] + fixPackageManager(pkg, "packages/opencode/package.json", { packageManager: "bun@1.3.14" }, changes) + expect(pkg.packageManager).toBe("bun@1.3.13") + expect(changes).toEqual([]) +}) + +test("assertBunPackageManager rejects merged downgrades and invalid values", () => { + expect(() => assertBunPackageManager("bun@1.3.13", "bun@1.3.14", "bun@1.3.12")).toThrow( + "Bun packageManager downgrade detected", + ) + expect(() => assertBunPackageManager("bun@latest", "bun@1.3.14", "bun@1.3.15")).toThrow( + "Bun packageManager validation failed", + ) +}) + +test("assertBunPackageManager accepts the newest input or a newer result", () => { + expect(() => assertBunPackageManager("bun@1.3.15", "bun@1.3.14", "bun@1.3.15")).not.toThrow() + expect(() => assertBunPackageManager("bun@1.3.16", "bun@1.3.14", "bun@1.3.15")).not.toThrow() +}) diff --git a/script/upstream/transforms/transform-package-json.ts b/script/upstream/transforms/transform-package-json.ts index 20fa0b280a..7986a847db 100644 --- a/script/upstream/transforms/transform-package-json.ts +++ b/script/upstream/transforms/transform-package-json.ts @@ -105,6 +105,51 @@ function compareVersions(a: string, b: string): number | null { return 0 } +function bun(value: unknown): { value: string; version: string } | null { + if (typeof value !== "string") return null + const match = value.match(/^bun@(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)$/) + if (!match) return null + if (compareVersions(match[1], match[1]) === null) return null + return { value, version: match[1] } +} + +export function selectBunPackageManager(ours: unknown, theirs: unknown): string | undefined { + const left = bun(ours) + const right = bun(theirs) + if (left && right) return compareVersions(left.version, right.version)! > 0 ? left.value : right.value + if (left) return left.value + if (right) return right.value + return undefined +} + +export function fixPackageManager( + pkg: Record, + path: string, + ours: Record | null, + changes: string[], +): void { + if (path !== "package.json") return + const next = selectBunPackageManager(ours?.packageManager, pkg.packageManager) + if (!next || pkg.packageManager === next) return + const prior = typeof pkg.packageManager === "string" ? pkg.packageManager : "missing or invalid" + changes.push(`packageManager: ${prior} -> ${next} (Kilo newer)`) + pkg.packageManager = next +} + +export function assertBunPackageManager(current: unknown, base: unknown, upstream: unknown): void { + const inputs = [bun(base), bun(upstream)].filter((item): item is NonNullable => item !== null) + if (inputs.length === 0) return + const required = inputs.reduce((max, item) => (compareVersions(item.version, max.version)! > 0 ? item : max)) + const actual = bun(current) + if (!actual) { + throw new Error( + `Bun packageManager validation failed: merged value is invalid; expected at least ${required.value}`, + ) + } + if (compareVersions(actual.version, required.version)! >= 0) return + throw new Error(`Bun packageManager downgrade detected: merged ${actual.value}, expected at least ${required.value}`) +} + /** * Merge two dependency objects using "newest wins" strategy * For non-comparable versions (URLs, catalog:, workspace:*), upstream (theirs) wins @@ -392,6 +437,8 @@ export async function transformPackageJson(file: string, options: PackageJsonOpt pkg.name = newName } + fixPackageManager(pkg, relativePath, ourPkg, changes) + // 2. Preserve Kilo version if requested if (options.preserveVersion !== false) { const kiloVersion = await getCurrentVersion() @@ -612,6 +659,8 @@ export async function transformAllPackageJson(options: PackageJsonOptions = {}): pkg.name = newName } + fixPackageManager(pkg, path, kiloPkg, changes) + // 2. Preserve Kilo version if requested if (options.preserveVersion !== false) { const kiloVersion = await getCurrentVersion() @@ -824,6 +873,8 @@ export async function reconcilePackageJsonFromRefs( pkg.name = newName } + fixPackageManager(pkg, relativePath, ourPkg, changes) + if (options.preserveVersion !== false) { const kiloVersion = await getCurrentVersion() if (pkg.version !== kiloVersion) { From ea92eb2244fe892e9f9890fd43f958909f19efe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Catriel=20M=C3=BCller?= Date: Thu, 11 Jun 2026 10:46:28 -0300 Subject: [PATCH 03/33] chore: remove implementation plan --- .kilo/plans/1781184434428-neon-island.md | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 .kilo/plans/1781184434428-neon-island.md diff --git a/.kilo/plans/1781184434428-neon-island.md b/.kilo/plans/1781184434428-neon-island.md deleted file mode 100644 index c175e33ae4..0000000000 --- a/.kilo/plans/1781184434428-neon-island.md +++ /dev/null @@ -1,24 +0,0 @@ -# Prevent Bun downgrades during upstream merges - -## Goal - -Ensure `script/upstream/merge.ts` keeps the newer root `packageManager` Bun version across Kilo and upstream: accept an upstream upgrade, preserve Kilo when upstream is older, and fail before finalizing if the merged result is below either input. - -## Implementation - -1. Update `script/upstream/transforms/transform-package-json.ts` with a small, exported Bun package-manager reconciliation helper that: - - Applies only to the root `package.json`. - - Parses `bun@` values and compares them with the existing version comparison logic. - - Selects the newer value from Kilo/base and upstream, preserving the original full `packageManager` string. - - Treats malformed or missing values conservatively so an unparseable upstream value cannot replace a valid Kilo value. - - Records a transform change when it restores a newer Kilo version. -2. Invoke that helper in every root package reconciliation path: conflicted package transformation, pre-merge package transformation, and post-merge reconciliation. This makes an upstream `bun@1.3.15` upgrade flow through while preventing an upstream `bun@1.3.13` from replacing Kilo's `bun@1.3.14`. -3. Add a final guard in `script/upstream/merge.ts`, after successful package reconciliation and before finalization/push, that compares the working-tree Bun version with both the base commit and pristine upstream target. Abort with a clear error if the result is lower than the newest valid input, protecting against future transform/rerere regressions. -4. Extend `script/upstream/transforms/transform-package-json.test.ts` with focused cases for Kilo-newer, upstream-newer, equal, non-root, and malformed package-manager values. Cover the final comparison/guard through an exported pure assertion helper rather than mocking git. -5. Update `script/upstream/README.md` to document the newest-Bun-wins behavior and the no-downgrade final validation. - -## Validation - -- Run `bun test script/upstream/transforms/transform-package-json.test.ts`. -- Run the repository typecheck (`bun run typecheck`) to validate the updated transform and merge orchestration types. -- No changeset is needed because this only changes internal upstream-merge tooling. From 50dbbb30d11009d6f771ea625168bf5bb44beb83 Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Thu, 11 Jun 2026 16:19:23 +0200 Subject: [PATCH 04/33] fix(agent-manager): avoid blocked native autofocus --- .../kilo-vscode/webview-ui/agent-manager/NewWorktreeDialog.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/kilo-vscode/webview-ui/agent-manager/NewWorktreeDialog.tsx b/packages/kilo-vscode/webview-ui/agent-manager/NewWorktreeDialog.tsx index af364479a1..4d79b0cf04 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/NewWorktreeDialog.tsx +++ b/packages/kilo-vscode/webview-ui/agent-manager/NewWorktreeDialog.tsx @@ -396,7 +396,6 @@ export const NewWorktreeDialog: Component<{ onClose: () => void; defaultBaseBran