Skip to content

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

  1. One hint per capability per 7 days - Users won't see the same hint repeatedly
  2. Trigger-based - Only hints when user mentions relevant topics
  3. Context-aware - Skips hints during emotional/urgent conversations
  4. Service-aware - Won't suggest connecting services that are already connected
  5. 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:

  1. Group chat - Hints only in 1:1 conversations
  2. Emotional message - Intent is emotional_support, urgent, or crisis
  3. On cooldown - User saw this hint within cooldown period
  4. Service connected - For connect hints, if service already connected
  5. MiniApp active - For miniapp hints, if that miniapp is already active
  6. 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'"
Hint shown, cooldown started

Scenario 2: Second Trip Mention (Within 7 Days)

User: "also maybe greece"
Sage: "ooh greece would be gorgeous too"
No hint - on cooldown

Scenario 3: Service Already Connected

[User has calendar connected]

User: "am i busy tomorrow?"
Sage: "lemme check... u have a meeting at 2pm"
No "connect calendar" hint - already connected

Scenario 4: Emotional Context

User: "my flight got cancelled and I'm stuck"
Sage: "oh no that's so stressful, what happened?"
No hints during stressful situations


Adding New Capability Hints

To add a new capability hint:

  1. Add entry to CAPABILITY_HINTS in capability_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
}
  1. 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