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

# Persistent Memory Across Sessions

> Storage patterns for Abilities that need to remember yesterday — journals, latest-state files, JSON config, rolling windows.

OpenHome Abilities can read and write files that persist across sessions. This is how you build voice journals, running logs, long-term preferences, alarms, grocery lists, and any Ability that needs to remember something beyond the current conversation.

<Note>
  If you specifically need an Ability to influence the **Agent's prompt** (inject context the Agent can see), see [Agent Memory & Context Injection](/agent_memory_context_injection) — that page covers the `.md`-file → Agent-prompt pipeline. This page is about general-purpose file storage.
</Note>

## The API

Four methods, all from `self.capability_worker`:

```python theme={"system"}
# Write or append
await self.capability_worker.write_file(name, content, temp=False)

# Read (always check existence first)
if await self.capability_worker.check_if_file_exists(name, temp=False):
    content = await self.capability_worker.read_file(name, temp=False)

# Delete
await self.capability_worker.delete_file(name, temp=False)
```

| Parameter    | Meaning                                      |
| ------------ | -------------------------------------------- |
| `name`       | Filename, including extension                |
| `content`    | String payload (serialize JSON/CSV yourself) |
| `temp=False` | Persistent across sessions                   |
| `temp=True`  | Cleared when the session ends                |

Full method details: [SDK Reference](/api-sdk/sdk-reference).

***

## Storage patterns

Four patterns cover 90% of use cases. Pick the one that matches your data's shape.

### 1. Journal / append log

For things that grow forever and don't need to be read in structured form — voice journals, event logs, debug traces.

```python theme={"system"}
async def append_journal(self, entry: str):
    timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
    line = f"{timestamp}\t{entry}\n"
    await self.capability_worker.write_file("voice_journal.log", line)
```

**Key property:** `write_file` appends by default for text-like files — no read-modify-write needed.

**Read later:**

```python theme={"system"}
if await self.capability_worker.check_if_file_exists("voice_journal.log"):
    full_log = await self.capability_worker.read_file("voice_journal.log")
```

<Warning>
  **Never use this pattern for JSON.** Appending to a JSON file produces invalid JSON. Use the [Key-value JSON pattern](#3-key-value-json) instead.
</Warning>

### 2. Latest state / replaceable file

For a single current value that gets overwritten — user preferences, the latest mood reading, the current "focus mode" flag.

```python theme={"system"}
async def save_state(self, filename: str, content: str):
    if await self.capability_worker.check_if_file_exists(filename):
        await self.capability_worker.delete_file(filename)
    await self.capability_worker.write_file(filename, content)
```

Always **delete + write**, never append. Then read gives you the current state:

```python theme={"system"}
current = await self.capability_worker.read_file("focus_mode.md")
```

This is also the pattern required for [`.md` context files that inject into the Agent prompt](/agent_memory_context_injection).

### 3. Key-value JSON

For structured data with multiple fields — alarms, reminders, a grocery list, user settings.

```python theme={"system"}
import json

async def read_alarms(self):
    if not await self.capability_worker.check_if_file_exists("alarms.json"):
        return []
    raw = await self.capability_worker.read_file("alarms.json")
    try:
        return json.loads(raw)
    except json.JSONDecodeError:
        return []

async def write_alarms(self, alarms: list):
    # Always delete + write — append corrupts JSON
    if await self.capability_worker.check_if_file_exists("alarms.json"):
        await self.capability_worker.delete_file("alarms.json")
    await self.capability_worker.write_file(
        "alarms.json",
        json.dumps(alarms, indent=2),
    )

# Usage
alarms = await self.read_alarms()
alarms.append({"id": "alarm_1", "target_iso": "2026-04-18T07:00:00"})
await self.write_alarms(alarms)
```

<Warning>
  **Always delete + write for JSON.** `write_file()` appends by default, which produces invalid JSON and breaks the next read.
</Warning>

### 4. Rolling window

For recent-N data — last 100 sensor readings, last 24 hours of transcripts, recent commands.

```python theme={"system"}
MAX_ENTRIES = 100

async def push_reading(self, reading: dict):
    entries = []
    if await self.capability_worker.check_if_file_exists("readings.json"):
        try:
            entries = json.loads(await self.capability_worker.read_file("readings.json"))
        except json.JSONDecodeError:
            entries = []

    entries.append(reading)
    entries = entries[-MAX_ENTRIES:]  # Trim old entries

    if await self.capability_worker.check_if_file_exists("readings.json"):
        await self.capability_worker.delete_file("readings.json")
    await self.capability_worker.write_file(
        "readings.json",
        json.dumps(entries),
    )
```

**Always trim at write time, not at read time** — the file size stays bounded, reads stay fast.

***

## Persistent vs temp

| `temp=False` (default)             | `temp=True`                               |
| ---------------------------------- | ----------------------------------------- |
| Survives session end               | Deleted when session ends                 |
| User preferences, alarms, journals | Conversation scratch space, session flags |
| Read on next session               | Gone after the current call               |

Default to `temp=False`. Only use `temp=True` when you need to remember something *within* a session but want it cleared after.

***

## Best practices

### Namespace your filenames

Avoid generic names. They collide across Abilities.

* ❌ `data.json`, `state.md`, `list.txt`
* ✅ `smarthub_prefs.json`, `alarm_active.md`, `grocery_list.txt`

Use the Ability name (or a short prefix) at the start of every filename.

### Keep each file focused

One logical object per file. Split a shared concern across files when the single file gets past \~1 MB or starts holding multiple unrelated concepts.

### Always check existence before read

`read_file()` on a missing file throws. Always:

```python theme={"system"}
if await self.capability_worker.check_if_file_exists(name):
    content = await self.capability_worker.read_file(name)
else:
    content = ""  # or default
```

### Serialize JSON yourself

`write_file()` takes a string. Use `json.dumps()` when writing, `json.loads()` when reading. Wrap `json.loads` in a `try/except json.JSONDecodeError` to recover from corrupt files.

### Bound your data

Rolling windows, journal rotation, log truncation — pick a cap and enforce it at write time. Unbounded files fill disk and slow down every future read.

### Handle missing files as "first run"

On the first call, every file is missing. Design for that:

```python theme={"system"}
prefs = {}
if await self.capability_worker.check_if_file_exists("prefs.json"):
    prefs = json.loads(await self.capability_worker.read_file("prefs.json"))
prefs.setdefault("theme", "dark")  # defaults for new users
```

***

## When to use `main.py` + `background.py` together

If you need to **write from `main.py` and react to that write from a background daemon** — like alarms or reminders — see the [Coordination Pattern in Background Abilities](/building-abilities/background-abilities#coordination-pattern). Both files read and write to the same shared file storage.

## See also

* [Agent Memory & Context Injection](/agent_memory_context_injection) — `.md` files that get injected into the Agent's live prompt
* [Background Abilities](/building-abilities/background-abilities) — daemons that poll shared storage
* [SDK Reference](/api-sdk/sdk-reference) — the full file API and sandbox rules
