# desktop-assistant A local text assistant daemon. Questions arrive on a Unix socket, the model already loaded in a local llama-server answers, calling read-only tools when a question needs them, and the reply streams back token by token. Every conversation is kept in a local SQLite history with full-text search. The model and the history stay on the machine; only tools you enable (weather, web search) reach the network. The front end is the assistant canvas of the quickshell desktop (`desktop/assistant/` in the quickshell repo, on SUPER+Home). Anything that speaks JSON lines on a Unix socket can drive it. The voice version (push-to-talk, whisper, Kokoro) is the tag `voice-v1`. ## Requirements - a running `llama-server` with an OpenAI-compatible API, router mode - Python 3 (stdlib only; SQLite with FTS5) - poppler (`pdftotext`, `pdftoppm`) for PDF attachments - `curl` and `jq` for the weather and search example tools, `python3-dateutil` for the calendar one ## Usage ```sh python3 assistant.py # run python3 assistant.py --test # self-check ./assistant.sh start # detached ./assistant.sh stop ./assistant.sh restart ./assistant.sh status ``` The daemon uses whichever model the router has loaded, so it never swaps a model out under another client. Only one daemon serves the socket: a second one refuses to start while the first answers. ## Protocol JSON lines both ways on `$XDG_RUNTIME_DIR/desktop-assistant.sock` (mode 0600). Every connected client sees every event. In: `ask {text, files}`, `write {files, prompt, dir, name}`, `stop`, `new`, `list`, `open {id}`, `search {q}`, `delete {id}`. Out: `hello` on connect (state, model, context use, the current conversation and any reply in flight), then `user`, `think`, `token`, `tool`, `done`, `state`, `ctx`, `error`, and the answers `conversations`, `conversation` and `results`. The full table is in `docs/superpowers/specs/2026-10-16-text-daemon-design.md`. Ids are integers; a malformed command or argument answers `error` and the connection stays open. `write` is a one-off request whose reply goes to a file instead of the conversation (the canvas's To .md): no history, no thinking, no tools; it streams `progress {chars}` and ends with `written {path}`, the first free name of `name` in `dir`. A stop leaves no file. `files` is a list of absolute paths, at most 8. Text and code files and PDFs are read and inlined, sharing half the free context (a PDF page with no text goes as an image, at most 4, rendered at 150 dpi, at most 1600 px); images go as image parts to a model that reports seeing them. History keeps the text and, for images, the path and hash: a file that changed or moved is reported as no longer available when the conversation is reopened, and a model that cannot see images gets a note instead. Try it by hand: ```sh socat - UNIX-CONNECT:$XDG_RUNTIME_DIR/desktop-assistant.sock {"type":"ask","text":"What time is it?"} ``` ## History `~/.local/share/desktop-assistant/history.db` (`$XDG_DATA_HOME` honoured), directory 0700, file 0600. The model is sent the last 10 turns of the open conversation. Tool results are stored as a placeholder, not their output. Reasoning is shown live and not stored. ## Configuration and tools Everything is optional. Settings and tools live in `~/.config/desktop-assistant/` (or `$XDG_CONFIG_HOME/desktop-assistant/`): ```sh mkdir -p ~/.config/desktop-assistant cp examples/config.toml ~/.config/desktop-assistant/ cp -r examples/tools ~/.config/desktop-assistant/ ``` - `config.toml`: every key is optional, the example lists the defaults. `[env]` is added to every tool's environment; put instance URLs and key file paths there. - `tools/.toml`: one tool each, enabled by being there. A tool is a command run without a shell, its stdout goes back to the model: ```toml description = "What the tool does, for the model" command = ["script.sh", "{param}"] # looked up in tools/ first, then PATH timeout = 10 [params.param] type = "string" description = "What the model should pass" ``` The example tools only read: - `now`: date and time - `calendar`: `agenda.py` over a vdirsyncer folder - `weather`: OpenWeatherMap, key file in the conky-theme-udt format - `search`: a SearXNG instance with the JSON format enabled - `calc`: `qalc` (arithmetic, units, currencies, date math) - `mail`: unread threads through `mailctl` searches, no tagging or sync - `playing`: the current track from `playerctl`, or `mpc` - `finance`: balances, budgets and spending from Firefly III's `firefly` list commands - `bluetooth`: power, connected devices (battery when reported) and paired ones, through `bluetoothctl show/devices/info` - `vms`: libvirt VMs and their state, or one VM's details, through `virsh` read commands - `updates`: Slackware -current ChangeLog entries newer than the installed packages: counts, security fixes, kernel bump, one package's notes, or a full markdown recap opened in `MD_EDITOR` (default `xdg-open`); `CHANGELOG` and `PKGDIR` in `[env]` override the mirror path and package database ## License Copyright (C) 2026 Danilo M. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. See [LICENSE](LICENSE). ## Development Approach This project is developed using AI-assisted tools. Code is generated with the help of AI based on human-provided specifications, design decisions, and iterative feedback. All contributions are reviewed, tested, and curated by the maintainer before being included in the codebase. AI is used as a productivity and exploration tool, while human oversight remains central to all decisions. The goal is to combine the flexibility of AI-assisted development with standard open-source practices such as transparency, review, and accountability.