mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-30 17:14:40 +08:00
fix(vscode): keep legacy session detection history-driven and stat-only
Split scanTaskStore into a history mode (legacy) that trusts taskHistory and only stats each conversation file, and a discover mode (Roo) that enumerates and parses task directories. This restores the prior legacy behavior of not surfacing on-disk tasks missing from history and avoids parsing every conversation file during detection. Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
This commit is contained in:
@@ -121,7 +121,7 @@ export async function detectLegacyData(context: vscode.ExtensionContext): Promis
|
||||
async function readSessionCatalog(context: vscode.ExtensionContext) {
|
||||
const items = context.globalState.get<LegacyHistoryItem[]>("taskHistory", [])
|
||||
const dir = vscode.Uri.joinPath(context.globalStorageUri, "tasks").fsPath
|
||||
return (await scanTaskStore(dir, items)).catalog
|
||||
return (await scanTaskStore(dir, items, { mode: "history" })).catalog
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -40,11 +40,54 @@ export function resolveSession(catalog: SessionCatalog, id: string) {
|
||||
return catalog.get(id)?.source
|
||||
}
|
||||
|
||||
export type ScanMode = "history" | "discover"
|
||||
|
||||
export interface ScanOptions {
|
||||
namespace?: string
|
||||
/**
|
||||
* "history" trusts the provided history items and only checks that each task's
|
||||
* conversation file still exists (cheap stat, used by legacy migration).
|
||||
* "discover" enumerates every task directory on disk and parses conversation
|
||||
* files to recover titles (used when no history is available, e.g. Roo import).
|
||||
*/
|
||||
mode?: ScanMode
|
||||
}
|
||||
|
||||
export async function scanTaskStore(
|
||||
dir: string,
|
||||
items: LegacyHistoryItem[] = [],
|
||||
namespace?: string,
|
||||
options: ScanOptions = {},
|
||||
): Promise<TaskScan> {
|
||||
const mode = options.mode ?? (items.length ? "history" : "discover")
|
||||
return mode === "history"
|
||||
? scanFromHistory(dir, items, options.namespace)
|
||||
: scanFromDisk(dir, items, options.namespace)
|
||||
}
|
||||
|
||||
/** Builds a catalog from known history items, only confirming each conversation file exists. */
|
||||
async function scanFromHistory(dir: string, items: LegacyHistoryItem[], namespace?: string): Promise<TaskScan> {
|
||||
const catalog: SessionCatalog = new Map()
|
||||
|
||||
for (const item of items) {
|
||||
if (catalog.has(item.id)) continue
|
||||
if (!(await exists(path.join(dir, item.id, API_FILE)))) continue
|
||||
catalog.set(item.id, {
|
||||
id: item.id,
|
||||
session: {
|
||||
id: item.id,
|
||||
title: item.task?.trim() || fallbackTitle(item.id),
|
||||
directory: item.workspace?.trim() || "",
|
||||
time: item.ts ?? timestamp(item.id),
|
||||
},
|
||||
source: { id: item.id, dir, item, namespace },
|
||||
})
|
||||
}
|
||||
|
||||
return { catalog, diagnostics: [] }
|
||||
}
|
||||
|
||||
/** Enumerates every task directory on disk and parses conversation files to recover titles. */
|
||||
async function scanFromDisk(dir: string, items: LegacyHistoryItem[], namespace?: string): Promise<TaskScan> {
|
||||
const entries = await vscode.workspace.fs.readDirectory(vscode.Uri.file(dir)).then(
|
||||
(value) => value,
|
||||
() => [] as [string, vscode.FileType][],
|
||||
|
||||
@@ -24,7 +24,7 @@ export async function detectRooCodeSessions(context: vscode.ExtensionContext): P
|
||||
|
||||
for (const root of ROOTS) {
|
||||
const dir = path.join(parent, root, "tasks")
|
||||
const scan = await scanTaskStore(dir, [], "roo")
|
||||
const scan = await scanTaskStore(dir, [], { namespace: "roo", mode: "discover" })
|
||||
diagnostics.push(...scan.diagnostics)
|
||||
for (const [id, entry] of [...scan.catalog].sort(([a], [b]) => a.localeCompare(b))) {
|
||||
if (!catalog.has(id)) catalog.set(id, entry)
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { afterEach, describe, expect, it } from "bun:test"
|
||||
import * as vscode from "vscode"
|
||||
import { listSessions, resolveSession, scanTaskStore } from "../../../src/legacy-migration/task-store"
|
||||
|
||||
type Fs = typeof vscode.workspace.fs
|
||||
const fs = vscode.workspace.fs as Fs
|
||||
const original = { readDirectory: fs.readDirectory, readFile: fs.readFile, stat: fs.stat }
|
||||
const dir = "/storage/kilocode.kilo-code/tasks"
|
||||
const api = (id: string) => `${dir}/${id}/api_conversation_history.json`
|
||||
|
||||
describe("task store history scan", () => {
|
||||
afterEach(() => {
|
||||
fs.readDirectory = original.readDirectory
|
||||
fs.readFile = original.readFile
|
||||
fs.stat = original.stat
|
||||
})
|
||||
|
||||
it("includes only history items whose conversation file exists, without scanning or parsing disk", async () => {
|
||||
let listed = false
|
||||
let read = false
|
||||
fs.readDirectory = async () => {
|
||||
listed = true
|
||||
return []
|
||||
}
|
||||
fs.readFile = async () => {
|
||||
read = true
|
||||
throw new Error("history scan must not read files")
|
||||
}
|
||||
fs.stat = async (uri) => {
|
||||
if (uri.fsPath === api("keep")) return { type: vscode.FileType.File, ctime: 0, mtime: 0, size: 1 }
|
||||
throw new Error(`missing ${uri.fsPath}`)
|
||||
}
|
||||
|
||||
const items = [
|
||||
{ id: "keep", task: "Keep me", workspace: "/repo", ts: 1700000000000 },
|
||||
{ id: "gone", task: "Deleted on disk", workspace: "/repo", ts: 1700000000001 },
|
||||
]
|
||||
const scan = await scanTaskStore(dir, items, { mode: "history" })
|
||||
|
||||
// "gone" is dropped (no file) and on-disk orphans are never considered (no enumeration).
|
||||
expect(listSessions(scan.catalog)).toEqual([{ id: "keep", title: "Keep me", directory: "/repo", time: 1700000000000 }])
|
||||
expect(resolveSession(scan.catalog, "keep")).toMatchObject({ id: "keep", dir })
|
||||
expect(scan.diagnostics).toEqual([])
|
||||
expect(listed).toBe(false)
|
||||
expect(read).toBe(false)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user