How I built an app launcher for my desktop AI companion: an index, not a shell command
Description and use
A safe app launcher for a desktop AI companion uses an index of applications instead of shell commands, preventing voice queue blockage and security risks.
Problem
A shell command to launch apps can leave a child process that holds the voice queue open, causing the assistant to freeze, and can be unsafe if the model injects malicious code.
Outcome
The launcher safely opens applications without freezing the assistant, using an index to prevent shell injection and detaching processes to avoid voice queue blockage.
Workflow
- 1. Scan installed apps and cache results in apps-index.json (max 600 entries, 7‑day age).
- 2. Build a strict matching ladder: exact name → unique prefix → substring → multi‑token matching.
- 3. If multiple matches, return ambiguous with up to 5 candidates and ask user to choose.
- 4. For Windows AppIDs, launch via
cmd /c startafter shape validation; for file paths, spawn directly withshell:falseafter absolute path and existence checks. - 5. Detach child process, ignore stdio, call
unref()and do not register in job state. - 6. Log app name and source, not absolute path.
- 7. Prompt user only for unknown apps not in index.
Evidence
"Open Notepad" sounds like the simplest request a desktop companion can get. It nearly became the most dangerous one.
The obvious implementation is a shell command: take the app name, run it, done. But a shell command has a child process, and a child that outlives its turn can hold the voice queue open — the exact bug that used to freeze voice entirely.
So I built open_app around one decision: the model picks a name from an index, never a path.
Ankita scans the machine's installed apps and caches them in apps-index.json (mode 0o600, capped at 600 entries).
The launch itself is detached, with stdio ignored and unref() called — and deliberately not registered in the assistant's job state.
- Record date: 2026-11-01T19:59:06.879Z