Skip to content

feat(kilo-vscode): allow removing queued messages#8844

Open
kilo-code-bot[bot] wants to merge 12 commits intomainfrom
session/agent_90f2cfa1-8041-4251-8261-7abcae5c5ee0
Open

feat(kilo-vscode): allow removing queued messages#8844
kilo-code-bot[bot] wants to merge 12 commits intomainfrom
session/agent_90f2cfa1-8041-4251-8261-7abcae5c5ee0

Conversation

@kilo-code-bot
Copy link
Copy Markdown
Contributor

@kilo-code-bot kilo-code-bot bot commented Apr 13, 2026

Summary

  • Adds a delete control to queued user messages so users can remove pending prompts before processing.
  • Wires optimistic webview removal through the extension to the existing session message delete endpoint.

Closes #8832

- wire deleteMessage through the webview messaging system
- add DeleteMessageRequest type and handler in KiloProvider
- propagate deleteMessage through session context and UI
- render trash icon next to queued messages with deleteQueued label
- pass onDeleteQueued to UserMessageDisplay and VscodeSessionTurn
- update i18n with ui.message.deleteQueued
- adjust CSS to align controls in bash-output area
console.warn("[Kilo New] Cannot delete message: not connected")
return
}
handleMessageRemoved(sessionID, messageID)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRITICAL: Queued deletes cannot succeed while the session is busy

onDeleteQueued is only exposed for queued prompts, which only exist while another turn is still running. The server-side delete route calls SessionPrompt.assertNotBusy(), so this request is rejected every time the button is shown. Because the message is removed optimistically before the request returns, the UI looks successful until the session reloads. This needs either a queue-aware backend delete path or a rollback instead of unconditional local removal.

<GrowBox animate={!!props.animate} open={!!props.queued}>
<div data-slot="user-message-queued-indicator">
<TextShimmer text={i18n.t("ui.message.queued")} />
<Show when={props.onDeleteQueued}>
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Attachment-only queued messages never show the delete action

This button is rendered inside the surrounding Show when={text()} block. PromptInput allows sending prompts with only image/PDF attachments, so those queued messages will never render the queued indicator or delete control and cannot be removed with this feature.

@kilo-code-bot
Copy link
Copy Markdown
Contributor Author

kilo-code-bot bot commented Apr 13, 2026

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (1 files)
  • packages/opencode/src/server/routes/session.ts

Reviewed by gpt-5.4-20260305 · 432,871 tokens

// kilocode_change start
const messages = await Session.messages({ sessionID: params.sessionID })
const target = messages.find((msg) => msg.info.id === params.messageID)
const newer = messages.find((msg) => msg.info.role === "user" && msg.info.id > params.messageID)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRITICAL: This still rejects the common single-queued-message case

Using has newer user message as the proxy for is queued misses the newest queued prompt, because the last queued user message has no later user after it. In the common case of one active turn plus one queued prompt, newer is undefined, so the route still falls back to assertNotBusy() and the delete fails. It also means the currently active user message would bypass the busy guard whenever another queued prompt exists. The check needs to identify messages after the active turn, not messages with any later user in history.

const messages = await Session.messages({ sessionID: params.sessionID })
const user = messages.filter((msg) => msg.info.role === "user")
const target = user.find((msg) => msg.info.id === params.messageID)
const active = user.at(0)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRITICAL: active points at the oldest user message, not the running one

Session.messages() is consumed throughout the app as chronological history, so user.at(0) selects the first prompt in the session. While the session is busy, every later user message now skips assertNotBusy(), including the currently active prompt, so this can allow deleting the turn that is still being processed. The queued/delete check needs to compare against the latest active user turn, not the first user in history.

@kilo-code-bot
Copy link
Copy Markdown
Contributor Author

kilo-code-bot bot commented Apr 13, 2026

kilo-code-bot: please re-review latest commit 0b22c62. The active turn is now calculated from the first assistant boundary, not user.at(-1).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: Allow removing queued messages in the chat UI

1 participant