> ## Documentation Index
> Fetch the complete documentation index at: https://docs.openhome.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Background Abilities

> Always-on Abilities that run in parallel with the main conversation — alarms, reminders, note-taking, and ambient intelligence.

Abilities can run background threads alongside the main conversation. Add a file called `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

<Note>
  This is additive. Existing `main.py`-only Abilities work exactly as before.
</Note>

## The four Ability categories

See [Ability Types](/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.

| Type                             | Files                       | Description                                                                                                                          |
| -------------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| **Standard Interactive**         | `main.py` only              | User triggers with hotwords → runs → exits with `resume_normal_flow()`. The original pattern.                                        |
| **Standalone Background Daemon** | `background.py` only        | Starts automatically on session. Runs in background for monitoring, logging, note-taking. **Works even when Personality is asleep.** |
| **Interactive + Daemon**         | `main.py` + `background.py` | Interactive handles user requests. Daemon runs in background. They coordinate through shared file storage.                           |

### Example: Interactive Combined Ability

```
AlarmAbility/
├── main.py           # Interactive — set an alarm
├── background.py     # Background — fire the alarm
├── config.json       # Required
└── alarm.mp3         # Supporting files
```

<Warning>
  The background file **must** be named exactly `background.py`. No other filename will be detected by the platform.
</Warning>

## `main.py` vs `background.py`

These are the most common sources of bugs when writing background daemons. Pay close attention.

| Aspect                 | `main.py`                       | `background.py`                                |
| ---------------------- | ------------------------------- | ---------------------------------------------- |
| `call()` signature     | `call(self, worker)`            | `call(self, worker, background_daemon_mode)`   |
| CapabilityWorker init  | `CapabilityWorker(self)`        | `CapabilityWorker(self)`                       |
| Triggered by           | User hotwords                   | Automatically on session start                 |
| Lifecycle              | Runs once, then exits           | Continuous `while True` loop                   |
| `resume_normal_flow()` | **Required** on every exit path | Not needed (independent thread)                |
| Works in sleep mode    | No — requires active session    | **Yes** — runs even when Personality is asleep |
| Multiple instances     | One at a time                   | Multiple daemons supported                     |

## New SDK methods

| Method                       | Returns | Async | Description                                                                              |
| ---------------------------- | ------- | ----- | ---------------------------------------------------------------------------------------- |
| `get_timezone()`             | `str`   | No    | User's timezone (e.g. `"America/Chicago"`). Use for alarms, calendars, time-aware logic. |
| `get_full_message_history()` | `list`  | No    | Full conversation transcript. Daemons use this to monitor the live conversation.         |
| `send_interrupt_signal()`    | —       | Yes   | Stops current Personality output. Call before `speak()` or `play_audio()` from a daemon. |

### Usage

```python theme={"system"}
# Get user timezone (synchronous)
tz = self.capability_worker.get_timezone()

# Get conversation history (synchronous)
history = self.capability_worker.get_full_message_history()

# Interrupt before speaking from a background daemon (async)
await self.capability_worker.send_interrupt_signal()
await self.capability_worker.speak("Your alarm is going off!")
```

## Background daemon code template

Copy this as your starting point for any background daemon. The `call()` signature has an extra `background_daemon_mode` parameter, but the `CapabilityWorker` constructor is the same as `main.py`.

```python theme={"system"}
import json
from src.agent.capability import MatchingCapability
from src.main import AgentWorker
from src.agent.capability_worker import CapabilityWorker
from time import time


class YourWatcherCapability(MatchingCapability):
    worker: AgentWorker = None
    capability_worker: CapabilityWorker = None
    background_daemon_mode: bool = False

    #{{register capability}}

    async def watcher_loop(self):
        self.worker.editor_logging_handler.info(
            "%s: Watcher started" % time()
        )
        while True:
            # --- your background logic here ---
            self.worker.editor_logging_handler.info(
                "%s: Watcher cycle" % time()
            )
            await self.worker.session_tasks.sleep(20.0)

    def call(self, worker: AgentWorker, background_daemon_mode: bool):
        self.worker = worker
        self.background_daemon_mode = background_daemon_mode
        self.capability_worker = CapabilityWorker(self)
        self.worker.session_tasks.create(self.watcher_loop())
```

<Warning>
  **Ordering matters.** `self.worker` and `self.background_daemon_mode` must be set **before** calling `CapabilityWorker(self)`. The constructor reads from `self` internally — if these aren't set first, it will fail.
</Warning>

## 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

The only requirement is that the daemon's main function runs in a never-ending `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 call `speak()`, `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

| Step | Component       | Action                                                               |
| ---- | --------------- | -------------------------------------------------------------------- |
| 1    | User            | Says *"set an alarm for 3pm Thursday"*                               |
| 2    | `main.py`       | LLM parses time, writes alarm to `alarms.json`                       |
| 3    | `main.py`       | Confirms to user, calls `resume_normal_flow()`                       |
| 4    | `background.py` | Polls `alarms.json` every \~15 seconds (running since session start) |
| 5    | `background.py` | Target time hits → `send_interrupt_signal()`                         |
| 6    | `background.py` | Plays `alarm.mp3`, speaks notification                               |
| 7    | `background.py` | Updates alarm status to `"triggered"` in `alarms.json`               |

### Sample `alarms.json`

```json theme={"system"}
[
  {
    "id": "alarm_1772046000778",
    "created_at_epoch": 1772046000,
    "timezone": "America/Los_Angeles",
    "target_iso": "2026-02-26T00:06:00-08:00",
    "human_time": "12:01 AM on Thursday, Feb 26, 2026",
    "source_text": "Can you set an alarm for me?",
    "status": "scheduled"
  }
]
```

## Best practices

<AccordionGroup>
  <Accordion title="Use session_tasks.sleep() — not asyncio.sleep()">
    Session tasks ensure proper cleanup when the session ends. `asyncio.sleep()` can leak.
  </Accordion>

  <Accordion title="Keep poll intervals reasonable">
    10–30 seconds is typical. For alarms, 15–30 seconds is fine.
  </Accordion>

  <Accordion title="Handle missing files gracefully">
    The JSON file may not exist yet if `main.py` hasn't been triggered. Always `check_if_file_exists()` first.
  </Accordion>

  <Accordion title="Use delete + write for JSON">
    `write_file()` appends, which corrupts JSON. Always delete, then write the full object.
  </Accordion>

  <Accordion title="Log generously">
    Background daemons run silently. `editor_logging_handler` is your only window into what they're doing.
  </Accordion>

  <Accordion title="Call send_interrupt_signal() before speaking">
    Otherwise audio overlaps, or the system tries to transcribe your daemon's output as user input.
  </Accordion>

  <Accordion title="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.
  </Accordion>
</AccordionGroup>

## Templates & resources

| Resource                             | Link                                                                                                                   |
| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------- |
| Alarm Ability (Interactive Combined) | [openhome-dev/abilities/templates/Alarm](https://github.com/openhome-dev/abilities/tree/dev/templates/Alarm)           |
| Standalone Background Daemon         | [openhome-dev/abilities/templates/Background](https://github.com/openhome-dev/abilities/tree/dev/templates/Background) |
| SDK Reference                        | [SDK Reference](/api-sdk/sdk-reference)                                                                                |
| Questions / support                  | [#dev-help on Discord](https://discord.com/channels/1197724389630824508/1201669938126008350)                           |

The Alarm template is the best reference for the Interactive Combined pattern. Study both `main.py` and `background.py` to understand how they coordinate.
