Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions community/private-notes/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Private Notes

`Private Notes` is a voice-first note-taking agent for OpenHome. It stores notes in persistent `private_notes.json`, so note contents stay out of the Personality prompt and are only spoken when the user explicitly asks.

## What It Does

- saves a new note
- reads one or more notes
- overwrites a specific note after confirmation
- deletes one or more notes after confirmation

The ability uses a single LLM tool loop with conversation history. Python owns all note reads and writes.

## Example Phrases

- `take a note`
- `note this down: call Sarah after lunch`
- `read my notes`
- `read my last note`
- `update my grocery note`
- `delete my last note`
- `delete my notes`

## Storage

- File: `private_notes.json`
- Persistence: `temp=False`
- JSON saves safely overwrite by deleting any existing file before writing because `write_file()` appends by default
- No `.md` files are written, so the Memory Watcher does not inject note contents into the Personality prompt

## Voice UX

- if no request is captured, the ability asks what the user wants to do
- reads are capped to the 3 most recent matches to avoid long voice dumps
- overwrite and delete actions always require confirmation
- final responses stay short, warm, and conversational

## Suggested Trigger Words

Configure these in the OpenHome dashboard:

- `private note`
- `private notes`
- `take a note`
- `note this down`
- `write this down`
- `read my notes`
- `delete my notes`
145 changes: 145 additions & 0 deletions community/private-notes/SPEC.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# Private Notes Spec

## Goal

`private-notes` is a voice-first personal note-taking agent for OpenHome.

- save a note
- read one or more notes
- overwrite a specific note
- delete one or more notes

---

## Core Principles

1. Notes are private user data stored in JSON.
2. The LLM picks which tool to call. Python executes it.
3. Tool execution is id-based, not title-based.
4. Voice responses are short and natural.
5. No open-ended agent loop. Capped at 4 turns.

---

## Architecture

A uniform tool loop with one system prompt and conversation history:

```text
history = [user: initial context]

while turns remain:
tool_call = LLM(history, SYSTEM_PROMPT)
history += assistant: tool_call

finish -> speak response, stop
ask_followup -> speak question, history += user: answer, continue
write/read/delete -> execute in Python, history += user: result, continue
```

One system prompt. One conversation via `history`. `finish` is a tool like any other. The LLM writes confirmation messages for destructive actions.

---

## Data Model

### Note

```json
{
"id": "uuid",
"title": "string",
"content": "string",
"created_at": "ISO timestamp",
"updated_at": "ISO timestamp"
}
```

### Store

```json
{
"schema_version": 2,
"notes": [Note]
}
```

Persistent storage lives in `private_notes.json`.

---

## Tools

### `write_note`

```json
{"name": "write_note", "arguments": {"note_id": null, "title": "string", "content": "string", "confirmation": "string or null"}}
```

- `note_id = null` creates a new note (no confirmation needed).
- `note_id = <uuid>` overwrites an existing note. LLM provides the `confirmation` prompt.
- If `note_id` does not exist, Python returns an error result instead of crashing the ability.

### `read_notes`

```json
{"name": "read_notes", "arguments": {"note_ids": ["uuid"]}}
```

- Readback capped to 3 notes.
- Returns raw note data (title, content, updated_at). LLM formats for speech via `finish`.

### `delete_notes`

```json
{"name": "delete_notes", "arguments": {"note_ids": ["uuid"], "confirmation": "string"}}
```

- LLM provides the `confirmation` prompt. Always confirmed before deleting.

### `ask_followup`

```json
{"name": "ask_followup", "arguments": {"question": "string"}}
```

- Used when the request is ambiguous.

### `finish`

```json
{"name": "finish", "arguments": {"response": "string"}}
```

- Spoken response to the user. Ends the loop.

---

## Context

The first message in history contains:

1. Current local time (captured once for caching)
2. User request
3. Minimal note index: id, title, updated_at for each note (sorted by recency)

Subsequent turns append tool results and follow-up answers as history entries. The LLM resolves "my latest note" to the first id in the index.

---

## Safety Rules

1. Python executes all note mutations, not the LLM.
2. Overwrite requires confirmation (LLM writes the prompt).
3. Delete requires confirmation (LLM writes the prompt).
4. JSON saves safely overwrite by deleting any existing file before writing because `write_file()` appends to existing files.
5. The loop is capped at 4 turns.

---

## Validation

```
python3 -m py_compile community/private-notes/main.py
python3 validate_ability.py community/private-notes
```
1 change: 1 addition & 0 deletions community/private-notes/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Loading
Loading