diff --git a/web/src/chat/normalizeAgent.ts b/web/src/chat/normalizeAgent.ts index fc63f72d..81301f6f 100644 --- a/web/src/chat/normalizeAgent.ts +++ b/web/src/chat/normalizeAgent.ts @@ -232,6 +232,20 @@ export function normalizeAgentRecord( meta } } + if (data.type === 'system' && data.subtype === 'turn_duration') { + return { + id: messageId, + localId, + createdAt, + role: 'event', + content: { + type: 'turn-duration', + durationMs: asNumber(data.durationMs) ?? 0 + }, + isSidechain: false, + meta + } + } return null } diff --git a/web/src/chat/presentation.ts b/web/src/chat/presentation.ts index 33ed40e7..49491859 100644 --- a/web/src/chat/presentation.ts +++ b/web/src/chat/presentation.ts @@ -7,6 +7,14 @@ export function formatUnixTimestamp(value: number): string { return date.toLocaleString() } +function formatDuration(ms: number): string { + const seconds = ms / 1000 + if (seconds < 60) return `${seconds.toFixed(1)}s` + const mins = Math.floor(seconds / 60) + const secs = Math.round(seconds % 60) + return `${mins}m ${secs}s` +} + export type EventPresentation = { icon: string | null text: string @@ -46,6 +54,10 @@ export function getEventPresentation(event: AgentEvent): EventPresentation { if (event.type === 'message') { return { icon: null, text: typeof event.message === 'string' ? event.message : 'Message' } } + if (event.type === 'turn-duration') { + const ms = typeof event.durationMs === 'number' ? event.durationMs : 0 + return { icon: '⏱️', text: `Turn: ${formatDuration(ms)}` } + } try { return { icon: null, text: JSON.stringify(event) } } catch { diff --git a/web/src/chat/types.ts b/web/src/chat/types.ts index 51435514..84d204b4 100644 --- a/web/src/chat/types.ts +++ b/web/src/chat/types.ts @@ -15,6 +15,7 @@ export type AgentEvent = | { type: 'limit-reached'; endsAt: number } | { type: 'ready' } | { type: 'api-error'; retryAttempt: number; maxRetries: number; error: unknown } + | { type: 'turn-duration'; durationMs: number } | ({ type: string } & Record) export type ToolResultPermission = {