import type { Sql } from '../db.js'; interface WorkspacePaneRow { id: string; kind: string; chatId?: string; chatIds?: string[]; activeChatIdx?: number; } function chatNameForKind(kind: string): string { if (kind === 'coder' || kind === 'agent') return 'BooCoder'; if (kind === 'terminal') return 'Terminal'; return 'Chat'; } function activeChatIdForPane(pane: WorkspacePaneRow): string | undefined { const chatIds = pane.chatIds ?? []; const idx = pane.activeChatIdx ?? 0; if (idx >= 0 && idx < chatIds.length) return chatIds[idx]; return pane.chatId; } /** Resolve the active chat for a workspace pane; auto-seed when empty. */ export async function resolveChatId( sql: Sql, sessionId: string, paneId: string, ): Promise { return sql.begin(async (tx) => { const sessionRows = await tx<{ workspace_panes: WorkspacePaneRow[] }[]>` SELECT workspace_panes FROM sessions WHERE id = ${sessionId} FOR UPDATE `; if (sessionRows.length === 0) return null; const panes = sessionRows[0]!.workspace_panes ?? []; const paneIdx = panes.findIndex((p) => p.id === paneId); if (paneIdx < 0) return null; const pane = panes[paneIdx]!; const existingChatId = activeChatIdForPane(pane); if (existingChatId) { const chatRows = await tx<{ id: string }[]>` SELECT id FROM chats WHERE id = ${existingChatId} AND session_id = ${sessionId} AND status = 'open' `; if (chatRows.length > 0) return existingChatId; } const [newChat] = await tx<{ id: string }[]>` INSERT INTO chats (session_id, name, status) VALUES (${sessionId}, ${chatNameForKind(pane.kind)}, 'open') RETURNING id `; if (!newChat) return null; const nextChatIds = [...(pane.chatIds ?? []), newChat.id]; const nextActiveIdx = nextChatIds.length - 1; const nextPanes = panes.map((p, i) => i === paneIdx ? { ...p, chatIds: nextChatIds, activeChatIdx: nextActiveIdx, chatId: newChat.id, } : p, ); await tx` UPDATE sessions SET workspace_panes = ${tx.json(nextPanes as never)}, updated_at = clock_timestamp() WHERE id = ${sessionId} `; return newChat.id; }); }