import { useMutation, useQueryClient } from '@tanstack/react-query'
import { createFileRoute, useNavigate } from '@tanstack/react-router'
import { AlertTriangle, ExternalLink, Skull } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { AgentBudgetBar } from '@/components/agent/agent-budget-bar'
import { AgentHeader } from '@/components/agent/agent-header'
import { AgentLogs as AgentLogsComponent } from '@/components/agent/agent-logs'
import { ExecutionTrace } from '@/components/agent/execution-trace'
import { ErrorState } from '@/components/shared/error-state'
import { LoadingCards } from '@/components/shared/loading'
import { Button } from '@/components/ui/button'
import { Card, CardContent } from '@/components/ui/card'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { useAgentDetail, useAgentLogs, useAgentTrace } from '@/hooks/use-agent-trace'
import { api } from '@/lib/api-client'
import { defaultAgentSearch } from '@/routes/agents'
export const Route = createFileRoute('/agents/$agentId')({
component: AgentDetailPage,
})
function AgentDetailPage() {
const { t } = useTranslation()
const { agentId } = Route.useParams()
const navigate = useNavigate()
const queryClient = useQueryClient()
const { data: agent, isLoading, isError, refetch } = useAgentDetail(agentId)
const { data: trace, isLoading: traceLoading } = useAgentTrace(agentId)
const { data: logs } = useAgentLogs(agentId)
const killMutation = useMutation({
mutationFn: () => api.post(`/api/agents/${agentId}/kill`),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['agents'] })
queryClient.invalidateQueries({ queryKey: ['agents', 'detail', agentId] })
navigate({ to: '/agents', search: { ...defaultAgentSearch } })
},
})
if (isLoading) return <LoadingCards count={3} />
if (isError) return <ErrorState onRetry={() => refetch()} />
if (!agent) return <p className="text-muted-foreground">{t('agents.notFound')}</p>
return (
<div className="space-y-6">
<AgentHeader
agent={agent}
onBack={() => navigate({ to: '/agents', search: { ...defaultAgentSearch } })}
>
<Button
variant="destructive"
size="sm"
onClick={() => {
if (confirm(t('agents.terminateConfirm'))) killMutation.mutate()
}}
disabled={killMutation.isPending || agent.status?.toLowerCase() === 'stopped'}
>
<Skull className="h-4 w-4 mr-1" /> {t('agents.terminate')}
</Button>
</AgentHeader>
{/* Error message */}
{agent.error && (
<Card className="border-destructive/50 bg-destructive/5">
<CardContent className="pt-4">
<div className="flex items-start gap-2 text-sm text-destructive">
<AlertTriangle className="h-4 w-4 mt-0.5 shrink-0" />
<div>
<p className="font-medium">{t('agents.error', 'Execution Error')}</p>
<p className="text-xs font-mono mt-1 whitespace-pre-wrap break-all">
{agent.error}
</p>
</div>
</div>
</CardContent>
</Card>
)}
{/* Meta info */}
<Card>
<CardContent className="pt-4 space-y-3">
<div className="grid gap-2 md:grid-cols-3 text-sm">
{agent.seed_id && (
<div className="flex items-center gap-1">
<span className="text-muted-foreground">{t('agents.seed')}:</span>
<Button
variant="link"
className="h-auto p-0 text-xs"
onClick={() =>
navigate({
to: '/seeds/$seedId',
params: { seedId: agent.seed_id! },
})
}
>
{agent.seed_id.slice(0, 8)}...
</Button>
</div>
)}
{agent.project_id && (
<div className="flex items-center gap-1">
<span className="text-muted-foreground">{t('agents.project', 'Project')}:</span>
<span className="text-xs font-mono">{agent.project_id.slice(0, 8)}...</span>
</div>
)}
{(agent as { session_id?: string | null }).session_id && (
<div className="flex items-center gap-1">
<span className="text-muted-foreground">{t('agents.session', 'Session')}:</span>
<Button
variant="link"
className="h-auto p-0 text-xs"
onClick={() =>
navigate({
to: '/sessions/$sessionId',
params: { sessionId: (agent as { session_id: string }).session_id },
})
}
>
{(agent as { session_id: string }).session_id.slice(0, 12)}...
</Button>
</div>
)}
<div className="flex items-center gap-1">
<span className="text-muted-foreground">{t('agents.created')}:</span>
<span>{new Date(agent.created_at).toLocaleString()}</span>
</div>
{agent.started_at && (
<div className="flex items-center gap-1">
<span className="text-muted-foreground">{t('agents.startedAt', 'Started')}:</span>
<span>{new Date(agent.started_at).toLocaleString()}</span>
</div>
)}
{agent.completed_at && (
<div className="flex items-center gap-1">
<span className="text-muted-foreground">
{t('agents.completedAt', 'Completed')}:
</span>
<span>{new Date(agent.completed_at).toLocaleString()}</span>
</div>
)}
{agent.steps_completed > 0 && (
<div className="flex items-center gap-1">
<span className="text-muted-foreground">
{t('agents.stepsCompleted', 'Steps')}:
</span>
<span>
{agent.steps_completed}
{agent.steps_total ? ` / ${agent.steps_total}` : ''}
</span>
</div>
)}
{agent.model_id && (
<div className="flex items-center gap-1">
<span className="text-muted-foreground">{t('agents.model', 'Model')}:</span>
<span className="text-xs font-mono">{agent.model_id}</span>
</div>
)}
{agent.tokens_used > 0 && (
<div className="flex items-center gap-1">
<span className="text-muted-foreground">{t('agents.tokens', 'Tokens')}:</span>
<span>{agent.tokens_used.toLocaleString()}</span>
</div>
)}
{agent.cost_usd > 0 && (
<div className="flex items-center gap-1">
<span className="text-muted-foreground">{t('agents.cost', 'Cost')}:</span>
<span>${agent.cost_usd.toFixed(4)}</span>
</div>
)}
</div>
<AgentBudgetBar agent={agent} />
</CardContent>
</Card>
{/* Tabs */}
<Tabs defaultValue="trace" className="space-y-4">
<TabsList>
<TabsTrigger value="trace">{t('agents.trace')}</TabsTrigger>
<TabsTrigger value="logs">{t('agents.logs')}</TabsTrigger>
</TabsList>
<TabsContent value="trace">
<div className="flex items-center justify-between mb-2">
<h3 className="text-lg font-semibold">{t('agents.executionTrace')}</h3>
<Button
variant="ghost"
size="sm"
onClick={() =>
navigate({
to: '/agents/$agentId/trace',
params: { agentId },
})
}
>
<ExternalLink className="h-4 w-4 mr-1" /> {t('agents.traceFullscreen')}
</Button>
</div>
<ExecutionTrace trace={trace} isLoading={traceLoading} />
</TabsContent>
<TabsContent value="logs">
<AgentLogsComponent logs={logs} />
</TabsContent>
</Tabs>
</div>
)
}