import type { AgentSchedule, AgentThread, ImageChunk, Message } from "./types" export type { AgentSchedule, AgentThread, Message } export interface ThreadCreateRequest { prompt: string images?: Array repo?: string | null repo_explicitly_none?: boolean model_id?: string | null effort?: string | null } export interface ThreadMessageRequest { content: string images?: Array model_id?: string | null effort?: string | null } export interface ScheduleCreateRequest { prompt: string schedule: string name?: string | null repo?: string | null model_id?: string | null effort?: string | null } export interface ScheduleUpdateRequest { prompt?: string | null schedule?: string | null name?: string | null repo?: string | null model_id?: string | null effort?: string | null enabled?: boolean | null } const API_BASE = (import.meta.env.VITE_DASHBOARD_API_BASE_URL ?? "").replace( /\/$/, "" ) async function agentsRequest( path: string, init: RequestInit = {} ): Promise { const res = await fetch(`${API_BASE}/dashboard/api${path}`, { ...init, credentials: "include", headers: { "Content-Type": "application/json", ...(init.headers ?? {}), }, }) if (!res.ok) { let message = res.statusText try { const body = await res.json() if (body?.detail) { message = typeof body.detail === "string" ? body.detail : JSON.stringify(body.detail) } } catch { /* ignore */ } throw new Error(message) } if (res.status === 204) return undefined as T return (await res.json()) as T } export const agentsApi = { listThreads: () => agentsRequest>("/threads"), listSchedules: () => agentsRequest>("/schedules"), createSchedule: (body: ScheduleCreateRequest) => agentsRequest("/schedules", { method: "POST", body: JSON.stringify(body), }), updateSchedule: (scheduleId: string, body: ScheduleUpdateRequest) => agentsRequest( `/schedules/${encodeURIComponent(scheduleId)}`, { method: "PATCH", body: JSON.stringify(body), } ), deleteSchedule: (scheduleId: string) => agentsRequest(`/schedules/${encodeURIComponent(scheduleId)}`, { method: "DELETE", }), getThread: (threadId: string, options?: { markViewed?: boolean }) => agentsRequest( `/threads/${encodeURIComponent(threadId)}${ options?.markViewed === false ? "?mark_viewed=false" : "" }` ), createThread: (body: ThreadCreateRequest) => agentsRequest("/threads", { method: "POST", body: JSON.stringify(body), }), sendMessage: (threadId: string, body: ThreadMessageRequest) => agentsRequest( `/threads/${encodeURIComponent(threadId)}/messages`, { method: "POST", body: JSON.stringify(body), } ), cancelThread: (threadId: string) => agentsRequest( `/threads/${encodeURIComponent(threadId)}/cancel`, { method: "POST", } ), deleteThread: (threadId: string) => agentsRequest(`/threads/${encodeURIComponent(threadId)}`, { method: "DELETE", }), streamUrl: (threadId: string) => `${API_BASE}/dashboard/api/threads/${encodeURIComponent(threadId)}/stream`, } export function formatRelativeTime(ts: number): string { const diff = Date.now() - ts const mins = Math.floor(diff / 60000) if (mins < 60) return `${mins}m` const hours = Math.floor(mins / 60) if (hours < 24) return `${hours}h` const days = Math.floor(hours / 24) if (days < 7) return `${days}d` const weeks = Math.floor(days / 7) return `${weeks}w` } export type ThreadGroup = "today" | "last7" | "last30" | "older" export function groupThreads( threads: Array ): Record> { const todayStart = new Date() todayStart.setHours(0, 0, 0, 0) const sevenDaysAgo = Date.now() - 7 * 24 * 60 * 60 * 1000 const thirtyDaysAgo = Date.now() - 30 * 24 * 60 * 60 * 1000 const groups: Record> = { today: [], last7: [], last30: [], older: [], } for (const thread of [...threads].sort((a, b) => b.updatedAt - a.updatedAt)) { if (thread.updatedAt >= todayStart.getTime()) { groups.today.push(thread) } else if (thread.updatedAt >= sevenDaysAgo) { groups.last7.push(thread) } else if (thread.updatedAt >= thirtyDaysAgo) { groups.last30.push(thread) } else { groups.older.push(thread) } } return groups }