fix(agent-manager): continue sessions in worktrees

This commit is contained in:
marius-kilocode
2026-05-15 09:51:43 +02:00
parent 752022556d
commit 3031de6302
14 changed files with 211 additions and 87 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---
Keep continued Agent Manager sessions in the selected worktree instead of moving them to Local.
+23 -14
View File
@@ -135,6 +135,7 @@ import type { KiloProviderOptions } from "./kilo-provider/options"
import { fetchKiloEmbeddingModelCatalog } from "@kilocode/kilo-gateway"
type MessageLoadMode = "replace" | "prepend" | "focus" | "reconcile"
type ContextMessage = { contextDirectory?: unknown }
// Helper to map agent data to the subset of fields sent to the webview
const mapAgent = (a: Agent) => ({
name: a.name,
@@ -634,7 +635,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
this.readyResolvers.splice(0).forEach((r) => r())
break
case "sendMessage": {
const files = parseMessageFiles(message.files)
const msg = message as typeof message & ContextMessage
await this.handleSendMessage(
message.text,
typeof message.messageID === "string" ? message.messageID : undefined,
@@ -644,13 +645,14 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
message.modelID,
message.agent,
message.variant,
files,
parseMessageFiles(message.files),
typeof message.agentManagerContext === "string" ? message.agentManagerContext : undefined,
typeof msg.contextDirectory === "string" ? msg.contextDirectory : undefined,
)
break
}
case "sendCommand": {
const files = parseMessageFiles(message.files)
const msg = message as typeof message & ContextMessage
await this.handleSendCommand(
message.command,
message.arguments,
@@ -661,8 +663,9 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
message.modelID,
message.agent,
message.variant,
files,
parseMessageFiles(message.files),
typeof message.agentManagerContext === "string" ? message.agentManagerContext : undefined,
typeof msg.contextDirectory === "string" ? msg.contextDirectory : undefined,
)
break
}
@@ -1068,13 +1071,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
break
}
case "fetchMarketplaceData": {
const workspace = this.getProjectDirectory(this.currentSession?.id)
const mp = this.getMarketplace()
// Fetch skills from CLI backend (authoritative source) so the
// marketplace doesn't need to duplicate the CLI's skill scanning.
const skills = await this.fetchCliSkills()
const data = await mp.fetchData(workspace, skills)
this.postMessage({ type: "marketplaceData", ...data })
await this.handleFetchMarketplaceData()
break
}
case "filterMarketplaceItems": {
@@ -1124,6 +1121,14 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
this.diffVirtualProvider.open(d)
}
private async handleFetchMarketplaceData(): Promise<void> {
const workspace = this.getProjectDirectory(this.currentSession?.id)
const mp = this.getMarketplace()
const skills = await this.fetchCliSkills()
const data = await mp.fetchData(workspace, skills)
this.postMessage({ type: "marketplaceData", ...data })
}
/**
* Initialize connection to the CLI backend server.
* Subscribes to the shared KiloConnectionService.
@@ -1522,6 +1527,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
client.session.list({ directory: dir, roots: true }, { throwOnError: true }).then(({ data }) => data)
: null,
sessionDirectories: this.sessionDirectories,
worktreeDirectories: this.opts.worktreeDirectories,
workspaceDirectory: this.getWorkspaceDirectory(),
postMessage: (msg: unknown) => this.postMessage(msg),
}
@@ -2421,7 +2427,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
details: getConfigErrorDetails(error),
})
}
private async resolveSession(sessionID?: string, draftID?: string, context?: string) {
private async resolveSession(sessionID?: string, draftID?: string, context?: string, contextDirectory?: string) {
if (!this.client) return undefined
const dir = resolveNewSessionDirectory({
@@ -2429,6 +2435,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
currentSessionID: this.currentSession?.id,
contextSessionID: this.contextSessionID,
agentManagerContext: context,
contextDirectory,
sessionDirectories: this.sessionDirectories,
workspaceDirectory: this.getRootDirectory(),
})
@@ -2538,6 +2545,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
variant?: string,
files?: MessageFile[],
context?: string,
contextDirectory?: string,
): Promise<void> {
if (!this.client) {
this.postMessage({
@@ -2554,7 +2562,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
let resolved: { sid: string; dir: string } | undefined
try {
resolved = await this.resolveSession(sessionID, draftID, context)
resolved = await this.resolveSession(sessionID, draftID, context, contextDirectory)
const parts: Array<TextPartInput | FilePartInput> = []
if (files) {
@@ -2615,6 +2623,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
variant?: string,
files?: MessageFile[],
context?: string,
contextDirectory?: string,
): Promise<void> {
if (!this.client) {
this.postMessage({
@@ -2631,7 +2640,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
let resolved: { sid: string; dir: string } | undefined
try {
resolved = await this.resolveSession(sessionID, draftID, context)
resolved = await this.resolveSession(sessionID, draftID, context, contextDirectory)
if (messageID) {
this.connectionService.recordMessageSessionId(messageID, resolved!.sid)
@@ -181,6 +181,7 @@ export class AgentManagerProvider implements Disposable {
this.attachPanel(
this.host.openPanel({
onBeforeMessage: (msg) => this.onMessage(msg),
worktreeDirectories: () => this.getWorktreeDirectories(),
}),
)
}
@@ -378,7 +379,7 @@ export class AgentManagerProvider implements Disposable {
if (m.type === "agentManager.deleteWorktree") return this.onDeleteWorktree(m.worktreeId)
if (m.type === "agentManager.removeStaleWorktree") return this.onRemoveStaleWorktree(m.worktreeId)
if (m.type === "agentManager.promoteSession") return this.onPromoteSession(m.sessionId)
if (m.type === "agentManager.addSessionToWorktree") return this.onAddSessionToWorktree(m.worktreeId)
if (m.type === "agentManager.addSessionToWorktree") return this.onAddSessionToWorktree(m.worktreeId, m.sessionId)
if (m.type === "agentManager.forkSession") return this.onForkSession(m.sessionId, m.worktreeId, m.messageId)
if (m.type === "agentManager.closeSession") return this.onCloseSession(m.sessionId)
}
@@ -418,6 +419,15 @@ export class AgentManagerProvider implements Disposable {
return null
}
if ((m.type === "sendMessage" || m.type === "sendCommand") && !m.sessionID) {
const ctx = typeof m.agentManagerContext === "string" ? m.agentManagerContext : undefined
const worktree = ctx && ctx !== "local" ? this.getStateManager()?.getWorktree(ctx) : undefined
if (worktree) {
this.activeSessionId = m.draftID
return { ...msg, contextDirectory: worktree.path }
}
}
if ((m.type === "sendMessage" || m.type === "sendCommand") && m.draftID && !m.sessionID) {
this.activeSessionId = m.draftID
return msg
@@ -1049,7 +1059,7 @@ export class AgentManagerProvider implements Disposable {
}
/** Add a new session to an existing worktree. */
private async onAddSessionToWorktree(worktreeId: string): Promise<null> {
private async onAddSessionToWorktree(worktreeId: string, sessionId?: string): Promise<null> {
let client: KiloClient
try {
client = this.connectionService.getClient()
@@ -1068,6 +1078,26 @@ export class AgentManagerProvider implements Disposable {
return null
}
if (sessionId) {
if (state.getSession(sessionId)) state.moveSession(sessionId, worktreeId)
else state.addSession(sessionId, worktreeId)
this.registerWorktreeSession(sessionId, worktree.path)
this.pushState()
this.postToWebview({
type: "agentManager.sessionAdded",
sessionId,
worktreeId,
})
this.host.capture("Agent Manager Session Started", {
source: PLATFORM,
sessionId,
worktreeId,
existing: true,
})
this.log(`Added existing session ${sessionId} to worktree ${worktreeId}`)
return null
}
let session: Session
try {
const { data } = await client.session.create(
@@ -1648,6 +1678,10 @@ export class AgentManagerProvider implements Disposable {
return this.panel?.sessions.getSessionDirectories() ?? new Map()
}
public getWorktreeDirectories(): string[] {
return this.getStateManager()?.getWorktrees().map((wt) => wt.path) ?? []
}
/**
* Continue a sidebar session in a new worktree.
* Captures git state, creates worktree, applies state, forks session.
@@ -101,6 +101,7 @@ function createHarness() {
prBridge: { handleMessage: ReturnType<typeof vi.fn> }
activeSessionId: string | undefined
stateReady: Promise<void> | undefined
contextTarget: ReturnType<typeof vi.fn>
createWorktreeOnDisk: ReturnType<typeof vi.fn>
runSetupScriptForWorktree: ReturnType<typeof vi.fn>
createSessionInWorktree: ReturnType<typeof vi.fn>
@@ -121,6 +122,7 @@ function createHarness() {
manager.prBridge = { handleMessage: vi.fn().mockReturnValue(false) }
manager.activeSessionId = undefined
manager.stateReady = Promise.resolve()
manager.contextTarget = vi.fn()
manager.createWorktreeOnDisk = vi.fn()
manager.runSetupScriptForWorktree = vi.fn().mockResolvedValue(undefined)
manager.createSessionInWorktree = vi.fn()
@@ -202,4 +204,28 @@ describe("AgentManagerProvider worktree creation", () => {
expect(result).toEqual({ type: "requestFileSearch", query: "src", requestId: "r1", sessionID: "session-wt" })
})
it("resolves new sends to the selected worktree directory", async () => {
const manager = createHarness()
const state = {
getWorktree: vi.fn().mockReturnValue({ id: "wt-1", path: "/repo/.kilo/worktrees/wt-1" }),
}
manager.getStateManager.mockReturnValue(state)
manager.contextTarget.mockResolvedValue(undefined)
const result = await manager.onMessage({
type: "sendMessage",
text: "continue",
agentManagerContext: "wt-1",
draftID: "draft-1",
})
expect(result).toEqual({
type: "sendMessage",
text: "continue",
agentManagerContext: "wt-1",
draftID: "draft-1",
contextDirectory: "/repo/.kilo/worktrees/wt-1",
})
})
})
@@ -93,6 +93,7 @@ export interface Host {
*/
openPanel(opts: {
onBeforeMessage: (msg: Record<string, unknown>) => Promise<Record<string, unknown> | null>
worktreeDirectories?: () => string[]
}): PanelContext
/** Get the workspace/project root path. */
@@ -348,6 +348,7 @@ interface OpenLocallyIn {
interface AddSessionToWorktreeIn {
type: "agentManager.addSessionToWorktree"
worktreeId: string
sessionId?: string
}
interface CloseSessionIn {
@@ -606,6 +607,7 @@ interface SendMessageIn {
variant?: string
files?: Array<{ mime: string; url: string; filename?: string; source?: FileSourceIn }>
agentManagerContext?: string
contextDirectory?: string
}
interface SendCommandIn {
@@ -621,6 +623,7 @@ interface SendCommandIn {
variant?: string
files?: Array<{ mime: string; url: string; filename?: string; source?: FileSourceIn }>
agentManagerContext?: string
contextDirectory?: string
}
interface RequestTerminalContextIn {
@@ -35,6 +35,7 @@ export class VscodeHost implements Host {
openPanel(opts: {
onBeforeMessage: (msg: Record<string, unknown>) => Promise<Record<string, unknown> | null>
worktreeDirectories?: () => string[]
}): PanelContext {
const panel = vscode.window.createWebviewPanel(
"kilo-code.new.AgentManagerPanel",
@@ -54,6 +55,7 @@ export class VscodeHost implements Host {
panel: vscode.WebviewPanel,
opts: {
onBeforeMessage: (msg: Record<string, unknown>) => Promise<Record<string, unknown> | null>
worktreeDirectories?: () => string[]
},
): PanelContext {
return this.wirePanel(panel, opts)
@@ -63,6 +65,7 @@ export class VscodeHost implements Host {
panel: vscode.WebviewPanel,
opts: {
onBeforeMessage: (msg: Record<string, unknown>) => Promise<Record<string, unknown> | null>
worktreeDirectories?: () => string[]
},
): PanelContext {
panel.webview.options = {
@@ -86,6 +89,7 @@ export class VscodeHost implements Host {
const provider = new KiloProvider(this.extensionUri, this.connectionService, this.context, {
slimEditMetadata: true,
worktreeDirectories: () => opts.worktreeDirectories?.() ?? [],
})
if (this.diffVirtual) {
provider.setDiffVirtualProvider(this.diffVirtual)
+1
View File
@@ -171,6 +171,7 @@ export function activate(context: vscode.ExtensionContext) {
deserializeWebviewPanel(panel: vscode.WebviewPanel) {
const ctx = agentManagerHost.wrapExistingPanel(panel, {
onBeforeMessage: (msg) => agentManagerProvider.handleMessage(msg),
worktreeDirectories: () => agentManagerProvider.getWorktreeDirectories(),
})
agentManagerProvider.deserializePanel(ctx)
return Promise.resolve()
@@ -222,6 +222,7 @@ export interface SessionRefreshContext {
connectionState: "connecting" | "connected" | "disconnected" | "error"
listSessions: ((dir: string) => Promise<Session[]>) | null
sessionDirectories: Map<string, string>
worktreeDirectories?: () => string[]
workspaceDirectory: string
postMessage(message: unknown): void
}
@@ -245,7 +246,7 @@ export async function loadSessions(ctx: SessionRefreshContext): Promise<string |
const sessions = await list(ctx.workspaceDirectory)
const projectID = sessions[0]?.projectID
const worktreeDirs = new Set(ctx.sessionDirectories.values())
const worktreeDirs = new Set([...(ctx.worktreeDirectories?.() ?? []), ...ctx.sessionDirectories.values()])
const failed = new Set<string>()
const extra = await Promise.all(
[...worktreeDirs].map((dir) =>
@@ -339,6 +340,7 @@ export function resolveNewSessionDirectory(input: {
currentSessionID?: string
contextSessionID?: string
agentManagerContext?: string
contextDirectory?: string
sessionDirectories: Map<string, string>
workspaceDirectory: string
}) {
@@ -350,6 +352,8 @@ export function resolveNewSessionDirectory(input: {
})
}
if (input.contextDirectory) return input.contextDirectory
return resolveContextDirectory({
currentSessionID: input.currentSessionID,
contextSessionID: input.contextSessionID,
@@ -2,4 +2,5 @@ export type KiloProviderOptions = {
projectDirectory?: string | null
slimEditMetadata?: boolean
tabTitle?: (title: string) => void
worktreeDirectories?: () => string[]
}
@@ -93,6 +93,17 @@ describe("resolveNewSessionDirectory", () => {
expect(dir).toBe("/repo/.kilo/worktrees/feature")
})
it("uses explicit Agent Manager worktree context after the active session was cleared", () => {
const dir = resolveNewSessionDirectory({
agentManagerContext: "wt_feature",
contextDirectory: "/repo/.kilo/worktrees/feature",
sessionDirectories: new Map(),
workspaceDirectory: "/repo",
})
expect(dir).toBe("/repo/.kilo/worktrees/feature")
})
it("creates local Agent Manager sessions in the workspace root after a worktree was selected", () => {
const dir = resolveNewSessionDirectory({
contextSessionID: "ses_worktree",
@@ -130,6 +130,7 @@ import { createMarkdownRender } from "./review-preferences"
import { createSidebarCollapse } from "./sidebar-collapse"
import { SidebarToggleButton } from "./SidebarToggleButton"
import { setTabWidths } from "./tab-widths"
import { buildShortcutCategories } from "./shortcuts"
import "./agent-manager.css"
import "./agent-manager-review.css"
const REVIEW_TAB_ID = "review"
@@ -185,76 +186,6 @@ const defaultBindings: Record<string, string> = {
),
}
/** Shortcut category definition for the keyboard shortcuts dialog */
interface ShortcutEntry {
label: string
binding: string
}
interface ShortcutCategory {
title: string
shortcuts: ShortcutEntry[]
}
/** Build the categorized list of keyboard shortcuts from the current bindings */
function buildShortcutCategories(
bindings: Record<string, string>,
t: (key: string, params?: Record<string, string | number>) => string,
): ShortcutCategory[] {
return [
{
title: t("agentManager.shortcuts.category.quickSwitch"),
shortcuts: [
{
label: t("agentManager.shortcuts.jumpToItem"),
binding: (() => {
const first = bindings.jumpTo1 ?? ""
const prefix = first.replace(/\d+$/, "")
return prefix ? `${prefix}1-9` : ""
})(),
},
],
},
{
title: t("agentManager.shortcuts.category.sidebar"),
shortcuts: [
{ label: t("agentManager.shortcuts.previousItem"), binding: bindings.previousSession ?? "" },
{ label: t("agentManager.shortcuts.nextItem"), binding: bindings.nextSession ?? "" },
{ label: t("agentManager.shortcuts.newWorktree"), binding: bindings.newWorktree ?? "" },
{ label: t("agentManager.shortcuts.advancedWorktree"), binding: bindings.advancedWorktree ?? "" },
{ label: t("agentManager.shortcuts.deleteWorktree"), binding: bindings.closeWorktree ?? "" },
{ label: t("agentManager.shortcuts.openWorktree"), binding: bindings.openWorktree ?? "" },
],
},
{
title: t("agentManager.shortcuts.category.tabs"),
shortcuts: [
{ label: t("agentManager.shortcuts.previousTab"), binding: bindings.previousTab ?? "" },
{ label: t("agentManager.shortcuts.nextTab"), binding: bindings.nextTab ?? "" },
{ label: t("agentManager.shortcuts.newTab"), binding: bindings.newTab ?? "" },
{ label: t("agentManager.shortcuts.closeTab"), binding: bindings.closeTab ?? "" },
],
},
{
title: t("agentManager.shortcuts.category.terminal"),
shortcuts: [
{ label: t("agentManager.shortcuts.toggleTerminal"), binding: bindings.showTerminal ?? "" },
{ label: t("agentManager.shortcuts.runScript"), binding: bindings.runScript ?? "" },
{ label: t("agentManager.shortcuts.toggleDiff"), binding: bindings.toggleDiff ?? "" },
],
},
{
title: t("agentManager.shortcuts.category.global"),
shortcuts: [
{ label: t("agentManager.shortcuts.openAgentManager"), binding: bindings.agentManagerOpen ?? "" },
{ label: t("agentManager.shortcuts.cycleAgentMode"), binding: bindings.cycleAgentMode ?? "" },
{ label: t("agentManager.shortcuts.cyclePreviousAgentMode"), binding: bindings.cyclePreviousAgentMode ?? "" },
{ label: t("agentManager.shortcuts.showShortcuts"), binding: bindings.showShortcuts ?? "" },
].filter((s) => s.binding),
},
].filter((c) => c.shortcuts.length > 0)
}
import { parseBindingTokens } from "./keybind-tokens"
const AgentManagerContent: Component = () => {
@@ -1003,6 +934,27 @@ const AgentManagerContent: Component = () => {
setReviewActive(remembered === REVIEW_TAB_ID && reviewOpenByContext()[worktreeId] === true)
}
const addSessionToCurrentWorktree = (sid: string) => {
const sel = selection()
if (!sel || sel === LOCAL) return false
const current = managedSessions().find((entry) => entry.id === sid)
if (current?.worktreeId) return focusManagedSession(current.worktreeId, sid)
saveTabMemory()
setHistory(false)
setReviewActive(false)
appendToTabOrder(sel, sid)
evictLocal(sid)
vscode.postMessage({ type: "agentManager.addSessionToWorktree", worktreeId: sel, sessionId: sid })
return true
}
const focusManagedSession = (worktreeId: string, sid: string) => {
selectWorktree(worktreeId)
setHistory(false)
session.selectSession(sid)
return true
}
const cycleAgent = (direction: 1 | -1) => {
const available = session.agents().filter((a) => a.mode !== "subagent" && !a.hidden)
if (available.length <= 1) return
@@ -1220,6 +1172,7 @@ const AgentManagerContent: Component = () => {
saveTabMemory()
appendToTabOrder(ev.worktreeId, ev.sessionId)
setSelection(ev.worktreeId)
evictLocal(ev.sessionId)
drafts.apply(ev.worktreeId, ev.sessionId)
session.selectSession(ev.sessionId)
}
@@ -2987,6 +2940,7 @@ const AgentManagerContent: Component = () => {
<Show when={history()}>
<HistoryView
onSelectSession={(id) => {
if (addSessionToCurrentWorktree(id)) return
setHistory(false)
if (localSessionIDs().includes(id)) {
saveTabMemory()
@@ -3024,6 +2978,7 @@ const AgentManagerContent: Component = () => {
<div class="am-chat-wrapper">
<ChatView
onSelectSession={(id) => {
if (addSessionToCurrentWorktree(id)) return
if (localSessionIDs().includes(id)) {
session.selectSession(id)
if (selection() === null) setSelection(LOCAL)
@@ -0,0 +1,67 @@
export interface ShortcutEntry {
label: string
binding: string
}
export interface ShortcutCategory {
title: string
shortcuts: ShortcutEntry[]
}
export function buildShortcutCategories(
bindings: Record<string, string>,
t: (key: string, params?: Record<string, string | number>) => string,
): ShortcutCategory[] {
return [
{
title: t("agentManager.shortcuts.category.quickSwitch"),
shortcuts: [
{
label: t("agentManager.shortcuts.jumpToItem"),
binding: (() => {
const first = bindings.jumpTo1 ?? ""
const prefix = first.replace(/\d+$/, "")
return prefix ? `${prefix}1-9` : ""
})(),
},
],
},
{
title: t("agentManager.shortcuts.category.sidebar"),
shortcuts: [
{ label: t("agentManager.shortcuts.previousItem"), binding: bindings.previousSession ?? "" },
{ label: t("agentManager.shortcuts.nextItem"), binding: bindings.nextSession ?? "" },
{ label: t("agentManager.shortcuts.newWorktree"), binding: bindings.newWorktree ?? "" },
{ label: t("agentManager.shortcuts.advancedWorktree"), binding: bindings.advancedWorktree ?? "" },
{ label: t("agentManager.shortcuts.deleteWorktree"), binding: bindings.closeWorktree ?? "" },
{ label: t("agentManager.shortcuts.openWorktree"), binding: bindings.openWorktree ?? "" },
],
},
{
title: t("agentManager.shortcuts.category.tabs"),
shortcuts: [
{ label: t("agentManager.shortcuts.previousTab"), binding: bindings.previousTab ?? "" },
{ label: t("agentManager.shortcuts.nextTab"), binding: bindings.nextTab ?? "" },
{ label: t("agentManager.shortcuts.newTab"), binding: bindings.newTab ?? "" },
{ label: t("agentManager.shortcuts.closeTab"), binding: bindings.closeTab ?? "" },
],
},
{
title: t("agentManager.shortcuts.category.terminal"),
shortcuts: [
{ label: t("agentManager.shortcuts.toggleTerminal"), binding: bindings.showTerminal ?? "" },
{ label: t("agentManager.shortcuts.runScript"), binding: bindings.runScript ?? "" },
{ label: t("agentManager.shortcuts.toggleDiff"), binding: bindings.toggleDiff ?? "" },
],
},
{
title: t("agentManager.shortcuts.category.global"),
shortcuts: [
{ label: t("agentManager.shortcuts.openAgentManager"), binding: bindings.agentManagerOpen ?? "" },
{ label: t("agentManager.shortcuts.cycleAgentMode"), binding: bindings.cycleAgentMode ?? "" },
{ label: t("agentManager.shortcuts.cyclePreviousAgentMode"), binding: bindings.cyclePreviousAgentMode ?? "" },
{ label: t("agentManager.shortcuts.showShortcuts"), binding: bindings.showShortcuts ?? "" },
].filter((s) => s.binding),
},
].filter((c) => c.shortcuts.length > 0)
}
@@ -29,6 +29,7 @@ export interface SendMessageRequest {
variant?: string
files?: FileAttachment[]
agentManagerContext?: string
contextDirectory?: string
}
export interface AbortRequest {
@@ -240,6 +241,7 @@ export interface SendCommandRequest {
variant?: string
files?: FileAttachment[]
agentManagerContext?: string
contextDirectory?: string
}
export interface RemoveSkillMessage {
@@ -479,6 +481,7 @@ export interface OpenLocallyRequest {
export interface AddSessionToWorktreeRequest {
type: "agentManager.addSessionToWorktree"
worktreeId: string
sessionId?: string
}
// Fork an existing session (copies conversation history)