@@ -6066,6 +6066,241 @@ function readKiloCodeDatabaseSessionDetail(dbPath, sessionId, messageLimit = DEF
60666066 };
60676067}
60686068
6069+
6070+ function makeKiloCodeStateVscdbRef(dbPath, sessionId) {
6071+ return `${dbPath}#${encodeURIComponent(sessionId)}`;
6072+ }
6073+
6074+ function parseKiloCodeStateVscdbRef(value) {
6075+ if (typeof value !== 'string') return null;
6076+ const marker = value.lastIndexOf('#');
6077+ if (marker <= 0) return null;
6078+ const dbPath = value.slice(0, marker);
6079+ if (!/state\.vscdb$/i.test(dbPath)) return null;
6080+ return { dbPath, sessionId: decodeURIComponent(value.slice(marker + 1)) };
6081+ }
6082+
6083+ function decodeVscodeStateValue(value) {
6084+ if (value == null) return null;
6085+ let text = '';
6086+ if (Buffer.isBuffer(value)) text = value.toString('utf8');
6087+ else if (typeof value === 'string') text = value;
6088+ else return value;
6089+ text = stripUtf8Bom(text).trim();
6090+ if (!text) return null;
6091+ let parsed = parseKiloCodeJson(text, undefined);
6092+ if (typeof parsed === 'string') {
6093+ const nested = parseKiloCodeJson(parsed, undefined);
6094+ if (nested !== undefined) parsed = nested;
6095+ }
6096+ return parsed === undefined ? text : parsed;
6097+ }
6098+
6099+ function getKiloCodeStateVscdbFiles() {
6100+ const files = [];
6101+ const seen = new Set();
6102+ const pushFile = (value) => {
6103+ if (typeof value !== 'string' || !value.trim()) return;
6104+ const full = expandHomePath(value.trim());
6105+ if (!full || seen.has(full) || !fs.existsSync(full)) return;
6106+ seen.add(full);
6107+ files.push(full);
6108+ };
6109+ const pushDir = (value) => {
6110+ if (typeof value !== 'string' || !value.trim()) return;
6111+ const root = expandHomePath(value.trim());
6112+ if (!root || !fs.existsSync(root)) return;
6113+ const direct = path.join(root, 'state.vscdb');
6114+ pushFile(direct);
6115+ let entries = [];
6116+ try { entries = fs.readdirSync(root, { withFileTypes: true }); } catch (_) { entries = []; }
6117+ for (const entry of entries) {
6118+ if (entry.isDirectory()) pushFile(path.join(root, entry.name, 'state.vscdb'));
6119+ }
6120+ };
6121+ pushFile(process.env.KILOCODE_STATE_VSCDB_FILE);
6122+ pushDir(process.env.KILOCODE_STATE_VSCDB_DIR);
6123+ const home = os.homedir();
6124+ const xdgConfig = process.env.XDG_CONFIG_HOME || (home ? path.join(home, '.config') : '');
6125+ const appData = process.env.APPDATA;
6126+ const appBases = [];
6127+ for (const app of ['Code', 'Code - Insiders', 'VSCodium', 'Cursor', 'Windsurf']) {
6128+ if (xdgConfig) appBases.push(path.join(xdgConfig, app, 'User'));
6129+ if (home) appBases.push(path.join(home, 'Library', 'Application Support', app, 'User'));
6130+ if (appData) appBases.push(path.join(appData, app, 'User'));
6131+ }
6132+ for (const base of appBases) {
6133+ pushDir(path.join(base, 'workspaceStorage'));
6134+ pushFile(path.join(base, 'globalStorage', 'state.vscdb'));
6135+ }
6136+ return files;
6137+ }
6138+
6139+ function readVscodeStateVscdbRows(dbPath) {
6140+ if (!dbPath || !fs.existsSync(dbPath)) return [];
6141+ const Database = loadKiloCodeSqliteBinding();
6142+ if (!Database) return [];
6143+ let db;
6144+ try {
6145+ db = new Database(dbPath, { readonly: true, fileMustExist: true });
6146+ const tables = new Set(db.prepare("select name from sqlite_master where type='table'").all().map(row => row.name));
6147+ if (!tables.has('ItemTable')) return [];
6148+ const columns = new Set(db.prepare("pragma table_info(ItemTable)").all().map(row => row.name));
6149+ if (!columns.has('key') || !columns.has('value')) return [];
6150+ return db.prepare('select key, value from ItemTable').all().map(row => ({ key: String(row.key || ''), value: decodeVscodeStateValue(row.value) }));
6151+ } catch (_) {
6152+ return [];
6153+ } finally {
6154+ if (db) { try { db.close(); } catch (_) { } }
6155+ }
6156+ }
6157+
6158+ function looksLikeKiloCodeLegacyHistoryItem(value) {
6159+ return value && typeof value === 'object'
6160+ && typeof value.id === 'string'
6161+ && (typeof value.task === 'string' || typeof value.workspace === 'string' || Number.isFinite(Number(value.ts)));
6162+ }
6163+
6164+ function collectKiloCodeStateHistoryItems(rows) {
6165+ const items = [];
6166+ const pushArray = (arr) => {
6167+ if (!Array.isArray(arr)) return;
6168+ for (const item of arr) {
6169+ if (looksLikeKiloCodeLegacyHistoryItem(item)) items.push(item);
6170+ }
6171+ };
6172+ for (const row of rows) {
6173+ const key = String(row.key || '').toLowerCase();
6174+ const value = row.value;
6175+ if (key.includes('taskhistory')) pushArray(value);
6176+ if (value && typeof value === 'object' && !Array.isArray(value)) {
6177+ pushArray(value.taskHistory);
6178+ pushArray(value.history);
6179+ }
6180+ if (Array.isArray(value) && value.some(looksLikeKiloCodeLegacyHistoryItem)) pushArray(value);
6181+ }
6182+ return items;
6183+ }
6184+
6185+ function collectKiloCodeStateMessagesForSession(rows, sessionId) {
6186+ const target = String(sessionId || '').toLowerCase();
6187+ if (!target) return [];
6188+ const candidates = [];
6189+ const pushMessages = (value) => {
6190+ if (Array.isArray(value)) {
6191+ const normalized = value.map((entry, index) => normalizeKiloCodeLegacyConversationMessage(entry, index, {})).filter(Boolean);
6192+ if (normalized.length) candidates.push(value);
6193+ }
6194+ };
6195+ for (const row of rows) {
6196+ const key = String(row.key || '').toLowerCase();
6197+ const value = row.value;
6198+ if (key.includes(target)) {
6199+ pushMessages(value);
6200+ if (value && typeof value === 'object' && !Array.isArray(value)) {
6201+ pushMessages(value.api_conversation_history);
6202+ pushMessages(value.apiConversationHistory);
6203+ pushMessages(value.messages);
6204+ }
6205+ }
6206+ if (value && typeof value === 'object' && !Array.isArray(value)) {
6207+ const id = String(value.id || value.taskId || value.sessionId || '').toLowerCase();
6208+ if (id === target) {
6209+ pushMessages(value.api_conversation_history);
6210+ pushMessages(value.apiConversationHistory);
6211+ pushMessages(value.messages);
6212+ }
6213+ }
6214+ }
6215+ return candidates[0] || [];
6216+ }
6217+
6218+ function toKiloCodeStateVscdbSummary(dbPath, item, rows) {
6219+ const parsed = parseKiloCodeLegacyHistoryItem(item, item?.id);
6220+ if (!parsed || !parsed.id) return null;
6221+ const embeddedMessages = collectKiloCodeStateMessagesForSession(rows, parsed.id);
6222+ const normalized = removeLeadingSystemMessage(embeddedMessages.map((entry, index) => normalizeKiloCodeLegacyConversationMessage(entry, index, parsed)).filter(Boolean));
6223+ return {
6224+ source: 'kilocode',
6225+ sourceLabel: 'KiloCode VSCode',
6226+ provider: 'kilocode',
6227+ model: '',
6228+ models: [],
6229+ sessionId: parsed.id,
6230+ title: parsed.task || titleFromKiloCodeLegacyMessages(embeddedMessages) || parsed.id,
6231+ cwd: parsed.workspace || '',
6232+ createdAt: toIsoTime(parsed.ts, ''),
6233+ updatedAt: toIsoTime(parsed.ts, ''),
6234+ messageCount: normalized.length,
6235+ totalTokens: 0,
6236+ contextWindow: 0,
6237+ inputTokens: 0,
6238+ cachedInputTokens: 0,
6239+ cacheCreationInputTokens: 0,
6240+ outputTokens: 0,
6241+ reasoningOutputTokens: 0,
6242+ __messageCountExact: Boolean(normalized.length),
6243+ filePath: makeKiloCodeStateVscdbRef(dbPath, parsed.id),
6244+ keywords: [],
6245+ capabilities: { code: true, vscode: true, stateVscdb: true }
6246+ };
6247+ }
6248+
6249+ function listKiloCodeStateVscdbSessions(limit) {
6250+ const sessions = [];
6251+ const lookupStore = g_sessionFileLookupCache.kilocode;
6252+ for (const dbPath of getKiloCodeStateVscdbFiles()) {
6253+ const rows = readVscodeStateVscdbRows(dbPath);
6254+ const seen = new Set();
6255+ for (const item of collectKiloCodeStateHistoryItems(rows)) {
6256+ const summary = toKiloCodeStateVscdbSummary(dbPath, item, rows);
6257+ if (!summary) continue;
6258+ const idKey = summary.sessionId.toLowerCase();
6259+ if (seen.has(idKey)) continue;
6260+ seen.add(idKey);
6261+ if (lookupStore instanceof Map) lookupStore.set(idKey, summary.filePath);
6262+ sessions.push(summary);
6263+ if (sessions.length >= limit) break;
6264+ }
6265+ if (sessions.length >= limit) break;
6266+ }
6267+ return sessions;
6268+ }
6269+
6270+ function readKiloCodeStateVscdbSessionDetail(refOrPath, sessionId, messageLimit = DEFAULT_SESSION_DETAIL_MESSAGES) {
6271+ const parsedRef = parseKiloCodeStateVscdbRef(refOrPath);
6272+ const dbPath = parsedRef ? parsedRef.dbPath : refOrPath;
6273+ const id = sessionId || parsedRef?.sessionId || '';
6274+ if (!dbPath || !id || !fs.existsSync(dbPath)) return null;
6275+ const rows = readVscodeStateVscdbRows(dbPath);
6276+ const item = collectKiloCodeStateHistoryItems(rows).find(entry => String(entry.id || '').toLowerCase() === String(id).toLowerCase()) || {};
6277+ const embeddedMessages = collectKiloCodeStateMessagesForSession(rows, id);
6278+ if (embeddedMessages.length) {
6279+ const parsed = parseKiloCodeLegacyHistoryItem(item, id) || { id, workspace: '', ts: 0 };
6280+ const filtered = removeLeadingSystemMessage(embeddedMessages.map((entry, index) => normalizeKiloCodeLegacyConversationMessage(entry, index, parsed)).filter(Boolean));
6281+ const clipped = filtered.length > messageLimit;
6282+ return {
6283+ sessionId: id,
6284+ cwd: parsed.workspace || '',
6285+ updatedAt: toIsoTime(parsed.ts, ''),
6286+ totalMessages: filtered.length,
6287+ clipped,
6288+ truncated: clipped,
6289+ messages: clipped ? filtered.slice(-messageLimit) : filtered
6290+ };
6291+ }
6292+ for (const taskRoot of getKiloCodeLegacyTaskRoots()) {
6293+ const apiPath = path.join(taskRoot, id, 'api_conversation_history.json');
6294+ const detail = readKiloCodeLegacySessionDetail(apiPath, id, messageLimit);
6295+ if (detail) return detail;
6296+ }
6297+ if (looksLikeKiloCodeLegacyHistoryItem(item)) {
6298+ const parsed = parseKiloCodeLegacyHistoryItem(item, id) || {};
6299+ return { sessionId: id, cwd: parsed.workspace || '', updatedAt: toIsoTime(parsed.ts, ''), totalMessages: 0, clipped: false, truncated: false, messages: [] };
6300+ }
6301+ return null;
6302+ }
6303+
60696304function extractKiloCodeMessageFromRecord(record, state, lineIndex = -1) {
60706305 if (!record || typeof record !== 'object') return;
60716306 if (record.timestamp || record.updatedAt || record.time) {
@@ -6178,7 +6413,7 @@ function collectKiloCodeSessionFiles(rootDir, maxFiles = 5000) {
61786413
61796414function listKiloCodeSessions(limit, options = {}) {
61806415 const targetCount = Math.max(limit * (Number(options.scanFactor) || SESSION_SCAN_FACTOR), Number(options.minFiles) || SESSION_SCAN_MIN_FILES);
6181- const sessions = listKiloCodeDatabaseSessions(targetCount).concat(listKiloCodeLegacyTaskSessions(targetCount));
6416+ const sessions = listKiloCodeDatabaseSessions(targetCount).concat(listKiloCodeLegacyTaskSessions(targetCount), listKiloCodeStateVscdbSessions(targetCount) );
61826417 const seenIds = new Set(sessions.map(item => String(item.sessionId || '').toLowerCase()).filter(Boolean));
61836418 const filesMeta = [];
61846419 for (const root of getKiloCodeSessionRoots()) {
@@ -7043,6 +7278,10 @@ function resolveSessionFilePath(source, filePath, sessionId) {
70437278 const lookupStore = g_sessionFileLookupCache[normalizedSource];
70447279 if (lookupStore instanceof Map && lookupStore.has(targetId)) {
70457280 const cachedPath = lookupStore.get(targetId);
7281+ const cachedStateRef = normalizedSource === 'kilocode' ? parseKiloCodeStateVscdbRef(cachedPath) : null;
7282+ if (cachedStateRef && fs.existsSync(cachedStateRef.dbPath)) {
7283+ return cachedPath;
7284+ }
70467285 if (cachedPath && fs.existsSync(cachedPath) && availableRoots.some((rootPath) => isPathInside(cachedPath, rootPath))) {
70477286 return cachedPath;
70487287 }
@@ -7094,6 +7333,15 @@ function resolveSessionFilePath(source, filePath, sessionId) {
70947333 }
70957334 }
70967335 }
7336+ if (!matchedFile) {
7337+ for (const dbPath of getKiloCodeStateVscdbFiles()) {
7338+ const detail = readKiloCodeStateVscdbSessionDetail(dbPath, sessionId.trim(), 1);
7339+ if (detail) {
7340+ matchedFile = makeKiloCodeStateVscdbRef(dbPath, sessionId.trim());
7341+ break;
7342+ }
7343+ }
7344+ }
70977345 if (!matchedFile) {
70987346 const files = [];
70997347 for (const rootPath of availableRoots) {
@@ -7110,8 +7358,10 @@ function resolveSessionFilePath(source, filePath, sessionId) {
71107358 }
71117359 matchedFile = files.find(item => path.basename(item, '.jsonl').toLowerCase() === targetId) || '';
71127360 }
7113- if (matchedFile && fs.existsSync(matchedFile)) {
7114- return matchedFile;
7361+ if (matchedFile) {
7362+ const stateRef = normalizedSource === 'kilocode' ? parseKiloCodeStateVscdbRef(matchedFile) : null;
7363+ if (stateRef && fs.existsSync(stateRef.dbPath)) return matchedFile;
7364+ if (fs.existsSync(matchedFile)) return matchedFile;
71157365 }
71167366 }
71177367
@@ -8940,6 +9190,7 @@ async function readSessionDetail(params = {}) {
89409190 if (source === 'kilocode') {
89419191 extracted = readKiloCodeDatabaseSessionDetail(filePath, params.sessionId, messageLimit)
89429192 || readKiloCodeLegacySessionDetail(filePath, params.sessionId, messageLimit)
9193+ || readKiloCodeStateVscdbSessionDetail(filePath, params.sessionId, messageLimit)
89439194 || await extractSessionDetailPreviewFromFile(filePath, source, messageLimit, { preview });
89449195 } else if (source === 'gemini') {
89459196 let json;
@@ -9075,6 +9326,7 @@ async function readSessionPlain(params = {}) {
90759326 if (source === 'kilocode') {
90769327 extracted = readKiloCodeDatabaseSessionDetail(filePath, params.sessionId, maxMessages === Infinity ? MAX_EXPORT_MESSAGES : maxMessages)
90779328 || readKiloCodeLegacySessionDetail(filePath, params.sessionId, maxMessages === Infinity ? MAX_EXPORT_MESSAGES : maxMessages)
9329+ || readKiloCodeStateVscdbSessionDetail(filePath, params.sessionId, maxMessages === Infinity ? MAX_EXPORT_MESSAGES : maxMessages)
90789330 || await extractMessagesFromFile(filePath, source, { maxMessages });
90799331 } else if (source === 'gemini') {
90809332 let json;
@@ -9167,6 +9419,7 @@ async function exportSessionData(params = {}) {
91679419 if (source === 'kilocode') {
91689420 extracted = readKiloCodeDatabaseSessionDetail(filePath, params.sessionId, maxMessages === Infinity ? MAX_EXPORT_MESSAGES : maxMessages)
91699421 || readKiloCodeLegacySessionDetail(filePath, params.sessionId, maxMessages === Infinity ? MAX_EXPORT_MESSAGES : maxMessages)
9422+ || readKiloCodeStateVscdbSessionDetail(filePath, params.sessionId, maxMessages === Infinity ? MAX_EXPORT_MESSAGES : maxMessages)
91709423 || await extractMessagesFromFile(filePath, source, { maxMessages });
91719424 } else if (source === 'gemini') {
91729425 let json;
0 commit comments