Melian

The Watchtower

From the high places she watches, sorting what matters from what can wait, what demands action from what drifts past like leaves.

Overview

The attention pipeline is Melian's triage system. Every incoming email and message passes through a four-tier classifier cascade that determines urgency, routes to the appropriate handler, and decides what reaches you immediately versus what can wait.

Classifications

Each item is assigned one of five classifications:

Classification Meaning
personal From a known contact. Highest priority, immediate attention.
transactional OTPs, delivery notifications, receipts. Route to notify.
spam Promotional or junk. Archive silently.
fyi Informational but not urgent. Batch for triage.
action Requires a response or decision

The classifier also tracks its own certainty:

export type AttentionClassification = "personal" | "transactional" | "spam" | "fyi" | "action";
export type ClassificationSource = "contact" | "heuristic" | "embedding" | "llm";

export interface ClassifyInput {
  body: string;
  actor_id?: string | null;
  chat_identifier?: string | null;
}

export interface ClassifyResult {
  classification: AttentionClassification;
  source: ClassificationSource;
  confidence: number;
}

The Four Tiers

The classifier runs as a cascade: each tier either commits to a result or passes the item down to the next.

Tier 0: Contact lookup

Before any analysis, Melian checks whether the sender is a known contact. If yes, the item is immediately classified as personal with confidence 1.0. Source: "contact". No further tiers run.

Tier 1: Heuristic patterns

Fast regex matching against the message body. No model call, no latency.

  • Shortcodes, OTPs, delivery tracking keywords → transactional, confidence 0.85–0.95
  • Promotional patterns (unsubscribe links, marketing language) → spam, confidence 0.85

Items that don't match any pattern pass through to Tier 2.

Tier 2: Embedding nearest-neighbors

The message is embedded and compared against labeled examples in Qdrant. K=20 nearest neighbors are retrieved. The tier requires at least 3 valid neighbors and a confidence floor of 0.70. A top-3 unanimous agreement check prevents noisy classifications from committing.

If the neighbors don't agree well enough, the item passes to Tier 3.

Tier 3: LLM classification

Full LLM call with the message body as context. Returns structured JSON:

{ "classification": "fyi", "confidence": 0.88, "reason": "newsletter from a known publication, no action required" }

The LLM result is stored in Qdrant as a new labeled example for future Tier 2 lookups.

Mode Resolution

The classification drives how an item is handled, but the active mode determines the exact behavior:

export type EffectiveMode = "auto" | "draft" | "notify" | "triage" | "spam" | "auto_assistant";

Resolution order:

  1. spam classification → mode "spam" (archive silently)
  2. transactional → mode "notify" (push notification, no reply)
  3. fyi → mode "notify" (push notification, no reply)
  4. Falls through to the contact's configured mode preference
  5. If busy mode is active and mode is "triage" and busy_auto_reply is enabled → mode becomes "auto_assistant" (Melian drafts and sends a reply on your behalf)

State Machine

Each item moves through a defined lifecycle:

export type AttentionState =
  | "received"
  | "classified"
  | "routed"
  | "awaiting_user"
  | "auto_replied"
  | "spam_archived"
  | "digested"
  | "handled";
received → classified → routed → awaiting_user  → handled
                                                 → digested → handled
                                → auto_replied   → handled
                                → spam_archived  → handled

Learning & Correction

The classifier improves over time through three types of stored examples in Qdrant, all weighted differently:

Source Weight When used
Seed Built-in examples shipped with the system
LLM output Results from Tier 3 that get stored automatically
Correction Explicit user corrections override automatic results

When you correct a misclassification, the correction is stored at double weight and becomes a stronger signal for future K-NN lookups at Tier 2. Over time, the embedding store learns your specific senders and patterns, reducing how often items fall through to the expensive Tier 3 LLM call.