From b901ff862019c9f0b18fff3f3429206145a1a22d Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Mon, 20 Apr 2026 12:02:20 +0200 Subject: [PATCH 1/7] feat(agent-manager): fork sessions from user messages --- .changeset/agent-manager-message-fork.md | 5 +++++ packages/kilo-ui/src/components/message-part.tsx | 16 ++++++++++++++++ .../src/agent-manager/AgentManagerProvider.ts | 5 +++-- .../src/agent-manager/fork-session.ts | 10 ++++++++-- packages/kilo-vscode/src/agent-manager/types.ts | 1 + .../webview-ui/agent-manager/AgentManagerApp.tsx | 10 +++++----- .../webview-ui/src/components/chat/ChatView.tsx | 2 ++ .../src/components/chat/MessageList.tsx | 3 ++- .../src/components/chat/VscodeSessionTurn.tsx | 2 ++ .../kilo-vscode/webview-ui/src/types/messages.ts | 1 + 10 files changed, 45 insertions(+), 10 deletions(-) create mode 100644 .changeset/agent-manager-message-fork.md diff --git a/.changeset/agent-manager-message-fork.md b/.changeset/agent-manager-message-fork.md new file mode 100644 index 00000000000..23fc7bdf7f3 --- /dev/null +++ b/.changeset/agent-manager-message-fork.md @@ -0,0 +1,5 @@ +--- +"kilo-code": minor +--- + +Support forking Agent Manager sessions from a specific user message. diff --git a/packages/kilo-ui/src/components/message-part.tsx b/packages/kilo-ui/src/components/message-part.tsx index 1bbbdb1eb7a..e7392935d2a 100644 --- a/packages/kilo-ui/src/components/message-part.tsx +++ b/packages/kilo-ui/src/components/message-part.tsx @@ -706,6 +706,7 @@ export function UserMessageDisplay(props: { interrupted?: boolean animate?: boolean queued?: boolean + onFork?: () => void onRevert?: () => void }) { const data = useData() @@ -847,6 +848,21 @@ export function UserMessageDisplay(props: { + + + e.preventDefault()} + onClick={(event) => { + event.stopPropagation() + props.onFork?.() + }} + aria-label={i18n.t("ui.message.forkMessage")} + /> + + this.connectionService.getClient(), @@ -951,6 +951,7 @@ export class AgentManagerProvider implements Disposable { }, sessionId, worktreeId, + messageId, ) } diff --git a/packages/kilo-vscode/src/agent-manager/fork-session.ts b/packages/kilo-vscode/src/agent-manager/fork-session.ts index 2f688fdbabd..c78e8086b14 100644 --- a/packages/kilo-vscode/src/agent-manager/fork-session.ts +++ b/packages/kilo-vscode/src/agent-manager/fork-session.ts @@ -21,7 +21,12 @@ export interface ForkContext { * * Pure orchestration — no vscode imports. */ -export async function forkSession(ctx: ForkContext, sessionId: string, worktreeId?: string): Promise { +export async function forkSession( + ctx: ForkContext, + sessionId: string, + worktreeId?: string, + messageId?: string, +): Promise { let client: KiloClient try { client = ctx.getClient() @@ -38,7 +43,8 @@ export async function forkSession(ctx: ForkContext, sessionId: string, worktreeI let forked: Session try { - const { data } = await client.session.fork({ sessionID: sessionId, directory }, { throwOnError: true }) + const input = { sessionID: sessionId, directory, ...(messageId ? { messageID: messageId } : {}) } + const { data } = await client.session.fork(input, { throwOnError: true }) forked = data } catch (error) { const err = getErrorMessage(error) diff --git a/packages/kilo-vscode/src/agent-manager/types.ts b/packages/kilo-vscode/src/agent-manager/types.ts index 8392b332014..86807ba4ce9 100644 --- a/packages/kilo-vscode/src/agent-manager/types.ts +++ b/packages/kilo-vscode/src/agent-manager/types.ts @@ -590,6 +590,7 @@ interface ForkSessionIn { type: "agentManager.forkSession" sessionId: string worktreeId?: string + messageId?: string } interface AbortIn { diff --git a/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx b/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx index ffdd0c1031b..c9ac1142019 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx +++ b/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx @@ -1888,13 +1888,12 @@ const AgentManagerContent: Component = () => { if (sel === LOCAL) addPendingTab() else if (sel) vscode.postMessage({ type: "agentManager.addSessionToWorktree", worktreeId: sel }) } - - const handleForkSession = (sessionId: string) => { + const handleForkSession = (sessionId: string, messageId?: string) => { const sel = selection() - if (sel === LOCAL) vscode.postMessage({ type: "agentManager.forkSession", sessionId }) - else if (sel) vscode.postMessage({ type: "agentManager.forkSession", sessionId, worktreeId: sel }) + const msg = { type: "agentManager.forkSession" as const, sessionId, ...(messageId ? { messageId } : {}) } + if (sel === LOCAL) return vscode.postMessage(msg) + if (sel) vscode.postMessage({ ...msg, worktreeId: sel }) } - const handleCloseTab = (sessionId: string) => { const pending = isPending(sessionId) const isActive = pending ? sessionId === activePendingId() : session.currentSessionID() === sessionId @@ -2948,6 +2947,7 @@ const AgentManagerContent: Component = () => { openLocally(id) }} onShowHistory={() => setHistory(true)} + onForkMessage={readOnly() ? undefined : handleForkSession} readonly={readOnly()} continueInWorktree={selection() === LOCAL} promptBoxId={`agent-manager:${selection() ?? "unassigned"}`} diff --git a/packages/kilo-vscode/webview-ui/src/components/chat/ChatView.tsx b/packages/kilo-vscode/webview-ui/src/components/chat/ChatView.tsx index 6e6969b60f5..7da3fa1dce4 100644 --- a/packages/kilo-vscode/webview-ui/src/components/chat/ChatView.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/chat/ChatView.tsx @@ -24,6 +24,7 @@ import { isPromptBlocked, isSuggesting, isQuestioning } from "./prompt-input-uti interface ChatViewProps { onSelectSession?: (id: string) => void onShowHistory?: () => void + onForkMessage?: (sessionId: string, messageId: string) => void readonly?: boolean /** When true, show the "Continue in Worktree" button. Defaults to true in the sidebar. */ continueInWorktree?: boolean @@ -136,6 +137,7 @@ export const ChatView: Component = (props) => { { interface MessageListProps { onSelectSession?: (id: string) => void onShowHistory?: () => void + onForkMessage?: (sessionId: string, messageId: string) => void /** Non-tool question requests to render inline at the bottom of the message list */ questions?: () => QuestionRequest[] /** Non-tool suggestion requests to render inline at the bottom of the message list */ @@ -238,7 +239,7 @@ export const MessageList: Component = (props) => { return index() > active }) - return + return }} diff --git a/packages/kilo-vscode/webview-ui/src/components/chat/VscodeSessionTurn.tsx b/packages/kilo-vscode/webview-ui/src/components/chat/VscodeSessionTurn.tsx index 4daaaa6f318..01e84785c70 100644 --- a/packages/kilo-vscode/webview-ui/src/components/chat/VscodeSessionTurn.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/chat/VscodeSessionTurn.tsx @@ -55,6 +55,7 @@ export interface VscodeTurn { interface VscodeSessionTurnProps { turn: VscodeTurn queued?: boolean + onForkMessage?: (sessionId: string, messageId: string) => void } export const VscodeSessionTurn: Component = (props) => { @@ -153,6 +154,7 @@ export const VscodeSessionTurn: Component = (props) => { parts={parts() as unknown as Parameters[0]["parts"]} interrupted={interrupted()} queued={props.queued} + onFork={props.onForkMessage ? () => props.onForkMessage?.(msg().sessionID, msg().id) : undefined} onRevert={ assistantMessages().length > 0 && !session.revert() ? () => { diff --git a/packages/kilo-vscode/webview-ui/src/types/messages.ts b/packages/kilo-vscode/webview-ui/src/types/messages.ts index 3b82ebc252e..69fda551c73 100644 --- a/packages/kilo-vscode/webview-ui/src/types/messages.ts +++ b/packages/kilo-vscode/webview-ui/src/types/messages.ts @@ -2054,6 +2054,7 @@ export interface ForkSessionRequest { type: "agentManager.forkSession" sessionId: string worktreeId?: string + messageId?: string } // Close (remove) a session from its worktree From f0c763ef76913d47658bfee045b4a4a0da26c51c Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Mon, 20 Apr 2026 12:14:21 +0200 Subject: [PATCH 2/7] fix(agent-manager): handle unassigned message forks --- .../kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx b/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx index c9ac1142019..61bc4d6d782 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx +++ b/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx @@ -1891,8 +1891,8 @@ const AgentManagerContent: Component = () => { const handleForkSession = (sessionId: string, messageId?: string) => { const sel = selection() const msg = { type: "agentManager.forkSession" as const, sessionId, ...(messageId ? { messageId } : {}) } - if (sel === LOCAL) return vscode.postMessage(msg) - if (sel) vscode.postMessage({ ...msg, worktreeId: sel }) + if (!sel || sel === LOCAL) return vscode.postMessage(msg) + vscode.postMessage({ ...msg, worktreeId: sel }) } const handleCloseTab = (sessionId: string) => { const pending = isPending(sessionId) From a8c83ac3f35301f350baeae1ed0c70bc46c786ec Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Mon, 20 Apr 2026 12:32:50 +0200 Subject: [PATCH 3/7] test(vscode): register chat tool overrides --- packages/kilo-vscode/webview-ui/src/stories/chat.stories.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/kilo-vscode/webview-ui/src/stories/chat.stories.tsx b/packages/kilo-vscode/webview-ui/src/stories/chat.stories.tsx index 67229d5d457..505c38d60ac 100644 --- a/packages/kilo-vscode/webview-ui/src/stories/chat.stories.tsx +++ b/packages/kilo-vscode/webview-ui/src/stories/chat.stories.tsx @@ -14,10 +14,13 @@ import { TaskHeader } from "../components/chat/TaskHeader" import { QuestionDock } from "../components/chat/QuestionDock" import { SuggestBar } from "../components/chat/SuggestBar" import { MessageList } from "../components/chat/MessageList" +import { registerVscodeToolOverrides } from "../components/chat/VscodeToolOverrides" import { SessionContext } from "../context/session" import { ServerContext } from "../context/server" import type { QuestionRequest, SuggestionRequest, TodoItem } from "../types/messages" +registerVscodeToolOverrides() + const SESSION_ID = "story-session-chat-001" // --------------------------------------------------------------------------- From 93d3d8037f9ed57137c4e796f9213abe01ac16fe Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Mon, 20 Apr 2026 12:41:12 +0200 Subject: [PATCH 4/7] fix(vscode): make tool overrides idempotent --- .../webview-ui/src/components/chat/VscodeToolOverrides.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/kilo-vscode/webview-ui/src/components/chat/VscodeToolOverrides.tsx b/packages/kilo-vscode/webview-ui/src/components/chat/VscodeToolOverrides.tsx index ba84289e659..55ddd9d102a 100644 --- a/packages/kilo-vscode/webview-ui/src/components/chat/VscodeToolOverrides.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/chat/VscodeToolOverrides.tsx @@ -12,9 +12,11 @@ import { ToolRegistry } from "@kilocode/kilo-ui/message-part" /** Tools that should be open by default in the VS Code sidebar. */ const DEFAULT_OPEN_TOOLS = ["bash"] +const registered = new Set() export function registerVscodeToolOverrides() { for (const name of DEFAULT_OPEN_TOOLS) { + if (registered.has(name)) continue const upstream = ToolRegistry.render(name) if (!upstream) continue @@ -22,5 +24,6 @@ export function registerVscodeToolOverrides() { name, render: (props) => , }) + registered.add(name) } } From ce29d13c04c92dbe6a0e328d878a442c66a36e52 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 20 Apr 2026 10:43:25 +0000 Subject: [PATCH 5/7] chore: update kilo-vscode visual regression baselines --- ...essage-list-tool-to-queued-user-spacing-chromium-linux.png | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/chat/message-list-tool-to-queued-user-spacing-chromium-linux.png b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/chat/message-list-tool-to-queued-user-spacing-chromium-linux.png index 0ccf5483f0a..f1ada1dd34e 100644 --- a/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/chat/message-list-tool-to-queued-user-spacing-chromium-linux.png +++ b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/chat/message-list-tool-to-queued-user-spacing-chromium-linux.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:55616e4acb64a04ef5f5c37707f6123561065c22a2266197b031d47c068949a4 -size 14255 +oid sha256:579c1817866f19735c86ec00d4d74bf15149f37f8ce27cfe7873bdf79f1d103f +size 12066 From c5cf753d4f12e06be5cc88612e9bfbaf08ca3165 Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Mon, 20 Apr 2026 13:12:21 +0200 Subject: [PATCH 6/7] test(vscode): revert chat story override registration --- ...essage-list-tool-to-queued-user-spacing-chromium-linux.png | 4 ++-- packages/kilo-vscode/webview-ui/src/stories/chat.stories.tsx | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/chat/message-list-tool-to-queued-user-spacing-chromium-linux.png b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/chat/message-list-tool-to-queued-user-spacing-chromium-linux.png index f1ada1dd34e..0ccf5483f0a 100644 --- a/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/chat/message-list-tool-to-queued-user-spacing-chromium-linux.png +++ b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/chat/message-list-tool-to-queued-user-spacing-chromium-linux.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:579c1817866f19735c86ec00d4d74bf15149f37f8ce27cfe7873bdf79f1d103f -size 12066 +oid sha256:55616e4acb64a04ef5f5c37707f6123561065c22a2266197b031d47c068949a4 +size 14255 diff --git a/packages/kilo-vscode/webview-ui/src/stories/chat.stories.tsx b/packages/kilo-vscode/webview-ui/src/stories/chat.stories.tsx index 505c38d60ac..67229d5d457 100644 --- a/packages/kilo-vscode/webview-ui/src/stories/chat.stories.tsx +++ b/packages/kilo-vscode/webview-ui/src/stories/chat.stories.tsx @@ -14,13 +14,10 @@ import { TaskHeader } from "../components/chat/TaskHeader" import { QuestionDock } from "../components/chat/QuestionDock" import { SuggestBar } from "../components/chat/SuggestBar" import { MessageList } from "../components/chat/MessageList" -import { registerVscodeToolOverrides } from "../components/chat/VscodeToolOverrides" import { SessionContext } from "../context/session" import { ServerContext } from "../context/server" import type { QuestionRequest, SuggestionRequest, TodoItem } from "../types/messages" -registerVscodeToolOverrides() - const SESSION_ID = "story-session-chat-001" // --------------------------------------------------------------------------- From b40bd686d69760b73cf58c94dbbbd7bd8448d7f0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 20 Apr 2026 11:15:37 +0000 Subject: [PATCH 7/7] chore: update kilo-vscode visual regression baselines --- ...essage-list-tool-to-queued-user-spacing-chromium-linux.png | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/chat/message-list-tool-to-queued-user-spacing-chromium-linux.png b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/chat/message-list-tool-to-queued-user-spacing-chromium-linux.png index 0ccf5483f0a..f1ada1dd34e 100644 --- a/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/chat/message-list-tool-to-queued-user-spacing-chromium-linux.png +++ b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/chat/message-list-tool-to-queued-user-spacing-chromium-linux.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:55616e4acb64a04ef5f5c37707f6123561065c22a2266197b031d47c068949a4 -size 14255 +oid sha256:579c1817866f19735c86ec00d4d74bf15149f37f8ce27cfe7873bdf79f1d103f +size 12066