How it works
One clear path, from event to reply.
From a Discord event to the router, then the action gate or the model chain, then the output filter — on a single SQLite archive.
-
The event arrives
Discord events enter through bot.py, are taken up by angel/client.py, and are routed by angel/nl_router.py.
-
Context is read
The router reads the persona, the member memory and the member note, then decides: a direct reply, the model chain when generated text is needed, or an action that needs the gate.
-
The action gate
The gate checks the actor’s immutable Discord id, the required permission, and a word that signals intent, before anything reaches the Discord API.
-
The output filter
It strips @everyone, token-like strings and leftover tags, and corrects a reply that claims an action still sitting in the queue was executed.
-
The archive
One SQLite file: server_messages for raw messages, an FTS5 index for search, and a table for member notes. No external queue, cache or worker.
Operations
-
A gate before every start
The unit runs the check gate before the service starts; if it fails, startup is cancelled rather than letting a bad deploy take the bot down.
-
Independent timers
Learning from the day’s conversations, refreshing culture notes, a SQLite backup, a monthly member-note refresh, and an optional metrics snapshot.
-
Local backups
Local snapshots made with sqlite3 .backup and stored with 600 permissions; they contain messages and member notes, and there is no off-server copy yet.