* Materialize mothership task chats as browsable conversation files. * Returns a summary for WORKSPACE.md generation.
(
workspaceId: string,
userId: string
)
| 1877 | * Returns a summary for WORKSPACE.md generation. |
| 1878 | */ |
| 1879 | private async materializeTasks( |
| 1880 | workspaceId: string, |
| 1881 | userId: string |
| 1882 | ): Promise<WorkspaceMdData['tasks']> { |
| 1883 | try { |
| 1884 | const taskRows = await db |
| 1885 | .select({ |
| 1886 | id: copilotChats.id, |
| 1887 | title: copilotChats.title, |
| 1888 | messageCount: sql<number>`COALESCE(( |
| 1889 | SELECT COUNT(*) FROM copilot_messages cm |
| 1890 | WHERE cm.chat_id = ${copilotChats.id} AND cm.deleted_at IS NULL |
| 1891 | ), 0)`, |
| 1892 | messages: sql<unknown[]>`COALESCE(( |
| 1893 | SELECT jsonb_agg( |
| 1894 | jsonb_build_object( |
| 1895 | 'role', cm.content->>'role', |
| 1896 | 'content', cm.content->'content', |
| 1897 | 'contentBlocks', COALESCE(( |
| 1898 | SELECT jsonb_agg(jsonb_build_object('type', 'text', 'content', b.value->'content') ORDER BY b.ord) |
| 1899 | FROM jsonb_array_elements( |
| 1900 | CASE WHEN jsonb_typeof(cm.content->'contentBlocks') = 'array' |
| 1901 | THEN cm.content->'contentBlocks' |
| 1902 | ELSE '[]'::jsonb |
| 1903 | END |
| 1904 | ) WITH ORDINALITY AS b(value, ord) |
| 1905 | WHERE b.value->>'type' = 'text' |
| 1906 | ), '[]'::jsonb) |
| 1907 | ) |
| 1908 | ORDER BY cm.seq ASC NULLS LAST, cm.created_at ASC, cm.id ASC |
| 1909 | ) |
| 1910 | FROM copilot_messages cm |
| 1911 | WHERE cm.chat_id = ${copilotChats.id} |
| 1912 | AND cm.deleted_at IS NULL |
| 1913 | AND cm.content->>'role' IN ('user', 'assistant') |
| 1914 | ), '[]'::jsonb)`, |
| 1915 | createdAt: copilotChats.createdAt, |
| 1916 | updatedAt: copilotChats.updatedAt, |
| 1917 | }) |
| 1918 | .from(copilotChats) |
| 1919 | .where( |
| 1920 | and( |
| 1921 | eq(copilotChats.workspaceId, workspaceId), |
| 1922 | eq(copilotChats.userId, userId), |
| 1923 | eq(copilotChats.type, 'mothership') |
| 1924 | ) |
| 1925 | ) |
| 1926 | .orderBy(desc(copilotChats.updatedAt)) |
| 1927 | .limit(5) |
| 1928 | |
| 1929 | for (const task of taskRows) { |
| 1930 | const title = task.title || 'Untitled task' |
| 1931 | const safeName = sanitizeName(title) |
| 1932 | const prefix = `tasks/${safeName}/` |
| 1933 | const messages = Array.isArray(task.messages) ? task.messages : [] |
| 1934 | const messageCount = Number(task.messageCount) || 0 |
| 1935 | |
| 1936 | this.files.set( |
no test coverage detected