background.py to any Ability folder and it runs automatically when the user connects to a Personality, staying alive as an independent thread for the entire session. No hotword trigger needed.
What this unlocks
- Background polling — check a file or API on a timer
- Proactive notifications — interrupt the conversation when something fires
- Scheduled tasks — monitor for time-based events like alarms
- Ambient monitoring — watch the live conversation for note-taking or summarization
This is additive. Existing
main.py-only Abilities work exactly as before.The four Ability categories
See Ability Types for the full breakdown. Background Daemons are one of the four.File structure
Every Ability is built from one or two files, regardless of which category you pick in the Dashboard.Example: Interactive Combined Ability
main.py vs background.py
These are the most common sources of bugs when writing background daemons. Pay close attention.
New SDK methods
Usage
Background daemon code template
Copy this as your starting point for any background daemon. Thecall() signature has an extra background_daemon_mode parameter, but the CapabilityWorker constructor is the same as main.py.
Key behaviors
Works in sleep mode
Background daemons continue running even when the Personality is in sleep mode. The daemon is an independent thread — it does not depend on the main conversation flow being active. This means your daemon can:- Monitor and transcribe ambient audio without a wake word
- Process conversations happening around the device
- Interject when it detects something relevant (via
send_interrupt_signal()) - Build RAG-style user context summaries in the background
while True loop.
Multiple watchers
You can have multiple background daemons running simultaneously. Each is its own independent thread. For example, you could run an alarm daemon, a note-taking daemon, and a conversation summarizer all at the same time.Full speak capability
Background daemons have the same ability to speak as interactive Abilities. A watcher can callspeak(), play_audio(), text_to_speech(), and all other CapabilityWorker methods. Just call send_interrupt_signal() first to avoid audio overlap with any active conversation.
Coordination pattern
The primary way interactive and background components communicate is through shared persistent file storage. Both files read and write to the same user-scoped files.Example: Alarm Ability
Sample alarms.json
Best practices
Use session_tasks.sleep() — not asyncio.sleep()
Use session_tasks.sleep() — not asyncio.sleep()
Session tasks ensure proper cleanup when the session ends.
asyncio.sleep() can leak.Keep poll intervals reasonable
Keep poll intervals reasonable
10–30 seconds is typical. For alarms, 15–30 seconds is fine.
Handle missing files gracefully
Handle missing files gracefully
The JSON file may not exist yet if
main.py hasn’t been triggered. Always check_if_file_exists() first.Use delete + write for JSON
Use delete + write for JSON
write_file() appends, which corrupts JSON. Always delete, then write the full object.Log generously
Log generously
Background daemons run silently.
editor_logging_handler is your only window into what they’re doing.Call send_interrupt_signal() before speaking
Call send_interrupt_signal() before speaking
Otherwise audio overlaps, or the system tries to transcribe your daemon’s output as user input.
Use a while True loop for sleep mode support
Use a while True loop for sleep mode support
Background daemons work even when the Personality is asleep — but only if the main function is a never-ending
while True loop.Templates & resources
The Alarm template is the best reference for the Interactive Combined pattern. Study both
main.py and background.py to understand how they coordinate.
