diff --git a/ui/web/src/lib/format-user-label.ts b/ui/web/src/lib/format-user-label.ts index 704878ae..c63750fe 100644 --- a/ui/web/src/lib/format-user-label.ts +++ b/ui/web/src/lib/format-user-label.ts @@ -25,7 +25,11 @@ export function formatUserLabel(userId: string, resolve?: Resolver): string { } } - // Fallback: prefix numeric IDs with #, string IDs with @ - if (/^\d+$/.test(userId)) return `#${userId}`; + // Fallback: prefix numeric IDs with # + if (/^-?\d+$/.test(userId)) return `#${userId}`; + + // Truncate long opaque IDs (e.g. oc_295eb80d325c976cbeb4a779e2010518) + if (userId.length > 20) return `${userId.slice(0, 10)}…${userId.slice(-4)}`; + return userId; } diff --git a/ui/web/src/pages/traces/traces-page.tsx b/ui/web/src/pages/traces/traces-page.tsx index f6364607..8917f3a7 100644 --- a/ui/web/src/pages/traces/traces-page.tsx +++ b/ui/web/src/pages/traces/traces-page.tsx @@ -1,6 +1,6 @@ import { useState, useCallback, useMemo } from "react"; import { useTranslation } from "react-i18next"; -import { Activity, GitFork, RefreshCw, Square, Bot, User, Users, Clock, Network, Globe } from "lucide-react"; +import { Activity, GitFork, RefreshCw, Square, Bot, User, Users, Clock, Network, Globe, CheckCircle2, XCircle, Loader2, CircleDot, CircleDashed } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { @@ -28,6 +28,12 @@ import { useWs } from "@/hooks/use-ws"; import { Methods } from "@/api/protocol"; import { toast } from "@/stores/use-toast-store"; +/** Strip media placeholder tags like from preview text */ +function cleanPreview(text: string): string { + if (!text) return text; + return text.replace(//g, "[media]"); +} + /** Parse session_key to extract source type: Direct, Group, Cron, Team, WS */ function parseSourceType(sessionKey: string): { type: string; topic?: string } { if (!sessionKey) return { type: "unknown" }; @@ -168,15 +174,14 @@ export function TracesPage() { /> ) : (
- +
- - + - - + + @@ -192,14 +197,20 @@ export function TracesPage() { className="cursor-pointer border-b last:border-0 hover:bg-muted/30" onClick={() => setSelectedTraceId(trace.id)} > - - - - - ); @@ -280,15 +289,18 @@ export function TracesPage() { ); } -function StatusBadge({ status }: { status: string }) { - const variant = - status === "ok" || status === "success" || status === "completed" - ? "success" - : status === "error" || status === "failed" - ? "destructive" - : status === "running" || status === "pending" - ? "info" - : "secondary"; - - return {status || "unknown"}; +function StatusIcon({ status }: { status: string }) { + if (status === "ok" || status === "success" || status === "completed") { + return ; + } + if (status === "error" || status === "failed") { + return ; + } + if (status === "running") { + return ; + } + if (status === "pending") { + return ; + } + return ; }
{t("columns.name")}{t("columns.status")}{t("columns.duration")} {t("columns.tokens")}{t("columns.spans")}{t("columns.time")}{t("columns.spans")}{t("columns.time")}
-
+
+
{trace.parent_trace_id && ( )} {agentName || trace.name || t("unnamed")} - · - {userLabel} + {userLabel && ( + <> + · + {userLabel} + + )} +
+
{t(`source.${source.type}`)} @@ -210,16 +221,16 @@ export function TracesPage() { {trace.channel} )} + {trace.input_preview && ( + + {cleanPreview(trace.input_preview)} + + )}
- {trace.input_preview && ( -

- {trace.input_preview} -

- )}
-
- +
+
+ {(trace.status === "running") && (
- {formatDuration(trace.duration_ms || computeDurationMs(trace.start_time, trace.end_time))} -
{formatTokens(trace.total_input_tokens)} / {formatTokens(trace.total_output_tokens)}
{(trace.metadata?.total_cache_read_tokens ?? 0) > 0 && ( @@ -244,11 +252,12 @@ export function TracesPage() { )}
+ {trace.span_count} - {formatDate(trace.start_time, tz)} + +
{formatDate(trace.start_time, tz)}
+
{formatDuration(trace.duration_ms || computeDurationMs(trace.start_time, trace.end_time))}