import { StateHydrator } from '@/components/state-hydrator'; import { readLayoutDefaults } from '@/lib/layout/read-layout-defaults'; import { fetchWorkflowSnapshot, fetchTaskSession, fetchTask, fetchUserSettings, listAgents, listAvailableAgents, listWorkflows, listRepositories, listTaskSessionMessages, listTaskSessions, listWorkspaces, } from '@/lib/api'; import { toAgentProfileOption } from '@/lib/state/slices/settings/types'; import { listSessionTurns } from '@/lib/api/domains/session-api'; import { fetchTerminals } from '@/lib/api/domains/user-shell-api'; import type { ListMessagesResponse, Task } from '@/lib/types/http'; import type { Terminal } from '@/hooks/domains/session/use-terminals'; import { snapshotToState, taskToState } from '@/lib/ssr/mapper'; import { TaskPageContent } from '@/components/task/task-page-content'; export default async function SessionPage({ params, }: { params: Promise<{ sessionId: string }>; }) { let initialState: ReturnType | null = null; let task: Task | null = null; let sessionId: string | null = null; let initialTerminals: Terminal[] = []; const defaultLayouts = await readLayoutDefaults(); try { const { sessionId: paramSessionId } = await params; sessionId = paramSessionId; const sessionResponse = await fetchTaskSession(sessionId, { cache: 'no-store' }); const session = sessionResponse.session; if (!session?.task_id) { throw new Error('No task_id found for session'); } task = await fetchTask(session.task_id, { cache: 'no-store' }); const [snapshot, agents, repositoriesResponse, allSessionsResponse, availableAgentsResponse, workspacesResponse, workflowsResponse, turnsResponse, userSettingsResponse, terminalsResponse] = await Promise.all([ fetchWorkflowSnapshot(task.workflow_id, { cache: 'no-store' }), listAgents({ cache: 'no-store' }), listRepositories(task.workspace_id, { includeScripts: true }, { cache: 'no-store' }), listTaskSessions(session.task_id, { cache: 'no-store' }), listAvailableAgents({ cache: 'no-store' }).catch(() => ({ agents: [] })), listWorkspaces({ cache: 'no-store' }).catch(() => ({ workspaces: [] })), listWorkflows(task.workspace_id, { cache: 'no-store' }).catch(() => ({ workflows: [] })), listSessionTurns(sessionId, { cache: 'no-store' }).catch(() => ({ turns: [], total: 0 })), fetchUserSettings({ cache: 'no-store' }).catch(() => null), fetchTerminals(sessionId).catch(() => []), ]); const repositories = repositoriesResponse.repositories ?? []; const allSessions = allSessionsResponse.sessions ?? [session]; const availableAgents = availableAgentsResponse.agents ?? []; const workspaces = workspacesResponse.workspaces ?? []; const workflows = workflowsResponse.workflows ?? []; const turns = turnsResponse.turns ?? []; const userSettings = userSettingsResponse?.settings; // Transform terminals to frontend format initialTerminals = terminalsResponse.map((t) => ({ id: t.terminal_id, type: t.initial_command ? 'script' as const : 'shell' as const, label: t.label, closable: t.closable, })); // Get repository scripts from the repository (already fetched with includeScripts) const repositoryId = task.repositories?.[0]?.repository_id; const repository = repositories.find(r => r.id === repositoryId); const scripts = repository?.scripts ?? []; let messagesResponse: ListMessagesResponse | null = null; try { // Load most recent messages in descending order, then reverse to show oldest-to-newest messagesResponse = await listTaskSessionMessages( sessionId, { limit: 50, sort: 'desc' }, { cache: 'no-store' } ); } catch (error) { console.warn( 'Could not load session messages for SSR:', error instanceof Error ? error.message : String(error) ); } const messages = messagesResponse?.messages ? [...messagesResponse.messages].reverse() : []; const taskState = taskToState(task, sessionId, { items: messages, hasMore: messagesResponse?.has_more ?? false, // oldestCursor should be the first (oldest) message ID for lazy loading older messages oldestCursor: messages[0]?.id ?? null, }); initialState = { ...snapshotToState(snapshot), ...taskState, workspaces: { items: workspaces.map((workspace) => ({ id: workspace.id, name: workspace.name, description: workspace.description ?? null, owner_id: workspace.owner_id, default_executor_id: workspace.default_executor_id ?? null, default_environment_id: workspace.default_environment_id ?? null, default_agent_profile_id: workspace.default_agent_profile_id ?? null, created_at: workspace.created_at, updated_at: workspace.updated_at, })), activeId: task.workspace_id, }, workflows: { items: workflows.map((workflow) => ({ id: workflow.id, workspaceId: workflow.workspace_id, name: workflow.name, })), activeId: task.workflow_id, }, repositories: { itemsByWorkspaceId: { [task.workspace_id]: repositories, }, loadingByWorkspaceId: { [task.workspace_id]: false, }, loadedByWorkspaceId: { [task.workspace_id]: true, }, }, repositoryScripts: repositoryId ? { itemsByRepositoryId: { [repositoryId]: scripts, }, loadingByRepositoryId: { [repositoryId]: false, }, loadedByRepositoryId: { [repositoryId]: true, }, } : { itemsByRepositoryId: {}, loadingByRepositoryId: {}, loadedByRepositoryId: {}, }, agentProfiles: { items: agents.agents.flatMap((agent) => agent.profiles.map((profile) => toAgentProfileOption(agent, profile)) ), version: 0, }, taskSessions: { items: Object.fromEntries(allSessions.map((s) => [s.id, s])), }, taskSessionsByTask: { itemsByTaskId: { [task.id]: allSessions, }, loadingByTaskId: { [task.id]: false, }, loadedByTaskId: { [task.id]: true, }, }, turns: { bySession: { [sessionId]: turns, }, activeBySession: { // Find the most recent (last) incomplete turn [sessionId]: turns.filter((t) => !t.completed_at).pop()?.id ?? null, }, }, worktrees: { items: Object.fromEntries( allSessions .filter((s) => s.worktree_id) .map((s) => [ s.worktree_id, { id: s.worktree_id!, sessionId: s.id, repositoryId: s.repository_id ?? undefined, path: s.worktree_path ?? undefined, branch: s.worktree_branch ?? undefined, }, ]) ), }, sessionWorktreesBySessionId: { itemsBySessionId: Object.fromEntries( allSessions .filter((s) => s.worktree_id) .map((s) => [s.id, [s.worktree_id!]]) ), }, settingsAgents: { items: agents.agents, }, settingsData: { agentsLoaded: true, executorsLoaded: false, environmentsLoaded: false, }, availableAgents: { items: availableAgents, loaded: true, loading: false, }, userSettings: { workspaceId: userSettings?.workspace_id || null, workflowId: userSettings?.workflow_filter_id || null, kanbanViewMode: userSettings?.kanban_view_mode || null, repositoryIds: Array.from(new Set(userSettings?.repository_ids ?? [])).sort(), preferredShell: userSettings?.preferred_shell || null, shellOptions: userSettingsResponse?.shell_options ?? [], defaultEditorId: userSettings?.default_editor_id || null, enablePreviewOnClick: userSettings?.enable_preview_on_click ?? false, chatSubmitKey: userSettings?.chat_submit_key ?? 'cmd_enter', reviewAutoMarkOnScroll: userSettings?.review_auto_mark_on_scroll ?? true, lspAutoStartLanguages: userSettings?.lsp_auto_start_languages ?? [], lspAutoInstallLanguages: userSettings?.lsp_auto_install_languages ?? [], lspServerConfigs: userSettings?.lsp_server_configs ?? {}, loaded: Boolean(userSettings), }, }; } catch (error) { console.warn( 'Could not SSR session (client will load via WebSocket):', error instanceof Error ? error.message : String(error) ); initialState = null; task = null; sessionId = null; } return ( <> {initialState ? : null} ); }