mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-19 10:02:04 +08:00
feat(agent-manager): add fixed local session tab with branch display and persistence (#446)
* tmp * tmp * tmp * feat(agent-manager): add fixed local session tab with branch display and persistence Add a dedicated local session item at the top of the agent manager sidebar that always runs in the main repo (never a worktree). The local session persists across panel reopens via vscode webview state, shows the current git branch name (refreshed on click), and supports keyboard navigation. Extract navigation logic into a pure testable function with 16 unit tests. * fix(agent-manager): invalidate stale persisted localSessionID on recovery The local session ID was persisted to webview state but never checked against the actual sessions list on recovery. If the session was deleted or expired between VS Code reloads, the UI would get stuck selecting a ghost session with no way to recover.
This commit is contained in:
@@ -68,6 +68,7 @@ export class AgentManagerProvider implements vscode.Disposable {
|
||||
})
|
||||
|
||||
void this.recoverWorktrees()
|
||||
void this.sendRepoInfo()
|
||||
|
||||
this.panel.onDidDispose(() => {
|
||||
this.log("Panel disposed")
|
||||
@@ -86,6 +87,10 @@ export class AgentManagerProvider implements vscode.Disposable {
|
||||
|
||||
// Custom agent-manager messages -- consumed here, never reach KiloProvider
|
||||
if (type === "agentManager.createWorktreeSession") return this.onCreateWorktreeSession(msg)
|
||||
if (type === "agentManager.requestRepoInfo") {
|
||||
void this.sendRepoInfo()
|
||||
return null
|
||||
}
|
||||
|
||||
// After clearSession, re-register worktree sessions so SSE events keep flowing
|
||||
if (type === "clearSession") {
|
||||
@@ -222,6 +227,21 @@ export class AgentManagerProvider implements vscode.Disposable {
|
||||
return null
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Repo info
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
private async sendRepoInfo(): Promise<void> {
|
||||
const mgr = this.getWorktreeManager()
|
||||
if (!mgr) return
|
||||
try {
|
||||
const branch = await mgr.currentBranch()
|
||||
this.postToWebview({ type: "agentManager.repoInfo", branch })
|
||||
} catch (error) {
|
||||
this.log(`Failed to get current branch: ${error}`)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Worktree management
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -270,7 +270,7 @@ export class WorktreeManager {
|
||||
}
|
||||
}
|
||||
|
||||
private async currentBranch(): Promise<string> {
|
||||
async currentBranch(): Promise<string> {
|
||||
return (await this.git.revparse(["--abbrev-ref", "HEAD"])).trim()
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
import { describe, it, expect } from "bun:test"
|
||||
import { resolveNavigation, validateLocalSession } from "../../webview-ui/agent-manager/navigate"
|
||||
|
||||
const ids = ["a", "b", "c", "d"]
|
||||
|
||||
describe("resolveNavigation", () => {
|
||||
describe("from local (current = undefined)", () => {
|
||||
it("down → selects first session", () => {
|
||||
expect(resolveNavigation("down", undefined, ids)).toEqual({ action: "select", id: "a" })
|
||||
})
|
||||
|
||||
it("up → none (already at top)", () => {
|
||||
expect(resolveNavigation("up", undefined, ids)).toEqual({ action: "none" })
|
||||
})
|
||||
|
||||
it("down with empty list → none", () => {
|
||||
expect(resolveNavigation("down", undefined, [])).toEqual({ action: "none" })
|
||||
})
|
||||
|
||||
it("up with empty list → none", () => {
|
||||
expect(resolveNavigation("up", undefined, [])).toEqual({ action: "none" })
|
||||
})
|
||||
})
|
||||
|
||||
describe("from first session", () => {
|
||||
it("up → local", () => {
|
||||
expect(resolveNavigation("up", "a", ids)).toEqual({ action: "local" })
|
||||
})
|
||||
|
||||
it("down → selects second session", () => {
|
||||
expect(resolveNavigation("down", "a", ids)).toEqual({ action: "select", id: "b" })
|
||||
})
|
||||
})
|
||||
|
||||
describe("from middle session", () => {
|
||||
it("up → selects previous session", () => {
|
||||
expect(resolveNavigation("up", "b", ids)).toEqual({ action: "select", id: "a" })
|
||||
})
|
||||
|
||||
it("down → selects next session", () => {
|
||||
expect(resolveNavigation("down", "b", ids)).toEqual({ action: "select", id: "c" })
|
||||
})
|
||||
})
|
||||
|
||||
describe("from last session", () => {
|
||||
it("down → none (already at bottom)", () => {
|
||||
expect(resolveNavigation("down", "d", ids)).toEqual({ action: "none" })
|
||||
})
|
||||
|
||||
it("up → selects previous session", () => {
|
||||
expect(resolveNavigation("up", "d", ids)).toEqual({ action: "select", id: "c" })
|
||||
})
|
||||
})
|
||||
|
||||
describe("current session not in list", () => {
|
||||
it("down → none", () => {
|
||||
expect(resolveNavigation("down", "unknown", ids)).toEqual({ action: "none" })
|
||||
})
|
||||
|
||||
it("up → none", () => {
|
||||
expect(resolveNavigation("up", "unknown", ids)).toEqual({ action: "none" })
|
||||
})
|
||||
})
|
||||
|
||||
describe("single session list", () => {
|
||||
it("down from local → selects only session", () => {
|
||||
expect(resolveNavigation("down", undefined, ["x"])).toEqual({ action: "select", id: "x" })
|
||||
})
|
||||
|
||||
it("up from only session → local", () => {
|
||||
expect(resolveNavigation("up", "x", ["x"])).toEqual({ action: "local" })
|
||||
})
|
||||
|
||||
it("down from only session → none", () => {
|
||||
expect(resolveNavigation("down", "x", ["x"])).toEqual({ action: "none" })
|
||||
})
|
||||
})
|
||||
|
||||
describe("sequential walk-through", () => {
|
||||
it("navigating down through entire list then back up returns to local", () => {
|
||||
const sessions = ["s1", "s2", "s3"]
|
||||
const trail: string[] = []
|
||||
|
||||
// Start at local, navigate down through all sessions
|
||||
let current: string | undefined = undefined
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const result = resolveNavigation("down", current, sessions)
|
||||
if (result.action === "select") {
|
||||
current = result.id
|
||||
trail.push(current)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
expect(trail).toEqual(["s1", "s2", "s3"])
|
||||
|
||||
// Navigate back up through all sessions to local
|
||||
const upTrail: (string | "local")[] = []
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const result = resolveNavigation("up", current, sessions)
|
||||
if (result.action === "select") {
|
||||
current = result.id
|
||||
upTrail.push(current)
|
||||
} else if (result.action === "local") {
|
||||
current = undefined
|
||||
upTrail.push("local")
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
expect(upTrail).toEqual(["s2", "s1", "local"])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("validateLocalSession", () => {
|
||||
it("returns the ID when it exists in the sessions list", () => {
|
||||
expect(validateLocalSession("abc", ["abc", "def"])).toBe("abc")
|
||||
})
|
||||
|
||||
it("returns undefined when the ID is not in the sessions list (stale/deleted)", () => {
|
||||
expect(validateLocalSession("gone", ["abc", "def"])).toBeUndefined()
|
||||
})
|
||||
|
||||
it("returns undefined when sessions list is empty", () => {
|
||||
expect(validateLocalSession("abc", [])).toBeUndefined()
|
||||
})
|
||||
|
||||
it("returns undefined when persisted ID is undefined", () => {
|
||||
expect(validateLocalSession(undefined, ["abc"])).toBeUndefined()
|
||||
})
|
||||
|
||||
it("returns undefined when both are empty/undefined", () => {
|
||||
expect(validateLocalSession(undefined, [])).toBeUndefined()
|
||||
})
|
||||
})
|
||||
@@ -4,6 +4,7 @@ import { Component, For, Show, createSignal, createEffect, createMemo, onMount,
|
||||
import type {
|
||||
ExtensionMessage,
|
||||
AgentManagerSessionMetaMessage,
|
||||
AgentManagerRepoInfoMessage,
|
||||
AgentManagerWorktreeSetupMessage,
|
||||
} from "../src/types/messages"
|
||||
import { ThemeProvider } from "@kilocode/kilo-ui/theme"
|
||||
@@ -26,6 +27,7 @@ import { WorktreeModeProvider, useWorktreeMode, type SessionMode } from "../src/
|
||||
import { ChatView } from "../src/components/chat"
|
||||
import { LanguageBridge, DataBridge } from "../src/App"
|
||||
import { formatRelativeDate } from "../src/utils/date"
|
||||
import { resolveNavigation, validateLocalSession } from "./navigate"
|
||||
import "./agent-manager.css"
|
||||
|
||||
interface WorktreeMeta {
|
||||
@@ -49,19 +51,52 @@ const AgentManagerContent: Component = () => {
|
||||
|
||||
const [sessionMeta, setSessionMeta] = createSignal<Record<string, WorktreeMeta>>({})
|
||||
const [setup, setSetup] = createSignal<SetupState>({ active: false, message: "" })
|
||||
const [repoBranch, setRepoBranch] = createSignal<string | undefined>()
|
||||
|
||||
// Recover persisted local session ID from webview state
|
||||
const persisted = vscode.getState<{ localSessionID?: string }>()
|
||||
const [localSessionID, setLocalSessionID] = createSignal<string | undefined>(persisted?.localSessionID)
|
||||
|
||||
// Whether the user is viewing the local workspace
|
||||
const [onLocal, setOnLocal] = createSignal(true)
|
||||
|
||||
const isLocal = () => {
|
||||
const lid = localSessionID()
|
||||
const current = session.currentSessionID()
|
||||
if (onLocal() && !current) return true
|
||||
if (lid && current === lid) return true
|
||||
return false
|
||||
}
|
||||
|
||||
// Sessions list excludes the local session
|
||||
const sorted = createMemo(() =>
|
||||
[...session.sessions()].sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()),
|
||||
[...session.sessions()]
|
||||
.filter((s) => s.id !== localSessionID())
|
||||
.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()),
|
||||
)
|
||||
|
||||
const selectLocal = () => {
|
||||
setOnLocal(true)
|
||||
// Request fresh branch info — cheap git rev-parse, ensures branch name
|
||||
// is current even if the user switched branches in an external terminal
|
||||
vscode.postMessage({ type: "agentManager.requestRepoInfo" })
|
||||
const lid = localSessionID()
|
||||
if (lid) {
|
||||
session.selectSession(lid)
|
||||
} else {
|
||||
session.clearCurrentSession()
|
||||
}
|
||||
}
|
||||
|
||||
const navigate = (direction: "up" | "down") => {
|
||||
const list = sorted()
|
||||
if (list.length === 0) return
|
||||
const current = session.currentSessionID()
|
||||
const idx = current ? list.findIndex((s) => s.id === current) : -1
|
||||
const next = direction === "up" ? idx - 1 : idx + 1
|
||||
if (next < 0 || next >= list.length) return
|
||||
session.selectSession(list[next]!.id)
|
||||
const ids = sorted().map((s) => s.id)
|
||||
const current = isLocal() ? undefined : session.currentSessionID()
|
||||
const result = resolveNavigation(direction, current, ids)
|
||||
if (result.action === "local") selectLocal()
|
||||
else if (result.action === "select") {
|
||||
setOnLocal(false)
|
||||
session.selectSession(result.id)
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
@@ -74,6 +109,18 @@ const AgentManagerContent: Component = () => {
|
||||
}
|
||||
window.addEventListener("message", handler)
|
||||
|
||||
// When a session is created while the user is on local with no local
|
||||
// session yet, adopt it. The session context sets currentSessionID before
|
||||
// this listener fires, so we also re-assert onLocal to keep the sidebar
|
||||
// highlight on the local item rather than the SESSIONS list.
|
||||
const unsubCreate = vscode.onMessage((msg) => {
|
||||
if (msg.type === "sessionCreated" && onLocal() && !localSessionID()) {
|
||||
const created = msg as { type: string; session: { id: string } }
|
||||
setLocalSessionID(created.session.id)
|
||||
setOnLocal(true)
|
||||
}
|
||||
})
|
||||
|
||||
// Worktree metadata and setup progress messages
|
||||
const unsub = vscode.onMessage((msg) => {
|
||||
if (msg.type === "agentManager.sessionMeta") {
|
||||
@@ -89,6 +136,11 @@ const AgentManagerContent: Component = () => {
|
||||
}))
|
||||
}
|
||||
|
||||
if (msg.type === "agentManager.repoInfo") {
|
||||
const info = msg as AgentManagerRepoInfoMessage
|
||||
setRepoBranch(info.branch)
|
||||
}
|
||||
|
||||
if (msg.type === "agentManager.worktreeSetup") {
|
||||
const ev = msg as AgentManagerWorktreeSetupMessage
|
||||
if (ev.status === "ready" || ev.status === "error") {
|
||||
@@ -103,24 +155,75 @@ const AgentManagerContent: Component = () => {
|
||||
|
||||
onCleanup(() => {
|
||||
window.removeEventListener("message", handler)
|
||||
unsubCreate()
|
||||
unsub()
|
||||
})
|
||||
})
|
||||
|
||||
// Reset mode when session is cleared
|
||||
// Persist local session ID to webview state for recovery
|
||||
createEffect(() => {
|
||||
const lid = localSessionID()
|
||||
vscode.setState({ localSessionID: lid })
|
||||
})
|
||||
|
||||
// Invalidate persisted local session ID if it no longer exists (e.g. server
|
||||
// restarted, session expired). Without this the UI would get stuck selecting a
|
||||
// ghost session on recovery.
|
||||
createEffect(() => {
|
||||
const all = session.sessions()
|
||||
if (all.length === 0) return // sessions not loaded yet
|
||||
const lid = localSessionID()
|
||||
if (!lid) return
|
||||
const valid = validateLocalSession(
|
||||
lid,
|
||||
all.map((s) => s.id),
|
||||
)
|
||||
if (!valid) {
|
||||
setLocalSessionID(undefined)
|
||||
session.clearCurrentSession()
|
||||
}
|
||||
})
|
||||
|
||||
// Reset worktree mode when no session is selected
|
||||
createEffect(() => {
|
||||
if (!session.currentSessionID()) worktreeMode.setMode("local")
|
||||
})
|
||||
|
||||
// If we have a persisted local session, select it on mount
|
||||
onMount(() => {
|
||||
const lid = localSessionID()
|
||||
if (lid) session.selectSession(lid)
|
||||
})
|
||||
|
||||
const getMeta = (sessionId: string): WorktreeMeta | undefined => sessionMeta()[sessionId]
|
||||
|
||||
return (
|
||||
<div class="am-layout">
|
||||
<div class="am-sidebar">
|
||||
<div class="am-sidebar-header">AGENT MANAGER</div>
|
||||
<Button variant="primary" size="large" onClick={() => session.clearCurrentSession()}>
|
||||
<Button
|
||||
variant="primary"
|
||||
size="large"
|
||||
onClick={() => {
|
||||
setOnLocal(false)
|
||||
session.clearCurrentSession()
|
||||
}}
|
||||
>
|
||||
+ New Agent
|
||||
</Button>
|
||||
<button class={`am-local-item ${isLocal() ? "am-local-item-active" : ""}`} onClick={() => selectLocal()}>
|
||||
<svg class="am-local-icon" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="2.5" y="3.5" width="15" height="10" rx="1" stroke="currentColor" />
|
||||
<path d="M6 16.5H14" stroke="currentColor" stroke-linecap="square" />
|
||||
<path d="M10 13.5V16.5" stroke="currentColor" />
|
||||
</svg>
|
||||
<div class="am-local-text">
|
||||
<span class="am-local-label">local</span>
|
||||
<Show when={repoBranch()}>
|
||||
<span class="am-local-branch">{repoBranch()}</span>
|
||||
</Show>
|
||||
</div>
|
||||
</button>
|
||||
<div class="am-sessions-header">SESSIONS</div>
|
||||
<div class="am-list">
|
||||
<For each={sorted()}>
|
||||
@@ -128,8 +231,11 @@ const AgentManagerContent: Component = () => {
|
||||
const meta = () => getMeta(s.id)
|
||||
return (
|
||||
<button
|
||||
class={`am-item ${s.id === session.currentSessionID() ? "am-item-active" : ""}`}
|
||||
onClick={() => session.selectSession(s.id)}
|
||||
class={`am-item ${!isLocal() && s.id === session.currentSessionID() ? "am-item-active" : ""}`}
|
||||
onClick={() => {
|
||||
setOnLocal(false)
|
||||
session.selectSession(s.id)
|
||||
}}
|
||||
>
|
||||
<span class="am-item-title">
|
||||
{s.title || "Untitled"}
|
||||
@@ -165,7 +271,12 @@ const AgentManagerContent: Component = () => {
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
<ChatView onSelectSession={(id) => session.selectSession(id)} />
|
||||
<ChatView
|
||||
onSelectSession={(id) => {
|
||||
setOnLocal(false)
|
||||
session.selectSession(id)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -32,8 +32,65 @@
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
color: var(--text-weak, var(--vscode-descriptionForeground, #888));
|
||||
padding: 4px 8px 0;
|
||||
color: var(--text-weaker, var(--vscode-descriptionForeground, #666));
|
||||
padding: 8px 10px 2px;
|
||||
}
|
||||
|
||||
/* Fixed local workspace item — styled like a session row */
|
||||
|
||||
.am-local-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 10px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
border: none;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
color: var(--text-base, var(--vscode-foreground));
|
||||
font-size: 13px;
|
||||
font-family: inherit;
|
||||
flex-shrink: 0;
|
||||
transition: background-color 0.15s ease;
|
||||
}
|
||||
|
||||
.am-local-item:hover {
|
||||
background: var(--surface-inset-base-hover, rgba(255, 255, 255, 0.05));
|
||||
}
|
||||
|
||||
.am-local-item-active {
|
||||
background: var(--surface-base-active, rgba(255, 255, 255, 0.08));
|
||||
}
|
||||
|
||||
.am-local-icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
flex-shrink: 0;
|
||||
color: var(--text-weaker, var(--vscode-descriptionForeground, #666));
|
||||
}
|
||||
|
||||
.am-local-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.am-local-label {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.am-local-branch {
|
||||
font-size: 11px;
|
||||
color: var(--text-weaker, var(--vscode-descriptionForeground, #777));
|
||||
line-height: 1.2;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Session list */
|
||||
@@ -52,8 +109,8 @@
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
padding: 6px 8px;
|
||||
border-radius: 4px;
|
||||
padding: 6px 10px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
border: none;
|
||||
@@ -62,15 +119,16 @@
|
||||
color: var(--text-base, var(--vscode-foreground));
|
||||
font-size: 13px;
|
||||
font-family: inherit;
|
||||
transition: background-color 0.15s ease;
|
||||
}
|
||||
|
||||
.am-item:hover {
|
||||
background: var(--surface-inset-base-hover, var(--vscode-list-hoverBackground, #2a2d2e));
|
||||
background: var(--surface-inset-base-hover, rgba(255, 255, 255, 0.05));
|
||||
}
|
||||
|
||||
.am-item-active {
|
||||
background: var(--surface-interactive-base, var(--vscode-list-activeSelectionBackground, #04395e));
|
||||
color: var(--text-on-interactive-base, var(--vscode-list-activeSelectionForeground, #fff));
|
||||
background: var(--surface-base-active, rgba(255, 255, 255, 0.08));
|
||||
color: var(--text-base, var(--vscode-foreground));
|
||||
}
|
||||
|
||||
.am-item-title {
|
||||
@@ -93,7 +151,7 @@
|
||||
}
|
||||
|
||||
.am-item-active .am-item-time {
|
||||
color: var(--text-on-interactive-weak, rgba(255, 255, 255, 0.7));
|
||||
color: var(--text-weaker, var(--vscode-descriptionForeground, #888));
|
||||
}
|
||||
|
||||
/* Worktree badge */
|
||||
@@ -116,8 +174,8 @@
|
||||
}
|
||||
|
||||
.am-item-active .am-worktree-badge {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
color: var(--text-on-interactive-weak, rgba(255, 255, 255, 0.85));
|
||||
background: var(--surface-inset-base, var(--vscode-badge-background, #4d4d4d));
|
||||
color: var(--text-weak, var(--vscode-badge-foreground, #ccc));
|
||||
}
|
||||
|
||||
/* Detail pane */
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Pure navigation logic for the agent manager sidebar.
|
||||
*
|
||||
* The sidebar has a fixed "local" item at index -1, followed by
|
||||
* session items at indices 0..N-1 (sorted newest-first).
|
||||
*
|
||||
* Returns the action to take: select a session by ID, go to local, or do nothing.
|
||||
*/
|
||||
|
||||
export type NavResult = { action: "select"; id: string } | { action: "local" } | { action: "none" }
|
||||
|
||||
export function resolveNavigation(direction: "up" | "down", current: string | undefined, ids: string[]): NavResult {
|
||||
// Determine current position: -1 = local, 0..N-1 = session index
|
||||
if (!current) {
|
||||
// On local
|
||||
if (direction === "up") return { action: "none" }
|
||||
if (ids.length === 0) return { action: "none" }
|
||||
return { action: "select", id: ids[0]! }
|
||||
}
|
||||
|
||||
const idx = ids.indexOf(current)
|
||||
// Current session not found in list — don't navigate
|
||||
if (idx === -1) return { action: "none" }
|
||||
|
||||
const next = direction === "up" ? idx - 1 : idx + 1
|
||||
|
||||
// Moving up past the first session → go to local
|
||||
if (next === -1) return { action: "local" }
|
||||
|
||||
// At the bottom boundary
|
||||
if (next >= ids.length) return { action: "none" }
|
||||
|
||||
return { action: "select", id: ids[next]! }
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a persisted local session ID against the current sessions list.
|
||||
* Returns the ID if it still exists, undefined otherwise.
|
||||
*/
|
||||
export function validateLocalSession(persisted: string | undefined, ids: string[]): string | undefined {
|
||||
if (!persisted) return undefined
|
||||
if (ids.indexOf(persisted) === -1) return undefined
|
||||
return persisted
|
||||
}
|
||||
@@ -511,6 +511,12 @@ export interface AgentManagerSessionMetaMessage {
|
||||
parentBranch?: string
|
||||
}
|
||||
|
||||
// Agent Manager repo info (current branch of the main workspace)
|
||||
export interface AgentManagerRepoInfoMessage {
|
||||
type: "agentManager.repoInfo"
|
||||
branch: string
|
||||
}
|
||||
|
||||
// Agent Manager worktree setup progress
|
||||
export interface AgentManagerWorktreeSetupMessage {
|
||||
type: "agentManager.worktreeSetup"
|
||||
@@ -554,6 +560,7 @@ export type ExtensionMessage =
|
||||
| ConfigUpdatedMessage
|
||||
| NotificationSettingsLoadedMessage
|
||||
| AgentManagerSessionMetaMessage
|
||||
| AgentManagerRepoInfoMessage
|
||||
| AgentManagerWorktreeSetupMessage
|
||||
|
||||
// ============================================
|
||||
@@ -750,6 +757,10 @@ export interface TelemetryRequest {
|
||||
properties?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface RequestRepoInfoMessage {
|
||||
type: "agentManager.requestRepoInfo"
|
||||
}
|
||||
|
||||
export type WebviewMessage =
|
||||
| SendMessageRequest
|
||||
| AbortRequest
|
||||
@@ -787,6 +798,7 @@ export type WebviewMessage =
|
||||
| SyncSessionRequest
|
||||
| CreateWorktreeSessionRequest
|
||||
| TelemetryRequest
|
||||
| RequestRepoInfoMessage
|
||||
|
||||
// ============================================
|
||||
// VS Code API type
|
||||
|
||||
@@ -76,13 +76,15 @@
|
||||
"@gitlab/opencode-gitlab-auth": "1.3.2",
|
||||
"@hono/standard-validator": "0.1.5",
|
||||
"@hono/zod-validator": "catalog:",
|
||||
"@kilocode/kilo-gateway": "workspace:*",
|
||||
"@kilocode/kilo-telemetry": "workspace:*",
|
||||
"@kilocode/plugin": "workspace:*",
|
||||
"@kilocode/sdk": "workspace:*",
|
||||
"@modelcontextprotocol/sdk": "1.25.2",
|
||||
"@octokit/graphql": "9.0.2",
|
||||
"@octokit/rest": "catalog:",
|
||||
"@openauthjs/openauth": "catalog:",
|
||||
"@kilocode/plugin": "workspace:*",
|
||||
"@opencode-ai/script": "workspace:*",
|
||||
"@kilocode/sdk": "workspace:*",
|
||||
"@opencode-ai/util": "workspace:*",
|
||||
"@openrouter/ai-sdk-provider": "1.5.4",
|
||||
"@opentui/core": "0.1.79",
|
||||
@@ -122,9 +124,7 @@
|
||||
"xdg-basedir": "5.1.0",
|
||||
"yargs": "18.0.0",
|
||||
"zod": "catalog:",
|
||||
"zod-to-json-schema": "3.24.5",
|
||||
"@kilocode/kilo-gateway": "workspace:*",
|
||||
"@kilocode/kilo-telemetry": "workspace:*"
|
||||
"zod-to-json-schema": "3.24.5"
|
||||
},
|
||||
"peerDependencies": {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user