Package-level declarations

Types

Link copied to clipboard

Broad grouping for a system action, for future filtering/retrieval (§12).

Link copied to clipboard
data class ActionDefinition(val id: String, val aliases: List<String>, val description: String, val category: ActionCategory)

One supported deterministic system action. id is the stable settings key the executor (settingAction in CommandRouter) maps to a public Android Intent.

Link copied to clipboard
data class ActionPair(val action: String, val value: String, val text: String = "")

Cycle-2 of the LLM pipeline: the model breaks an ACTION input into action→value pairs ("message mom hello on whatsapp" → open→whatsapp, message→mom "hello"); the launcher then maps the pairs onto intents deterministically here — the model names the pairs, the code decides what they execute as. Anything unmappable returns an empty plan so the caller falls back to the full decide prompt (cycle 3).

Link copied to clipboard

The central deterministic registry of supported system/settings actions (§12). Tier-0 routing resolves these from natural language with zero model calls and no app-list search; the LLM never produces them. Free of Android imports so it stays unit-testable in isolation.

Link copied to clipboard
sealed interface AiIntent

The structured decision the on-device model returns — one per capability in the catalog. leaving marks actions that hand off to another app (and thus background the launcher); the plan executor uses it to decide what can run inline vs. what can only be the terminal step. See Plan.

Link copied to clipboard
object AppCatalog

Answers questions about the installed apps deterministically from the local index (SRS §6) — "how many music apps", "list my games" — so the LLM never has to guess a count or invent an inventory. Matching is by Android's declared app category (ApplicationInfo.CATEGORY_*), with a label-keyword fallback.

Link copied to clipboard
class AppIndex

Precomputed, query-efficient index over the installed apps. Built once per app list (not per query), it replaces the old per-query full scans + double Levenshtein in CommandRouter. Two jobs:

Link copied to clipboard
class AppScope(val app: AppEntry, val cleaned: String)

An installed app named inside the command claims the whole command ("message mummy hello on whatsapp" is a WhatsApp message, start to finish). Detection is deterministic — the code knows the installed-app list, the model doesn't: a marker phrase ("on/in/via/using ") resolved with confidence, or a bare token that exactly equals an installed label. cleaned is the command with the mention removed, so the model never sees — and can't mangle — it.

Link copied to clipboard
data class Capability(val id: String, val schema: String, val shots: List<Pair<String, String>> = emptyList())

The capability catalog — the single source of truth for what the on-device model is allowed to do. Every entry is a typed template: the model only picks an action id and fills the declared slots; it never authors a raw Android Intent. Construction, validation and safety all live in Kotlin (CommandRouter), so a hallucinated or malformed reply can at worst fail to build — never fire something arbitrary.

Link copied to clipboard
data class Command(val title: String, val subtitle: String?, val kind: CommandKind, val confirm: Boolean = false, val options: List<Command> = emptyList(), val run: () -> Unit)
Link copied to clipboard

A resolved thing the user can run — produced by the AI, rendered by the UI. The launcher does no intent parsing; the model decides, this executes.

Link copied to clipboard
object Normalizer

Cheap deterministic text cleanup applied before any routing (§5.2). Keeps the original text for display; this is only for matching. It collapses whitespace and removes a repeated leading command verb ("open open developer options" → "open developer options") without stripping the primary verb that Tier-0 routing keys off, and without changing meaning.

Link copied to clipboard
typealias Plan = List<AiIntent>

An ordered list of actions the model wants performed for one command.

Link copied to clipboard
data class ResolverConfig(val autoThreshold: Float = 0.9f, val confirmThreshold: Float = 0.7f, val candidateThreshold: Float = 0.4f)

Centralized, tunable confidence policy for app resolution (SoT §15). These are starting values, not permanent constants — keep them here (not scattered through the code) so they can be tuned from real device tests.

Properties

Link copied to clipboard

Functions

Link copied to clipboard

The allowed-actions block for the prompt, generated from the catalog.

Link copied to clipboard
fun appScope(query: String, index: AppIndex): AppScope?
Link copied to clipboard
fun fastRoute(query: String, index: AppIndex): Plan?

Tier-0 deterministic router. The obvious ~70% of launcher commands — "open X", "call ", "set alarm for 7", "set a 10 minute timer", "turn on wifi" — don't need an LLM. This resolves them in microseconds with no model load, so the model only ever runs for compositional, conversational or ambiguous input.

Link copied to clipboard

The few-shot block, generated from the catalog. Uses a flat "input" -> json form (not a User:/Assistant: transcript) so a small model demonstrates the mapping without being taught to role-play extra conversation turns.

Link copied to clipboard
fun parseIntent(raw: String, index: AppIndex): AiIntent

Single-intent parse kept for callers that only want the first action.

Link copied to clipboard

Salvages {"action":…,"value":…} objects from a noisy model reply.

Link copied to clipboard
fun parsePlan(raw: String, index: AppIndex): Plan

Parses the model's reply into a Plan. Robust by design: it accepts a single object or a JSON array, salvages objects with a regex if the surrounding text is noisy, drops anything that doesn't map to a real capability, and coalesces redundant steps (e.g. "open whatsapp and message mom" → just the message).

Link copied to clipboard
fun planFromPairs(pairs: List<ActionPair>, index: AppIndex, scoped: AppEntry? = null): Plan

Maps pairs to a Plan. An "open" pair names the app the whole command is scoped to; when a message/email pair exists it absorbs that open (messaging opens the app itself). Conservative: one unmappable pair empties the plan.

Link copied to clipboard

Single-intent convenience for callers that don't build a plan.

fun Plan.toCommand(context: Context): Command

Turns a whole Plan into a single runnable Command the existing UI renders.