Parent Issue
Part of #500 — depends on #504 (useAgentChat hook)
Context
This is the main visual component for interactive agent sessions — a chat panel that renders streamed messages, tool call inputs/outputs, thinking blocks, and a user input bar. It consumes useAgentChat and has no backend dependencies of its own.
Existing Code to Reference
web-ui/src/components/prd/DiscoveryPanel.tsx — multi-turn Q&A pattern in the same codebase (closest existing analogue)
web-ui/src/components/execution/EventStream.tsx — grouped event rendering pattern
- Optio
apps/web/src/components/session-chat.tsx — full reference implementation
What to Build
New file: web-ui/src/components/sessions/AgentChatPanel.tsx
Props:
interface AgentChatPanelProps {
sessionId: string;
className?: string;
}
Layout (top to bottom):
┌─────────────────────────────────┐
│ Header: model badge | cost | status dot │
├─────────────────────────────────┤
│ │
│ Message list (scrollable) │
│ │
│ ┌─ user ────────────────────┐ │
│ │ "Add error handling..." │ │
│ └───────────────────────────┘ │
│ ┌─ assistant ───────────────┐ │
│ │ Sure, let me read the... │ │ ← text delta, streaming
│ └───────────────────────────┘ │
│ ┌─ tool_use ────────────────┐ │
│ │ ▶ Read auth/service.py │ │ ← collapsible
│ │ [input JSON] │ │
│ └───────────────────────────┘ │
│ ┌─ tool_result ─────────────┐ │
│ │ class AuthService:... │ │ ← truncated, expand on click
│ └───────────────────────────┘ │
│ ┌─ thinking ────────────────┐ │
│ │ 💡 I need to check... │ │ ← italic, muted border-left
│ └───────────────────────────┘ │
│ │
├─────────────────────────────────┤
│ [Interrupt] [textarea] [Send] │
└─────────────────────────────────┘
Message rendering rules:
| Role |
Visual treatment |
user |
Right-aligned or distinct background (follow existing Discovery Panel style) |
assistant |
Left-aligned, plain text, streaming cursor indicator while status === "streaming" |
tool_use |
Collapsible card: tool name + short summary in header, raw toolInput JSON in body (collapsed by default) |
tool_result |
Collapsible card: first 200 chars visible, "Show more" expands |
thinking |
Italic text, left border, muted color, lightbulb icon |
system |
Centered small text, muted |
error |
Red border card with alert icon |
Input bar:
<textarea> that auto-grows (up to 6 lines)
- Enter sends, Shift+Enter inserts newline
- Disabled + shows spinner while
status === "thinking" or "streaming"
[Interrupt] button visible only while status === "thinking" or "streaming"; calls interrupt()
Header:
- Status dot (green = connected, yellow = connecting, red = disconnected)
- Cost counter:
$0.0031 updating in real-time
- Model badge:
claude-sonnet-4-6
Auto-scroll:
- Scroll to bottom on new messages
- Stop auto-scroll if user has scrolled up; resume when user scrolls back to bottom
Empty state:
- Icon + "Start a conversation with your agent" when no messages
Acceptance Criteria
Notes
- Follow the existing Radix UI + Hugeicons + Tailwind pattern of the codebase
- Do not introduce new component library dependencies
Parent Issue
Part of #500 — depends on #504 (useAgentChat hook)
Context
This is the main visual component for interactive agent sessions — a chat panel that renders streamed messages, tool call inputs/outputs, thinking blocks, and a user input bar. It consumes
useAgentChatand has no backend dependencies of its own.Existing Code to Reference
web-ui/src/components/prd/DiscoveryPanel.tsx— multi-turn Q&A pattern in the same codebase (closest existing analogue)web-ui/src/components/execution/EventStream.tsx— grouped event rendering patternapps/web/src/components/session-chat.tsx— full reference implementationWhat to Build
New file:
web-ui/src/components/sessions/AgentChatPanel.tsxProps:
Layout (top to bottom):
Message rendering rules:
userassistantstatus === "streaming"tool_usetoolInputJSON in body (collapsed by default)tool_resultthinkingsystemerrorInput bar:
<textarea>that auto-grows (up to 6 lines)status === "thinking"or"streaming"[Interrupt]button visible only whilestatus === "thinking"or"streaming"; callsinterrupt()Header:
$0.0031updating in real-timeclaude-sonnet-4-6Auto-scroll:
Empty state:
Acceptance Criteria
status === "streaming"Notes