Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-queued-message-state.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---

Fix queued-state detection so prompts sent after a completed response are treated as active instead of queued.
43 changes: 43 additions & 0 deletions packages/kilo-vscode/tests/unit/session-queue.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { describe, expect, it } from "bun:test"
import { activeUserMessageID } from "../../webview-ui/src/context/session-queue"
import type { Message } from "../../webview-ui/src/types/messages"

const base = {
sessionID: "session",
createdAt: "2026-01-01T00:00:00.000Z",
time: { created: 1 },
}

const user = (id: string): Message => ({ ...base, id, role: "user" })

const assistant = (id: string, parentID: string, opts: Partial<Message> = {}): Message => ({
...base,
id,
parentID,
role: "assistant",
...opts,
})

describe("activeUserMessageID", () => {
it("ignores terminal assistant updates without completed timestamps", () => {
const messages = [user("message_1"), assistant("message_2", "message_1", { finish: "stop" }), user("message_3")]

expect(activeUserMessageID(messages, { type: "busy" })).toBe("message_3")
})

it("keeps tool-call assistants active until their follow-up finishes", () => {
const messages = [
user("message_1"),
assistant("message_2", "message_1", { finish: "tool-calls" }),
user("message_3"),
]

expect(activeUserMessageID(messages, { type: "busy" })).toBe("message_1")
})

it("keeps unknown assistants active until cleanup finishes", () => {
const messages = [user("message_1"), assistant("message_2", "message_1", { finish: "unknown" }), user("message_3")]

expect(activeUserMessageID(messages, { type: "busy" })).toBe("message_1")
})
})
3 changes: 1 addition & 2 deletions packages/kilo-vscode/webview-ui/src/context/session-queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,17 @@ import type { Message, SessionStatusInfo } from "../types/messages"
// Find the user message whose turn the server is actively processing.
// Any user message after this one is "queued" (waiting for its turn).
export function activeUserMessageID(messages: Message[], status: SessionStatusInfo) {
// Walk backward to find a non-completed assistant — its parent is the active turn
for (let i = messages.length - 1; i >= 0; i -= 1) {
const msg = messages[i]
if (msg.role !== "assistant") continue
if (typeof msg.time?.completed === "number") continue
if (msg.finish && !["tool-calls", "unknown"].includes(msg.finish)) continue
if (!msg.parentID) break
const parent = messages.find((item) => item.id === msg.parentID)
if (parent?.role === "user") return parent.id
break
}

// No pending assistant found — if busy, the last user message is the active turn
if (status.type === "idle") return undefined

for (let i = messages.length - 1; i >= 0; i -= 1) {
Expand Down
1 change: 1 addition & 0 deletions packages/kilo-vscode/webview-ui/src/types/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ export interface Message {
summary?: { title?: string; body?: string; diffs?: unknown[] } | boolean
cost?: number
tokens?: TokenUsage
finish?: string
}

// File diff info (matches Snapshot.FileDiff from CLI backend)
Expand Down
Loading