Comprehensive UI/UX updates including agent card redesign, chat container improvements, note editor enhancements, memory echo notifications, and updated translations for all 15 locales. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
171 lines
6.6 KiB
TypeScript
171 lines
6.6 KiB
TypeScript
'use client'
|
|
|
|
/**
|
|
* Agent Run Log
|
|
* Shows execution history for an agent.
|
|
*/
|
|
|
|
import { useState, useEffect } from 'react'
|
|
import { X, CheckCircle2, XCircle, Loader2, Clock, ChevronDown, Wrench } from 'lucide-react'
|
|
import { formatDistanceToNow } from 'date-fns'
|
|
import { fr } from 'date-fns/locale/fr'
|
|
import { enUS } from 'date-fns/locale/en-US'
|
|
import { useLanguage } from '@/lib/i18n'
|
|
|
|
interface AgentRunLogProps {
|
|
agentId: string
|
|
agentName: string
|
|
onClose: () => void
|
|
}
|
|
|
|
interface Action {
|
|
id: string
|
|
status: string
|
|
result?: string | null
|
|
log?: string | null
|
|
input?: string | null
|
|
toolLog?: string | null
|
|
tokensUsed?: number | null
|
|
createdAt: string | Date
|
|
}
|
|
|
|
interface ToolLogStep {
|
|
step: number
|
|
text?: string
|
|
toolCalls?: Array<{ toolName: string; args: any }>
|
|
toolResults?: Array<{ toolName: string; preview?: string }>
|
|
}
|
|
|
|
const statusKeys: Record<string, string> = {
|
|
success: 'agents.status.success',
|
|
failure: 'agents.status.failure',
|
|
running: 'agents.status.running',
|
|
pending: 'agents.status.pending',
|
|
}
|
|
|
|
export function AgentRunLog({ agentId, agentName, onClose }: AgentRunLogProps) {
|
|
const { t, language } = useLanguage()
|
|
const [actions, setActions] = useState<Action[]>([])
|
|
const [loading, setLoading] = useState(true)
|
|
const dateLocale = language === 'fr' ? fr : enUS
|
|
|
|
useEffect(() => {
|
|
async function load() {
|
|
try {
|
|
const { getAgentActions } = await import('@/app/actions/agent-actions')
|
|
const data = await getAgentActions(agentId)
|
|
setActions(data)
|
|
} catch {
|
|
// Silent fail
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
load()
|
|
}, [agentId])
|
|
|
|
return (
|
|
<div className="fixed inset-0 bg-black/30 flex items-center justify-center z-50 p-4">
|
|
<div className="bg-card rounded-2xl shadow-xl max-w-md w-full max-h-[70vh] overflow-hidden flex flex-col">
|
|
{/* Header */}
|
|
<div className="flex items-center justify-between px-5 py-4 border-b border-border">
|
|
<div>
|
|
<h3 className="font-semibold text-card-foreground">{t('agents.runLog.title')}</h3>
|
|
<p className="text-xs text-muted-foreground">{agentName}</p>
|
|
</div>
|
|
<button onClick={onClose} className="p-1 rounded-md hover:bg-accent">
|
|
<X className="w-5 h-5 text-muted-foreground" />
|
|
</button>
|
|
</div>
|
|
|
|
{/* List */}
|
|
<div className="flex-1 overflow-y-auto p-4 space-y-2">
|
|
{loading && (
|
|
<div className="flex items-center justify-center py-8">
|
|
<Loader2 className="w-5 h-5 animate-spin text-muted-foreground" />
|
|
</div>
|
|
)}
|
|
|
|
{!loading && actions.length === 0 && (
|
|
<p className="text-center text-sm text-muted-foreground py-8">
|
|
{t('agents.runLog.noHistory')}
|
|
</p>
|
|
)}
|
|
|
|
{actions.map(action => {
|
|
let toolSteps: ToolLogStep[] = []
|
|
try {
|
|
toolSteps = action.toolLog ? JSON.parse(action.toolLog) : []
|
|
} catch {}
|
|
|
|
return (
|
|
<div
|
|
key={action.id}
|
|
className={`
|
|
p-3 rounded-lg border
|
|
${action.status === 'success' ? 'bg-green-50/50 dark:bg-green-950/50 border-green-100 dark:border-green-900' : ''}
|
|
${action.status === 'failure' ? 'bg-red-50/50 dark:bg-red-950/50 border-red-100 dark:border-red-900' : ''}
|
|
${action.status === 'running' ? 'bg-blue-50/50 dark:bg-blue-950/50 border-blue-100 dark:border-blue-900' : ''}
|
|
${action.status === 'pending' ? 'bg-muted border-border' : ''}
|
|
`}
|
|
>
|
|
<div className="flex items-start gap-3">
|
|
<div className="mt-0.5">
|
|
{action.status === 'success' && <CheckCircle2 className="w-4 h-4 text-green-500" />}
|
|
{action.status === 'failure' && <XCircle className="w-4 h-4 text-red-500" />}
|
|
{action.status === 'running' && <Loader2 className="w-4 h-4 text-blue-500 animate-spin" />}
|
|
{action.status === 'pending' && <Clock className="w-4 h-4 text-muted-foreground" />}
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-sm font-medium text-foreground">
|
|
{t(statusKeys[action.status] || action.status)}
|
|
</span>
|
|
<span className="text-xs text-muted-foreground">
|
|
{formatDistanceToNow(new Date(action.createdAt), { addSuffix: true, locale: dateLocale })}
|
|
</span>
|
|
</div>
|
|
{action.log && (
|
|
<p className="text-xs text-muted-foreground mt-1 line-clamp-2">{action.log}</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Tool trace */}
|
|
{toolSteps.length > 0 && (
|
|
<details className="mt-2">
|
|
<summary className="flex items-center gap-1.5 text-xs text-primary cursor-pointer hover:text-primary/80 font-medium">
|
|
<Wrench className="w-3 h-3" />
|
|
{t('agents.runLog.toolTrace', { count: toolSteps.length })}
|
|
<ChevronDown className="w-3 h-3" />
|
|
</summary>
|
|
<div className="mt-2 space-y-2 pl-2">
|
|
{toolSteps.map((step, i) => (
|
|
<div key={i} className="text-xs border-l-2 border-primary/30 pl-2 py-1">
|
|
<span className="font-medium text-muted-foreground">{t('agents.runLog.step', { num: step.step })}</span>
|
|
{step.toolCalls && step.toolCalls.length > 0 && (
|
|
<div className="mt-1 space-y-1">
|
|
{step.toolCalls.map((tc, j) => (
|
|
<div key={j} className="bg-muted rounded px-2 py-1">
|
|
<span className="font-mono text-primary">{tc.toolName}</span>
|
|
<span className="text-muted-foreground ml-1">
|
|
{JSON.stringify(tc.args).substring(0, 80)}
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</details>
|
|
)}
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|