Skip to content

Commit d925d34

Browse files
Ju-uscgithub-actions[bot]uzair401Rizwan-095
authored
feat: add private notes note-taking agent (#232)
Signed-off-by: Uzair Ullah <uzairullahmail@gmail.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Uzair Ullah <uzairullahmail@gmail.com> Co-authored-by: Muhammad Rizwan <awanrizwan615@gmail.com>
1 parent 2a21500 commit d925d34

4 files changed

Lines changed: 465 additions & 0 deletions

File tree

community/private-notes/README.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Private Notes
2+
3+
`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.
4+
5+
## What It Does
6+
7+
- saves a new note
8+
- reads one or more notes
9+
- overwrites a specific note after confirmation
10+
- deletes one or more notes after confirmation
11+
12+
The ability uses a single LLM tool loop with conversation history. Python owns all note reads and writes.
13+
14+
## Example Phrases
15+
16+
- `take a note`
17+
- `note this down: call Sarah after lunch`
18+
- `read my notes`
19+
- `read my last note`
20+
- `update my grocery note`
21+
- `delete my last note`
22+
- `delete my notes`
23+
24+
## Storage
25+
26+
- File: `private_notes.json`
27+
- Persistence: `temp=False`
28+
- JSON saves safely overwrite by deleting any existing file before writing because `write_file()` appends by default
29+
- No `.md` files are written, so the Memory Watcher does not inject note contents into the Personality prompt
30+
31+
## Voice UX
32+
33+
- if no request is captured, the ability asks what the user wants to do
34+
- reads are capped to the 3 most recent matches to avoid long voice dumps
35+
- overwrite and delete actions always require confirmation
36+
- final responses stay short, warm, and conversational
37+
38+
## Suggested Trigger Words
39+
40+
Configure these in the OpenHome dashboard:
41+
42+
- `private note`
43+
- `private notes`
44+
- `take a note`
45+
- `note this down`
46+
- `write this down`
47+
- `read my notes`
48+
- `delete my notes`

community/private-notes/SPEC.md

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# Private Notes Spec
2+
3+
## Goal
4+
5+
`private-notes` is a voice-first personal note-taking agent for OpenHome.
6+
7+
- save a note
8+
- read one or more notes
9+
- overwrite a specific note
10+
- delete one or more notes
11+
12+
---
13+
14+
## Core Principles
15+
16+
1. Notes are private user data stored in JSON.
17+
2. The LLM picks which tool to call. Python executes it.
18+
3. Tool execution is id-based, not title-based.
19+
4. Voice responses are short and natural.
20+
5. No open-ended agent loop. Capped at 4 turns.
21+
22+
---
23+
24+
## Architecture
25+
26+
A uniform tool loop with one system prompt and conversation history:
27+
28+
```text
29+
history = [user: initial context]
30+
31+
while turns remain:
32+
tool_call = LLM(history, SYSTEM_PROMPT)
33+
history += assistant: tool_call
34+
35+
finish -> speak response, stop
36+
ask_followup -> speak question, history += user: answer, continue
37+
write/read/delete -> execute in Python, history += user: result, continue
38+
```
39+
40+
One system prompt. One conversation via `history`. `finish` is a tool like any other. The LLM writes confirmation messages for destructive actions.
41+
42+
---
43+
44+
## Data Model
45+
46+
### Note
47+
48+
```json
49+
{
50+
"id": "uuid",
51+
"title": "string",
52+
"content": "string",
53+
"created_at": "ISO timestamp",
54+
"updated_at": "ISO timestamp"
55+
}
56+
```
57+
58+
### Store
59+
60+
```json
61+
{
62+
"schema_version": 2,
63+
"notes": [Note]
64+
}
65+
```
66+
67+
Persistent storage lives in `private_notes.json`.
68+
69+
---
70+
71+
## Tools
72+
73+
### `write_note`
74+
75+
```json
76+
{"name": "write_note", "arguments": {"note_id": null, "title": "string", "content": "string", "confirmation": "string or null"}}
77+
```
78+
79+
- `note_id = null` creates a new note (no confirmation needed).
80+
- `note_id = <uuid>` overwrites an existing note. LLM provides the `confirmation` prompt.
81+
- If `note_id` does not exist, Python returns an error result instead of crashing the ability.
82+
83+
### `read_notes`
84+
85+
```json
86+
{"name": "read_notes", "arguments": {"note_ids": ["uuid"]}}
87+
```
88+
89+
- Readback capped to 3 notes.
90+
- Returns raw note data (title, content, updated_at). LLM formats for speech via `finish`.
91+
92+
### `delete_notes`
93+
94+
```json
95+
{"name": "delete_notes", "arguments": {"note_ids": ["uuid"], "confirmation": "string"}}
96+
```
97+
98+
- LLM provides the `confirmation` prompt. Always confirmed before deleting.
99+
100+
### `ask_followup`
101+
102+
```json
103+
{"name": "ask_followup", "arguments": {"question": "string"}}
104+
```
105+
106+
- Used when the request is ambiguous.
107+
108+
### `finish`
109+
110+
```json
111+
{"name": "finish", "arguments": {"response": "string"}}
112+
```
113+
114+
- Spoken response to the user. Ends the loop.
115+
116+
---
117+
118+
## Context
119+
120+
The first message in history contains:
121+
122+
1. Current local time (captured once for caching)
123+
2. User request
124+
3. Minimal note index: id, title, updated_at for each note (sorted by recency)
125+
126+
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.
127+
128+
---
129+
130+
## Safety Rules
131+
132+
1. Python executes all note mutations, not the LLM.
133+
2. Overwrite requires confirmation (LLM writes the prompt).
134+
3. Delete requires confirmation (LLM writes the prompt).
135+
4. JSON saves safely overwrite by deleting any existing file before writing because `write_file()` appends to existing files.
136+
5. The loop is capped at 4 turns.
137+
138+
---
139+
140+
## Validation
141+
142+
```
143+
python3 -m py_compile community/private-notes/main.py
144+
python3 validate_ability.py community/private-notes
145+
```
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

0 commit comments

Comments
 (0)