import { Database } from 'bun:sqlite'; import { AgentType } from '../types.js'; export type ServiceShadowAgentTypeResolver = ( serviceId: string, ) => AgentType | undefined; function hasLegacyServiceSessionsTable(database: Database): boolean { return Boolean( database .prepare( `SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'service_sessions'`, ) .get(), ); } export function migrateSessionsTableToCompositePk( database: Database, defaultAgentType: AgentType, ): void { const sessionCols = database .prepare('PRAGMA table_info(sessions)') .all() as Array<{ name: string }>; if (sessionCols.some((col) => col.name === 'agent_type')) { return; } database.exec(` CREATE TABLE sessions_new ( group_folder TEXT NOT NULL, agent_type TEXT NOT NULL DEFAULT '${defaultAgentType}', session_id TEXT NOT NULL, PRIMARY KEY (group_folder, agent_type) ); `); database .prepare( `INSERT INTO sessions_new (group_folder, agent_type, session_id) SELECT group_folder, ?, session_id FROM sessions`, ) .run(defaultAgentType); database.exec(` DROP TABLE sessions; ALTER TABLE sessions_new RENAME TO sessions; `); } export function backfillLegacyServiceSessions( database: Database, resolveAgentTypeFromServiceId: ServiceShadowAgentTypeResolver, ): void { if (!hasLegacyServiceSessionsTable(database)) { return; } const rows = database .prepare( `SELECT group_folder, service_id, session_id FROM service_sessions`, ) .all() as Array<{ group_folder: string; service_id: string; session_id: string; }>; const upsert = database.prepare( `INSERT OR IGNORE INTO sessions (group_folder, agent_type, session_id) VALUES (?, ?, ?)`, ); const tx = database.transaction( ( sessionRows: Array<{ group_folder: string; service_id: string; session_id: string; }>, ) => { for (const row of sessionRows) { const agentType = resolveAgentTypeFromServiceId(row.service_id); if (!agentType) { continue; } upsert.run(row.group_folder, agentType, row.session_id); } }, ); tx(rows); } export function dropLegacyServiceSessionsTable(database: Database): void { if (!hasLegacyServiceSessionsTable(database)) { return; } database.exec(`DROP TABLE service_sessions`); } export function getSessionFromDatabase( database: Database, groupFolder: string, agentType: string, ): string | undefined { const row = database .prepare( 'SELECT session_id FROM sessions WHERE group_folder = ? AND agent_type = ?', ) .get(groupFolder, agentType) as { session_id: string } | undefined; return row?.session_id; } export function setSessionInDatabase( database: Database, groupFolder: string, agentType: string, sessionId: string, ): void { database .prepare( 'INSERT OR REPLACE INTO sessions (group_folder, agent_type, session_id) VALUES (?, ?, ?)', ) .run(groupFolder, agentType, sessionId); } export function deleteSessionFromDatabase( database: Database, groupFolder: string, agentType: string, ): void { database .prepare('DELETE FROM sessions WHERE group_folder = ? AND agent_type = ?') .run(groupFolder, agentType); } export function deleteAllSessionsForGroupFromDatabase( database: Database, groupFolder: string, ): void { database .prepare( `DELETE FROM sessions WHERE group_folder IN (?, ?, ?)`, ) .run(groupFolder, `${groupFolder}:reviewer`, `${groupFolder}:arbiter`); } export function getAllSessionsForAgentTypeFromDatabase( database: Database, agentType: string, ): Record { const rows = database .prepare( 'SELECT group_folder, session_id FROM sessions WHERE agent_type = ?', ) .all(agentType) as Array<{ group_folder: string; session_id: string; }>; const result: Record = {}; for (const row of rows) { result[row.group_folder] = row.session_id; } return result; }