diff --git a/frontend/src/components/NormalizedConversation/DisplayConversationEntry.tsx b/frontend/src/components/NormalizedConversation/DisplayConversationEntry.tsx index 8aa86215..2b57cbb9 100644 --- a/frontend/src/components/NormalizedConversation/DisplayConversationEntry.tsx +++ b/frontend/src/components/NormalizedConversation/DisplayConversationEntry.tsx @@ -9,7 +9,6 @@ import { } from 'shared/types.ts'; import type { ProcessStartPayload } from '@/types/logs'; import FileChangeRenderer from './FileChangeRenderer'; -import { renderJson } from './ToolDetails'; import { useExpandable } from '@/stores/useExpandableStore'; import { AlertCircle, @@ -41,6 +40,11 @@ type Props = { }; type FileEditAction = Extract; +type JsonValue = any; + +const renderJson = (v: JsonValue) => ( +
{JSON.stringify(v, null, 2)}
+); const getEntryIcon = (entryType: NormalizedEntryType) => { const iconSize = 'h-3 w-3'; diff --git a/frontend/src/components/NormalizedConversation/ToolDetails.tsx b/frontend/src/components/NormalizedConversation/ToolDetails.tsx deleted file mode 100644 index a598bfb0..00000000 --- a/frontend/src/components/NormalizedConversation/ToolDetails.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import MarkdownRenderer from '@/components/ui/markdown-renderer.tsx'; -import RawLogText from '@/components/common/RawLogText'; -import { Braces, FileText } from 'lucide-react'; - -type JsonValue = any; - -type ToolResult = { - type: 'markdown' | 'json'; - value: JsonValue; -}; - -type Props = { - arguments?: JsonValue | null; - result?: ToolResult | null; - commandOutput?: string | null; // presence => command mode - commandExit?: - | { type: 'success'; success: boolean } - | { type: 'exit_code'; code: number } - | null; -}; - -export const renderJson = (v: JsonValue) => ( -
{JSON.stringify(v, null, 2)}
-); - -export default function ToolDetails({ - arguments: args, - result, - commandOutput, -}: Props) { - const isCommandMode = commandOutput !== undefined; - - return ( -
- {args && ( -
- {!isCommandMode ? ( - <> -
- - Arguments -
- {renderJson(args)} - - ) : ( - <> - - - )} -
- )} - - {result && !isCommandMode && ( -
-
- {result.type === 'json' ? ( - - ) : ( - - )} - Result -
-
- {result.type === 'markdown' ? ( - - ) : ( - renderJson(result.value) - )} -
-
- )} - - {isCommandMode && ( -
-
- -
-
- )} -
- ); -} diff --git a/frontend/src/components/common/ProfileVariantBadge.tsx b/frontend/src/components/common/ProfileVariantBadge.tsx index 0f067a8c..12b8ebea 100644 --- a/frontend/src/components/common/ProfileVariantBadge.tsx +++ b/frontend/src/components/common/ProfileVariantBadge.tsx @@ -26,5 +26,3 @@ export function ProfileVariantBadge({ ); } - -export default ProfileVariantBadge; diff --git a/frontend/src/components/dialogs/global/PrivacyOptInDialog.tsx b/frontend/src/components/dialogs/global/PrivacyOptInDialog.tsx index 8dc88c2a..d97b3056 100644 --- a/frontend/src/components/dialogs/global/PrivacyOptInDialog.tsx +++ b/frontend/src/components/dialogs/global/PrivacyOptInDialog.tsx @@ -115,4 +115,3 @@ const PrivacyOptInDialog = NiceModal.create(() => { }); export { PrivacyOptInDialog }; -export default PrivacyOptInDialog; diff --git a/frontend/src/components/dialogs/tasks/CreatePRDialog.tsx b/frontend/src/components/dialogs/tasks/CreatePRDialog.tsx index 6bf4dd8f..70d16baf 100644 --- a/frontend/src/components/dialogs/tasks/CreatePRDialog.tsx +++ b/frontend/src/components/dialogs/tasks/CreatePRDialog.tsx @@ -196,4 +196,3 @@ const CreatePrDialog = NiceModal.create(() => { }); export { CreatePrDialog as CreatePRDialog }; -export default CreatePrDialog; diff --git a/frontend/src/components/dialogs/tasks/DeleteTaskConfirmationDialog.tsx b/frontend/src/components/dialogs/tasks/DeleteTaskConfirmationDialog.tsx index 789e65cf..f92a3dca 100644 --- a/frontend/src/components/dialogs/tasks/DeleteTaskConfirmationDialog.tsx +++ b/frontend/src/components/dialogs/tasks/DeleteTaskConfirmationDialog.tsx @@ -94,4 +94,3 @@ const DeleteTaskConfirmationDialog = }); export { DeleteTaskConfirmationDialog }; -export default DeleteTaskConfirmationDialog; diff --git a/frontend/src/components/settings/TaskSettings.tsx b/frontend/src/components/settings/TaskSettings.tsx deleted file mode 100644 index 99f7b7b7..00000000 --- a/frontend/src/components/settings/TaskSettings.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { Label } from '@/components/ui/label'; -import BranchSelector from '@/components/tasks/BranchSelector'; -import ExecutorProfileSelector from './ExecutorProfileSelector'; -import type { - GitBranch, - ExecutorConfig, - ExecutorProfileId, -} from 'shared/types'; - -type Props = { - // Branch selector props - branches?: GitBranch[]; - selectedBranch?: string | null; - onBranchSelect?: (branch: string) => void; - showBranchSelector?: boolean; - branchSelectorProps?: { - placeholder?: string; - className?: string; - excludeCurrentBranch?: boolean; - }; - - // Executor profile selector props - profiles?: Record | null; - selectedProfile?: ExecutorProfileId | null; - onProfileSelect?: (profile: ExecutorProfileId) => void; - showExecutorSelector?: boolean; - executorSelectorProps?: { - showLabel?: boolean; - showVariantSelector?: boolean; - className?: string; - }; - - // Common props - disabled?: boolean; - className?: string; -}; - -function TaskSettings({ - // Branch selector props - branches = [], - selectedBranch, - onBranchSelect, - showBranchSelector = true, - branchSelectorProps = {}, - - // Executor profile selector props - profiles, - selectedProfile, - onProfileSelect, - showExecutorSelector = true, - executorSelectorProps = {}, - - // Common props - disabled = false, - className = '', -}: Props) { - return ( -
- {/* Executor Profile Selector */} - {showExecutorSelector && - profiles && - selectedProfile && - onProfileSelect && ( - - )} - - {/* Branch Selector */} - {showBranchSelector && - branches.length > 0 && - selectedBranch !== undefined && - onBranchSelect && ( -
- - -
- )} -
- ); -} - -export default TaskSettings; diff --git a/frontend/src/components/settings/index.ts b/frontend/src/components/settings/index.ts index 4db06bdf..c029b6d5 100644 --- a/frontend/src/components/settings/index.ts +++ b/frontend/src/components/settings/index.ts @@ -1,2 +1 @@ export { default as ExecutorProfileSelector } from './ExecutorProfileSelector'; -export { default as TaskSettings } from './TaskSettings'; diff --git a/frontend/src/components/tasks/TodoPanel.tsx b/frontend/src/components/tasks/TodoPanel.tsx index b5474cf5..1b07195f 100644 --- a/frontend/src/components/tasks/TodoPanel.tsx +++ b/frontend/src/components/tasks/TodoPanel.tsx @@ -12,7 +12,7 @@ function getStatusIcon(status?: string) { return ; } -export function TodoPanel() { +function TodoPanel() { const { entries } = useEntries(); const { todos } = usePinnedTodos(entries); diff --git a/frontend/src/constants/processes.ts b/frontend/src/constants/processes.ts index e58a96c7..b0928623 100644 --- a/frontend/src/constants/processes.ts +++ b/frontend/src/constants/processes.ts @@ -1,8 +1,4 @@ -import type { - ExecutionProcessRunReason, - ExecutionProcessStatus, - ExecutionProcess, -} from 'shared/types'; +import type { ExecutionProcessRunReason } from 'shared/types'; // Process run reasons export const PROCESS_RUN_REASONS = { @@ -12,51 +8,14 @@ export const PROCESS_RUN_REASONS = { DEV_SERVER: 'devserver' as ExecutionProcessRunReason, } as const; -// Process statuses -export const PROCESS_STATUSES = { - RUNNING: 'running' as ExecutionProcessStatus, - COMPLETED: 'completed' as ExecutionProcessStatus, - FAILED: 'failed' as ExecutionProcessStatus, - KILLED: 'killed' as ExecutionProcessStatus, -} as const; - -// Helper functions -export const isAutoCollapsibleProcess = ( - runReason: ExecutionProcessRunReason -): boolean => { - return ( - runReason === PROCESS_RUN_REASONS.SETUP_SCRIPT || - runReason === PROCESS_RUN_REASONS.CLEANUP_SCRIPT - ); -}; - export const isCodingAgent = ( runReason: ExecutionProcessRunReason ): boolean => { return runReason === PROCESS_RUN_REASONS.CODING_AGENT; }; -export const isProcessCompleted = (status: ExecutionProcessStatus): boolean => { - return ( - status === PROCESS_STATUSES.COMPLETED || status === PROCESS_STATUSES.FAILED - ); -}; - export const shouldShowInLogs = ( runReason: ExecutionProcessRunReason ): boolean => { return runReason !== PROCESS_RUN_REASONS.DEV_SERVER; }; - -export const getLatestCodingAgent = ( - processes: ExecutionProcess[] -): string | null => { - const codingAgents = processes.filter((p) => isCodingAgent(p.run_reason)); - if (codingAgents.length === 0) return null; - - return codingAgents.sort((a, b) => - a.started_at === b.started_at - ? a.id.localeCompare(b.id) // tie-break for same timestamp - : new Date(b.started_at).getTime() - new Date(a.started_at).getTime() - )[0].id; -}; diff --git a/frontend/src/hooks/index.ts b/frontend/src/hooks/index.ts index 3db6ebd5..3637e49a 100644 --- a/frontend/src/hooks/index.ts +++ b/frontend/src/hooks/index.ts @@ -3,8 +3,6 @@ export { useAttemptExecution } from './useAttemptExecution'; export { useOpenInEditor } from './useOpenInEditor'; export { useDevServer } from './useDevServer'; export { useRebase } from './useRebase'; -export { useCreatePR } from './useCreatePR'; export { useMerge } from './useMerge'; export { usePush } from './usePush'; -export { useProjectBranches } from './useProjectBranches'; export { useKeyboardShortcut } from './useKeyboardShortcut'; diff --git a/frontend/src/hooks/useCreatePR.ts b/frontend/src/hooks/useCreatePR.ts deleted file mode 100644 index e9691e05..00000000 --- a/frontend/src/hooks/useCreatePR.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { attemptsApi, type Result } from '@/lib/api'; -import type { CreateGitHubPrRequest, GitHubServiceError } from 'shared/types'; - -export function useCreatePR( - attemptId: string | undefined, - onSuccess?: (prUrl?: string) => void, - onError?: (err: unknown) => void -) { - const queryClient = useQueryClient(); - - return useMutation< - Result, - Error, - CreateGitHubPrRequest - >({ - mutationFn: async (prData: CreateGitHubPrRequest) => { - if (!attemptId) - return { success: false, error: undefined, message: 'No attempt ID' }; - return attemptsApi.createPR(attemptId, prData); - }, - onSuccess: (result) => { - if (result.success) { - queryClient.invalidateQueries({ - queryKey: ['branchStatus', attemptId], - }); - onSuccess?.(result.data); - } else { - throw ( - result.error || new Error(result.message || 'Failed to create PR') - ); - } - }, - onError: (err) => { - console.error('Failed to create PR:', err); - onError?.(err); - }, - }); -} diff --git a/frontend/src/hooks/useEventSourceManager.ts b/frontend/src/hooks/useEventSourceManager.ts deleted file mode 100644 index 9db04ded..00000000 --- a/frontend/src/hooks/useEventSourceManager.ts +++ /dev/null @@ -1,186 +0,0 @@ -import { useEffect, useState, useRef } from 'react'; -import { applyPatch } from 'rfc6902'; -import type { ExecutionProcess } from 'shared/types'; -import type { ProcessStartPayload } from '@/types/logs'; - -interface ProcessData { - [processId: string]: any; -} - -interface UseEventSourceManagerParams { - processes: ExecutionProcess[]; - enabled: boolean; - getEndpoint: (process: ExecutionProcess) => string; - initialData?: any; -} - -interface UseEventSourceManagerResult { - processData: ProcessData; - isConnected: boolean; - error: string | null; -} - -export const useEventSourceManager = ({ - processes, - enabled, - getEndpoint, - initialData = null, -}: UseEventSourceManagerParams): UseEventSourceManagerResult => { - const [processData, setProcessData] = useState({}); - const [isConnected, setIsConnected] = useState(false); - const [error, setError] = useState(null); - const eventSourcesRef = useRef>(new Map()); - const processDataRef = useRef({}); - const processedEntriesRef = useRef>>(new Map()); - const processesRef = useRef([]); - const enabledRef = useRef(enabled); - const getEndpointRef = useRef(getEndpoint); - const retryCountsRef = useRef>(new Map()); - const retryTimersRef = useRef>>( - new Map() - ); - - // Keep latest values in refs for retry handlers - useEffect(() => { - processesRef.current = processes; - }, [processes]); - useEffect(() => { - enabledRef.current = enabled; - }, [enabled]); - useEffect(() => { - getEndpointRef.current = getEndpoint; - }, [getEndpoint]); - - useEffect(() => { - if (!enabled || !processes.length) { - // Close all connections and reset state - eventSourcesRef.current.forEach((es) => es.close()); - eventSourcesRef.current.clear(); - setProcessData({}); - setIsConnected(false); - setError(null); - processDataRef.current = {}; - processedEntriesRef.current.clear(); - return; - } - - const currentIds = new Set(processes.map((p) => p.id)); - - // Remove old connections - eventSourcesRef.current.forEach((es, id) => { - if (!currentIds.has(id)) { - es.close(); - eventSourcesRef.current.delete(id); - delete processDataRef.current[id]; - processedEntriesRef.current.delete(id); - } - }); - - // Helper to open an EventSource with auto-retry on transient failures (e.g., race before store is ready) - const openEventSource = (process: ExecutionProcess) => { - // If disabled or process no longer present, don't connect - if (!enabledRef.current) return; - if (!processesRef.current.find((p) => p.id === process.id)) return; - - const endpoint = getEndpointRef.current(process); - - // Reinitialize process data on each (re)connect to avoid duplicating history - processDataRef.current[process.id] = initialData - ? structuredClone(initialData) - : { entries: [] }; - processedEntriesRef.current.delete(process.id); - - // Inject process start marker as the first entry (client-side only) - const processStartPayload: ProcessStartPayload = { - processId: process.id, - runReason: process.run_reason, - startedAt: process.started_at, - status: process.status, - }; - const processStartEntry = { - type: 'PROCESS_START' as const, - content: processStartPayload, - }; - processDataRef.current[process.id].entries.push(processStartEntry); - - const eventSource = new EventSource(endpoint); - - eventSource.onopen = () => { - setError(null); - setIsConnected(true); - retryCountsRef.current.set(process.id, 0); - }; - - eventSource.addEventListener('json_patch', (event) => { - try { - const patches = JSON.parse(event.data); - - if (!processedEntriesRef.current.has(process.id)) { - processedEntriesRef.current.set(process.id, new Set()); - } - applyPatch(processDataRef.current[process.id], patches); - setProcessData({ ...processDataRef.current }); - } catch (err) { - console.error('Failed to apply JSON patch:', err); - setError('Failed to process log update'); - } - }); - - eventSource.addEventListener('finished', () => { - eventSource.close(); - eventSourcesRef.current.delete(process.id); - retryCountsRef.current.delete(process.id); - const t = retryTimersRef.current.get(process.id); - if (t) { - clearTimeout(t); - retryTimersRef.current.delete(process.id); - } - setIsConnected(eventSourcesRef.current.size > 0); - }); - - eventSource.onerror = () => { - setError('Connection failed'); - eventSource.close(); - eventSourcesRef.current.delete(process.id); - - const nextAttempt = (retryCountsRef.current.get(process.id) || 0) + 1; - retryCountsRef.current.set(process.id, nextAttempt); - - const maxAttempts = 6; - if ( - nextAttempt <= maxAttempts && - enabledRef.current && - processesRef.current.find((p) => p.id === process.id) - ) { - const delay = Math.min(1500, 250 * 2 ** (nextAttempt - 1)); - const timer = setTimeout(() => openEventSource(process), delay); - const prevTimer = retryTimersRef.current.get(process.id); - if (prevTimer) clearTimeout(prevTimer); - retryTimersRef.current.set(process.id, timer); - } else { - setIsConnected(eventSourcesRef.current.size > 0); - } - }; - - eventSourcesRef.current.set(process.id, eventSource); - }; - - // Add new connections - processes.forEach((process) => { - if (eventSourcesRef.current.has(process.id)) return; - openEventSource(process); - }); - - setIsConnected(eventSourcesRef.current.size > 0); - - return () => { - // Cleanup all event sources and any pending retry timers - eventSourcesRef.current.forEach((es) => es.close()); - eventSourcesRef.current.clear(); - retryTimersRef.current.forEach((t) => clearTimeout(t)); - retryTimersRef.current.clear(); - }; - }, [processes, enabled, getEndpoint, initialData]); - - return { processData, isConnected, error }; -}; diff --git a/frontend/src/hooks/useProjectBranches.ts b/frontend/src/hooks/useProjectBranches.ts deleted file mode 100644 index e6543ca4..00000000 --- a/frontend/src/hooks/useProjectBranches.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import { projectsApi } from '@/lib/api'; - -export function useProjectBranches(projectId?: string) { - return useQuery({ - queryKey: ['projectBranches', projectId], - queryFn: () => projectsApi.getBranches(projectId!), - enabled: !!projectId, - staleTime: 30_000, - refetchOnWindowFocus: false, - }); -} diff --git a/frontend/src/i18n/languages.ts b/frontend/src/i18n/languages.ts index 3459b50d..e8836024 100644 --- a/frontend/src/i18n/languages.ts +++ b/frontend/src/i18n/languages.ts @@ -12,7 +12,7 @@ export const UI_TO_I18N = { ES: 'es', } as const; -export const SUPPORTED_UI_LANGUAGES = ['BROWSER', 'EN', 'JA', 'ES'] as const; +const SUPPORTED_UI_LANGUAGES = ['BROWSER', 'EN', 'JA', 'ES'] as const; export const SUPPORTED_I18N_CODES = Object.values(UI_TO_I18N); const FALLBACK_ENDONYMS = { diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 7cc9fe36..34b0769c 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -52,7 +52,7 @@ export type { UpdateFollowUpDraftRequest, } from 'shared/types'; -export class ApiError extends Error { +class ApiError extends Error { public status?: number; public error_data?: E; @@ -69,7 +69,7 @@ export class ApiError extends Error { } } -export const makeRequest = async (url: string, options: RequestInit = {}) => { +const makeRequest = async (url: string, options: RequestInit = {}) => { const headers = { 'Content-Type': 'application/json', ...(options.headers || {}), @@ -641,17 +641,6 @@ export const githubApi = { const response = await makeRequest(`/api/github/repositories?page=${page}`); return handleApiResponse(response); }, - // createProjectFromRepository: async ( - // data: CreateProjectFromGitHub - // ): Promise => { - // const response = await makeRequest('/api/projects/from-github', { - // method: 'POST', - // body: JSON.stringify(data, (_key, value) => - // typeof value === 'bigint' ? Number(value) : value - // ), - // }); - // return handleApiResponse(response); - // }, }; // Task Templates APIs diff --git a/frontend/src/lib/conflicts.ts b/frontend/src/lib/conflicts.ts index cf82879b..7ca6ed51 100644 --- a/frontend/src/lib/conflicts.ts +++ b/frontend/src/lib/conflicts.ts @@ -14,7 +14,7 @@ export function displayConflictOpLabel(op?: ConflictOp | null): string { } } -export function formatConflictHeader( +function formatConflictHeader( op: ConflictOp | null | undefined, sourceBranch: string, baseBranch?: string diff --git a/frontend/src/stores/useDiffViewStore.ts b/frontend/src/stores/useDiffViewStore.ts index 31cf54c2..f4572a21 100644 --- a/frontend/src/stores/useDiffViewStore.ts +++ b/frontend/src/stores/useDiffViewStore.ts @@ -16,4 +16,3 @@ export const useDiffViewStore = create((set) => ({ })); export const useDiffViewMode = () => useDiffViewStore((s) => s.mode); -export const useToggleDiffViewMode = () => useDiffViewStore((s) => s.toggle); diff --git a/frontend/src/stores/useExpandableStore.ts b/frontend/src/stores/useExpandableStore.ts index f1d58e75..836cfd72 100644 --- a/frontend/src/stores/useExpandableStore.ts +++ b/frontend/src/stores/useExpandableStore.ts @@ -7,7 +7,7 @@ type State = { clear: () => void; }; -export const useExpandableStore = create((set) => ({ +const useExpandableStore = create((set) => ({ expanded: {}, setKey: (key, value) => set((s) => diff --git a/frontend/src/stores/useTaskDetailsUiStore.ts b/frontend/src/stores/useTaskDetailsUiStore.ts index b7520ce4..69214bf1 100644 --- a/frontend/src/stores/useTaskDetailsUiStore.ts +++ b/frontend/src/stores/useTaskDetailsUiStore.ts @@ -26,7 +26,7 @@ const defaultUiState: TaskUiState = { fileToDelete: null, }; -export const useTaskDetailsUiStore = create((set, get) => ({ +const useTaskDetailsUiStore = create((set, get) => ({ ui: {}, getUiState: (taskId: string) => { @@ -59,17 +59,6 @@ export const useTaskDetailsUiStore = create((set, get) => ({ }, })); -// Convenience hooks for specific UI state -export const useTaskLoading = (taskId: string) => { - const { getUiState, setUiState } = useTaskDetailsUiStore(); - const { loading } = getUiState(taskId); - - return { - loading, - setLoading: (value: boolean) => setUiState(taskId, { loading: value }), - }; -}; - export const useTaskStopping = (taskId: string) => { const { getUiState, setUiState } = useTaskDetailsUiStore(); const { isStopping } = getUiState(taskId); @@ -80,17 +69,3 @@ export const useTaskStopping = (taskId: string) => { setUiState(taskId, { isStopping: value }), }; }; - -export const useTaskDeletingFiles = (taskId: string) => { - const { getUiState, setUiState } = useTaskDetailsUiStore(); - const { deletingFiles, fileToDelete } = getUiState(taskId); - - return { - deletingFiles, - fileToDelete, - setFileToDelete: (value: string | null) => - setUiState(taskId, { fileToDelete: value }), - setDeletingFiles: (value: Set) => - setUiState(taskId, { deletingFiles: value }), - }; -};