diff --git a/convex/mcp_mutations.ts b/convex/mcp_mutations.ts index 7cdf536..5c30ce1 100644 --- a/convex/mcp_mutations.ts +++ b/convex/mcp_mutations.ts @@ -83,10 +83,13 @@ export const acquireConnectionLock = internalMutation({ export const updateConnectionStatus = internalMutation({ args: { serverName: v.string(), - status: v.string(), // 'connected' | 'failed' | 'disconnected' + status: v.union( + v.literal('connected'), + v.literal('failed'), + v.literal('disconnected') + ), error: v.optional(v.string()) - }, - handler: async (ctx, args) => { + }, handler: async (ctx, args) => { const existing = await ctx.db .query('mcpConnections') .withIndex('by_server', q => q.eq('serverName', args.serverName)) diff --git a/src/components/AgenticChat.tsx b/src/components/AgenticChat.tsx index bc994b1..0f007ec 100644 --- a/src/components/AgenticChat.tsx +++ b/src/components/AgenticChat.tsx @@ -33,6 +33,19 @@ interface AgenticChatProps { onSearchResults?: (results: SearchResult[]) => void; } +/** + * Interactive chat UI that detects search intent, performs agentic multi-model searches, + * and displays interleaved reasoning, search results, and comparison metrics. + * + * This component: + * - Automatically detects when a user message implies a search and runs an agentic search flow. + * - Renders chat messages, explicit reasoning steps, search results, and an advanced comparison dashboard. + * - Updates internal dashboard state with unified search results and may persist search history via a Convex mutation. + * - Gates interactions until a CSRF token is available. + * + * @param onSearchResults - Optional callback invoked with the array of `SearchResult` when an agentic search completes or falls back to fallback results. + * @returns The AgenticChat React element. + */ export function AgenticChat({ onSearchResults }: AgenticChatProps) { const { token: csrfToken, error: csrfError } = useCsrfToken(); const saveSearch = useMutation(api.searchHistory.saveSearch); @@ -478,4 +491,4 @@ export function AgenticChat({ onSearchResults }: AgenticChatProps) { ); -} +} \ No newline at end of file diff --git a/src/components/ComparisonDashboard.tsx b/src/components/ComparisonDashboard.tsx index 02a715a..a8c4615 100644 --- a/src/components/ComparisonDashboard.tsx +++ b/src/components/ComparisonDashboard.tsx @@ -314,7 +314,11 @@ function ModelOutputCard({ response }: { response: ModelResponse }) { } /** - * Reasoning Step Card Component + * Render a card summarizing a single reasoning step, including its type, validation status, token count, output preview, confidence, and any validation errors. + * + * @param step - The reasoning step to render. Should include `type`, `output`, `confidence`, `validated`, `validationErrors`, and `tokenCount`. + * @param index - Zero-based index of the step used for display (e.g., "Step 1"). + * @returns A JSX element representing the rendered reasoning step card. */ function ReasoningStepCard({ step, @@ -438,4 +442,4 @@ function ScoreBar({ ); -} +} \ No newline at end of file diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 653d8f3..c22b809 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -13,6 +13,14 @@ import { useState } from "react"; import WorkOSHeader from "./workos-user.tsx"; import { SettingsModal } from "./SettingsModal"; +/** + * Renders the application header with controls and a slide-in left navigation menu. + * + * The header includes a menu button, title link, and a quick settings button that opens the settings modal. + * The left aside is a sliding navigation panel with links for Home, Search History, Compare Searches, Export Datasets, and Settings; selecting a link closes the menu. + * + * @returns The header and left-side navigation menu as JSX elements + */ export default function Header() { const [isOpen, setIsOpen] = useState(false); const [showSettings, setShowSettings] = useState(false); @@ -144,4 +152,4 @@ export default function Header() { ); -} +} \ No newline at end of file diff --git a/src/hooks/useCsrfToken.tsx b/src/hooks/useCsrfToken.tsx index dd81ee6..8bb6e8d 100644 --- a/src/hooks/useCsrfToken.tsx +++ b/src/hooks/useCsrfToken.tsx @@ -1,5 +1,10 @@ import { useEffect, useState } from "react"; +/** + * Provides the CSRF token fetched from the server and any error encountered while retrieving it. + * + * @returns An object containing `token` — the CSRF token string or `null` if not yet available, and `error` — an `Error` instance if retrieval failed, otherwise `null`. + */ export function useCsrfToken() { const [token, setToken] = useState(null); const [error, setError] = useState(null); @@ -12,4 +17,4 @@ export function useCsrfToken() { }, []); return { token, error }; -} +} \ No newline at end of file diff --git a/src/lib/model-storage.ts b/src/lib/model-storage.ts index d912d3a..43ac53f 100644 --- a/src/lib/model-storage.ts +++ b/src/lib/model-storage.ts @@ -29,8 +29,14 @@ interface StoredConfig { } /** - * Save model configuration to localStorage - * API keys are encrypted and stored separately + * Save a model configuration to localStorage and store any API key in encrypted secure storage. + * + * The configuration's API key (if present) is removed from the stored config and saved separately in encrypted secure storage; the saved config will reference the key by an `apiKeyRef`. The saved configuration becomes the active config and the storage timestamp is updated. + * + * @param id - Identifier for the model configuration + * @param config - The model configuration to save; if `config.apiKey` is present it will be stored in encrypted secure storage and not kept directly in localStorage + * @throws Error if an API key is provided but secure storage is unavailable (unencrypted storage of API keys is not allowed) + * @throws Any error encountered while persisting the configuration or secure storage operations */ export async function saveModelConfig( id: string, @@ -70,8 +76,10 @@ export async function saveModelConfig( } /** - * Load specific model configuration from localStorage - * Decrypts API key if present + * Load a saved model configuration by id, including decrypting its API key when present. + * + * @param id - The identifier of the model configuration to load + * @returns The reconstructed `ModelConfig` with a decrypted `apiKey` if available, or `null` if the config is not found or loading fails (for example, when an API key reference exists but secure storage is unavailable) */ export async function loadModelConfig(id: string): Promise { try { @@ -387,4 +395,4 @@ export function isStorageAvailable(): boolean { } catch (error) { return false; } -} +} \ No newline at end of file diff --git a/src/lib/s3-storage.ts b/src/lib/s3-storage.ts index 6ce62da..00fadeb 100644 --- a/src/lib/s3-storage.ts +++ b/src/lib/s3-storage.ts @@ -32,11 +32,13 @@ export interface UploadResult { } /** - * Upload document to S3 - * @param file File buffer or string content - * @param filename Original filename - * @param contentType MIME type - * @returns Upload result with S3 key and URL + * Upload a file to the configured S3 bucket and return its storage details. + * + * @param file - File contents as a `Buffer` or UTF-8 `string` to upload + * @param filename - Original filename used for metadata and to build a sanitized S3 object key + * @param contentType - MIME type to set on the uploaded S3 object + * @returns An object with `key` (S3 object key), `url` (public HTTPS URL assuming the bucket is public), and `size` (bytes) + * @throws If `AWS_ACCESS_KEY_ID` or `AWS_SECRET_ACCESS_KEY` environment variables are not set */ export async function uploadDocument( file: Buffer | string, @@ -108,9 +110,11 @@ export async function getPresignedDownloadUrl( } /** - * Download document from S3 - * @param key S3 object key - * @returns Document content as Buffer + * Retrieve an object from S3 and return its raw contents as a Buffer. + * + * @param key - The S3 object key (path) to download + * @returns A Buffer containing the object's bytes + * @throws Error if the S3 object has no response body */ export async function downloadDocument(key: string): Promise { const command = new GetObjectCommand({ @@ -169,4 +173,4 @@ export const FILE_SIZE_THRESHOLD = 1024 * 1024; // 1MB export function shouldUseS3(fileSize: number): boolean { return fileSize > FILE_SIZE_THRESHOLD && isS3Configured(); -} +} \ No newline at end of file diff --git a/src/routes/comparison.tsx b/src/routes/comparison.tsx index 554b6f9..f2eec2b 100644 --- a/src/routes/comparison.tsx +++ b/src/routes/comparison.tsx @@ -5,6 +5,11 @@ export const Route = createFileRoute("/comparison")({ component: ComparisonPage, }); +/** + * Renders the comparison page with a full-height gradient background and a centered SearchComparisonDashboard. + * + * @returns The rendered JSX element for the comparison page. + */ function ComparisonPage() { return (
@@ -13,4 +18,4 @@ function ComparisonPage() {
); -} +} \ No newline at end of file diff --git a/src/routes/export.tsx b/src/routes/export.tsx index e83c9c2..3c3506d 100644 --- a/src/routes/export.tsx +++ b/src/routes/export.tsx @@ -5,6 +5,11 @@ export const Route = createFileRoute("/export")({ component: DatasetExportPage, }); +/** + * Renders the dataset export page layout with a full-height gradient background and centered dashboard. + * + * @returns The JSX element containing a responsive container and the DatasetExportDashboard component. + */ function DatasetExportPage() { return (
@@ -13,4 +18,4 @@ function DatasetExportPage() {
); -} +} \ No newline at end of file diff --git a/src/routes/history.tsx b/src/routes/history.tsx index 7675b0c..26e34ca 100644 --- a/src/routes/history.tsx +++ b/src/routes/history.tsx @@ -5,6 +5,11 @@ export const Route = createFileRoute("/history")({ component: SearchHistoryPage, }); +/** + * Page component that renders the search history inside a themed, responsive container. + * + * @returns The React element for the history page, containing the SearchHistory component within layout and background styling. + */ function SearchHistoryPage() { return (
@@ -13,4 +18,4 @@ function SearchHistoryPage() {
); -} +} \ No newline at end of file