Capability Hints: Feature Discovery System¶
Last Updated: January 9, 2026
Overview¶
Sage can hint about her capabilities when relevant topics come up naturally in conversation. This helps users discover features like Trip Planner, Bill Split, and calendar integration without being intrusive.
Design philosophy: Conservative, contextual, and never annoying.
Key Principles¶
- One hint per capability per 7 days - Users won't see the same hint repeatedly
- Trigger-based - Only hints when user mentions relevant topics
- Context-aware - Skips hints during emotional/urgent conversations
- Service-aware - Won't suggest connecting services that are already connected
- 1:1 only - No capability hints in group chats
Available Capability Hints¶
Trip Planner¶
Triggers: vacation, travel, trip, flight, hotel, itinerary, getaway, "going to [place]"
Hint: "btw i can help plan trips if u want - just say 'plan a trip'"
Cooldown: 7 days
Bill Split¶
Triggers: split the bill, owe me/you, venmo, zelle, paypal, dinner bill, receipt
Hint: "oh i can help split bills too if u need - just send me a receipt pic"
Cooldown: 7 days
Calendar Connection¶
Triggers: busy, schedule, calendar, meeting, appointment, "am I free"
Hint: "want me to check ur calendar? i can help spot conflicts"
Cooldown: 3 days
Requirement: Only shows if calendar is NOT connected
Gmail Connection¶
Triggers: email, inbox, gmail, unread messages
Hint: "want me to check ur email for important stuff? i can help spot urgent things"
Cooldown: 3 days
Requirement: Only shows if Gmail is NOT connected
Reminders¶
Triggers: remind me, don't forget, remember to
Hint: None (reminders are already auto-triggered, no hint needed)
How It Works¶
Detection Flow¶
Response Generated
↓
Check if 1:1 conversation (not group)
↓
Get connected services status
↓
CapabilityHinter.check_for_hint()
↓
For each capability:
├── Check trigger patterns (regex)
├── Check cooldown (database)
├── Check service requirements
└── Skip if emotional/urgent message
↓
If hint found:
├── Record hint shown (update cooldown)
└── Append to response bubbles
Hint Check Logic¶
async def check_for_hint(
message: str, # User's message
user_id: str, # For cooldown tracking
connected_services: dict, # {calendar: True, gmail: False}
active_miniapp: Optional[str], # Skip if already in miniapp
classification: dict # Skip for emotional/urgent
) -> Optional[str]:
Cooldown System¶
Database Table¶
CREATE TABLE capability_hint_cooldowns (
id UUID PRIMARY KEY,
user_id UUID REFERENCES users(id),
capability TEXT, -- e.g., "trip_planner"
last_shown_at TIMESTAMPTZ,
UNIQUE (user_id, capability)
);
Cooldown Check¶
async def _is_on_cooldown(user_id, hint_id, cooldown_days):
# Query last_shown_at for this user+capability
# If last_shown_at + cooldown_days > now: return True (on cooldown)
# Otherwise: return False (can show hint)
Trigger Patterns¶
Triggers use regex patterns for flexible matching:
"trip_planner": {
"triggers": [
r"\b(vacation|travel|trip|flight|hotel|itinerary|getaway)\b",
r"\b(going to|visiting|flying to)\s+\w+",
r"\bplan(ning)?\s+(a\s+)?trip\b",
]
}
Pattern notes:
- \b = word boundary (prevents "stripe" matching "trip")
- Case-insensitive matching
- Multiple patterns per capability (any match triggers)
Skip Conditions¶
Hints are not shown when:
- Group chat - Hints only in 1:1 conversations
- Emotional message - Intent is
emotional_support,urgent, orcrisis - On cooldown - User saw this hint within cooldown period
- Service connected - For connect hints, if service already connected
- MiniApp active - For miniapp hints, if that miniapp is already active
- No hint text - Some capabilities (reminders) don't need hints
Integration¶
Response Flow (message_handler.py)¶
# After response generation, before delivery
if not is_group:
connected_services = await self._get_connected_services(user_id)
hinter = get_capability_hinter()
hint = await hinter.check_for_hint(
message=message_for_processing,
user_id=user_id,
connected_services=connected_services,
classification=classification
)
if hint:
response["bubbles"].append(hint)
Hint Delivery¶
Hints are added as a separate bubble after the main response:
User: "thinking about going to japan next year"
Sage: "ooh japan!! that would be amazing"
Sage: "btw i can help plan trips if u want - just say 'plan a trip'"
File Locations¶
| Component | Location |
|---|---|
| Hint definitions | app/orchestrator/capability_hints.py |
| Integration | app/orchestrator/message_handler.py (lines 734-755) |
| Database migration | supabase/migrations/20260109000000_add_capability_hint_cooldowns.sql |
Example Scenarios¶
Scenario 1: First Trip Mention¶
User: "planning a vacation to italy"
Sage: "ooh italy!! when are u thinking?"
Sage: "btw i can help plan trips if u want - just say 'plan a trip'"
Scenario 2: Second Trip Mention (Within 7 Days)¶
No hint - on cooldownScenario 3: Service Already Connected¶
[User has calendar connected]
User: "am i busy tomorrow?"
Sage: "lemme check... u have a meeting at 2pm"
Scenario 4: Emotional Context¶
No hints during stressful situationsAdding New Capability Hints¶
To add a new capability hint:
- Add entry to
CAPABILITY_HINTSincapability_hints.py:
"new_feature": {
"triggers": [r"\bkeyword\b", r"phrase pattern"],
"hint": "btw i can do X if u want",
"cooldown_days": 7,
"capability": "new_feature",
# Optional:
"requires_disconnected": "service_name", # Only show if not connected
"exclude_if_active": True, # Don't show if miniapp active
}
- Patterns are automatically compiled on init - no additional setup needed.
Metrics¶
Track these to measure effectiveness:
| Metric | Description |
|---|---|
| Hint shown rate | % of relevant messages that trigger hints |
| Feature adoption after hint | % of users who use feature after seeing hint |
| Cooldown effectiveness | Repeat hint rate (should be ~0) |
| Hint-to-annoyance ratio | User complaints about hints |