Skip to content

Routing

When a message comes in, the router figures out which worker should handle it. It tries three things, in order, and stops at the first match.

Tier 1: slash commands

If your message starts with /, the router checks a lookup table. No LLM call, no network request. Just a string match.

/expenses  → expense-tracker
/schedule  → calendar
/search    → research
/ask       → general

You define the table. Multiple commands can point to the same worker -- /expenses, /budget, and /spending could all go to the same place. The command name is arbitrary.

This costs nothing and takes less than a millisecond.

To add a new command, edit FAST_PASS_ROUTES in ique/router.ts.

Tier 2: semantic routing

If the message doesn't start with a slash, the router sends it to Claude Haiku along with a list of your active workers and their descriptions. Haiku reads the message, reads the descriptions, and picks the best match.

The descriptions come from the boutiques table in the database. Something like:

expense-tracker:  "Expense tracking, receipt logging, budget questions"
calendar:         "Scheduling, reminders, meeting conflicts, availability"
research:         "Web lookups, fact-checking, summarizing documents"

Write good descriptions and the routing mostly takes care of itself. "What am I doing Thursday?" goes to the calendar worker. "How much did I spend on coffee this month?" goes to expenses. You don't need to anticipate every phrasing.

The router uses generateObject() with a Zod enum, which forces Haiku to return exactly one worker ID. No free-form text to parse, no ambiguity.

This costs a fraction of a cent per message. Latency is 150-300ms.

Tier 3: keyword fallback

If the Haiku call fails (API is down, key is missing, network hiccup), keyword matching kicks in. Each worker has a list of trigger words:

expense-tracker: ["expense", "receipt", "budget", "cost", "spent", "invoice"]
calendar:        ["schedule", "meeting", "calendar", "reminder", "appointment"]

The worker with the most keyword hits wins. If nothing matches, the message goes to whichever worker you've set as the default.

This is the safety net. It's crude but it means the bot never just drops a message on the floor because an API was temporarily unavailable.

Adding routes

When you create a new worker, you have three options for routing. You can do all of them or just one.

Register the worker in the boutiques table with a clear description. This is the only required step. Tier 2 will start routing natural language messages to your worker based on the description alone.

Add slash commands in FAST_PASS_ROUTES if you want instant, deterministic routing for common queries. Good for power users who know the commands.

Add keywords in the keywordFallback() function as a safety net for when the LLM isn't available.

If you're prototyping, just register the worker with a description and skip the other two. You can add slash commands later once you know what people actually type.