Agent state

ThoughtBubble

Chain-of-thought display — visually distinct from a regular chat bubble with a dashed border, muted tint, and italic body. Pair with a streaming loop: toggle streaming while tokens arrive, set durationMs once thinking concludes. Five roles (planning, reflecting, reasoning, deciding, critiquing) drive the header icon and label.

Sourcesrc/components/ai/thought-bubble.tsx

Planning with duration

role='planning' picks the compass icon. durationMs renders a tabular-nums timer in the header.

Thought · Planning2.3s
I need to search for flights first, then filter by price under $1000, then sort by total travel time.

All five roles

Each role maps to its own icon — reflecting spins while in-flight. Keep the copy short so the header tag carries the intent.

Thought · Reasoning
The RFP weighs pricing transparency 2× against compliance, so Snowflake edges out BigQuery.
Thought · Deciding
Going with the direct flight — the 10pm arrival is worth the $67 premium.
Thought · Critiquing
My first draft leaked the vendor's internal pricing. Rewriting without the leak.

Streaming in-flight

role='reflecting' spins its loader; streaming appends a blinking cursor to the body while tokens arrive.

Thought · Reflecting

Collapsed by default

defaultCollapsed hides the body until the user clicks the chevron — good for long CoT that would otherwise dominate the transcript.

Thought · Reasoning
The user's request is ambiguous between two meanings of "cheap": lowest sticker price vs lowest total cost of ownership. I'll assume sticker price since they didn't mention a time horizon.