Inline Reply Suggestions
When you click an inbound message in the communication feed, an inline suggestions panel appears directly below it. The panel shows three AI-generated reply cards ranked by confidence, with controls to generate, regenerate, and use suggestions.
How It Works
Section titled “How It Works”Click inbound message bubble → Toggle InlineSuggestionsPanel below the bubble → GET /api/text-agents/suggestions/{message_id} → If suggestions exist → render 3 cards with confidence badges → If none exist → show "Generate Suggestions" button → Click generate → POST /api/text-agents/generate-suggestions → Agentic AI loop produces 3 ranked replies → Click "Use This Reply" → populate compose bar with suggestion textUI States
Section titled “UI States”The panel cycles through five states depending on data availability and user actions:
| State | Trigger | Display |
|---|---|---|
| Loading | Panel opens, fetching data | Three skeleton placeholder cards |
| Empty | No suggestions saved yet | Sparkles icon + “Generate Suggestions” button |
| Generating | Generate/regenerate clicked | Spinner + “Generating suggestions…” + skeletons |
| Loaded | Suggestions returned | Three cards in a responsive grid |
| Error | API call failed | Error message + “Try Again” button |
Suggestion Cards
Section titled “Suggestion Cards”Each card displays three elements:
pub struct TextAgentSuggestionItem { pub body: String, // The suggested reply text pub confidence: f64, // Score between 0.0 and 1.0}Confidence badges use color-coded thresholds:
- ≥ 80% — green badge (
emerald-500) - 50–79% — blue badge (
primary) - < 50% — gray badge (
muted)
The “Use This Reply” button on each card calls the on_use handler, which populates the compose bar with that suggestion’s body text.
Inbound Bubble Interactions
Section titled “Inbound Bubble Interactions”Inbound message bubbles gained interactive behaviors to support the suggestions panel:
- Hover — a subtle ring highlight (
hover:ring-1 ring-primary/30) and sparkles icon appear - Click — toggles the suggestions panel for that message
- Selected state — active ring (
ring-1 ring-primary/40) persists while the panel is open - Toggle — clicking the same bubble again closes the panel
- Switch — clicking a different inbound bubble closes the previous panel and opens a new one
Selection resets automatically when you navigate to a different contact.
Auto-Scroll
Section titled “Auto-Scroll”The communication feed now auto-scrolls to the most recent message when:
- The conversation first loads
- A new message arrives via WebSocket
- You switch to a different contact
The scroll uses a reactive use_effect hook tied to the message list signal, with a short timeout to ensure the DOM has rendered before scrolling.
Authorization
Section titled “Authorization”Both suggestion endpoints use contact-based access control instead of text agent management permissions. You need permission to view the contact — not permission to manage text agents.
The system calls check_contact_access with ContactInstanceAction::View, which checks:
- The contact exists in the caller’s organization
- The caller has either
ContactInstancePermission::View(org-wide) orContactInstancePermission::ViewAssigned(restricted to assigned contacts)
Super-admins and org owners bypass these checks automatically.
| Role | Can see suggestions? |
|---|---|
| Super admin / org owner | Always |
| Org admin with contact view permission | Yes, for all contacts |
| Org member assigned to the contact | Yes, via ViewAssigned |
| Org member not assigned to the contact | No (403 Forbidden) |
This means any team member who can view a contact’s conversation also sees AI suggestions for that contact’s messages — no separate text agent permission required.
API Endpoints
Section titled “API Endpoints”Get suggestions
Section titled “Get suggestions”GET /api/text-agents/suggestions/{message_id}Body/Query: { contact_id: string }Returns the most recent TextAgentSuggestion for the given message, or null if none exist. Requires ContactInstanceAction::View permission for the specified contact.
Status codes: 200 success, 400 invalid UUID, 403 no contact access, 500 server error.
Generate suggestions
Section titled “Generate suggestions”POST /api/text-agents/generate-suggestionsBody: { contact_id: string, message_id: string }Resolves the text agent assigned to the contact (via direct link or phone number fallback), runs the agentic suggestion loop, and persists the result to the text_agent_suggestion table. Requires ContactInstanceAction::View permission for the specified contact.
Status codes: 200 success, 400 invalid UUID, 403 no contact access, 404 message or contact not found, 500 server error.
Key Components
Section titled “Key Components”| Component | File | Role |
|---|---|---|
InlineSuggestionsPanel | text_agent/components/text_agent_suggestions_panel_component.rs | Panel with fetch, generate, and card rendering |
SuggestionCard | Same file (private) | Single card with confidence badge and use button |
SkeletonCards | Same file (private) | Three animated placeholder cards |
MessageEntry | messaging/components/message_entry_component.rs | Bubble with click, hover, and selection states |
CommunicationFeed | messaging/components/communication_feed_component.rs | Feed container managing selection state and auto-scroll |
Database
Section titled “Database”Suggestions are persisted in the text_agent_suggestion table:
text_agent_suggestion ( id UUID PRIMARY KEY, organization_id UUID NOT NULL, contact_id UUID NOT NULL, text_agent_id UUID NOT NULL, message_id UUID NOT NULL, suggestions JSONB NOT NULL, -- Vec<TextAgentSuggestionItem> auto_sent_body TEXT, -- Set if auto-reply was triggered created_at TIMESTAMP NOT NULL)Each generation creates a new row. The GET endpoint returns the most recent row for a given message_id, ordered by created_at DESC.