Initial spec: AGENTS.md build spec + archived original notes
This commit is contained in:
36
.gitignore
vendored
Normal file
36
.gitignore
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# Secrets
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
*.local
|
||||||
|
*.keystore
|
||||||
|
|
||||||
|
# Go
|
||||||
|
bin/
|
||||||
|
*.exe
|
||||||
|
*.test
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
|
||||||
|
# Node / web
|
||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
*.visual-check.*
|
||||||
|
|
||||||
|
# Android / Kotlin
|
||||||
|
.gradle/
|
||||||
|
build/
|
||||||
|
local.properties
|
||||||
|
*.apk
|
||||||
|
|
||||||
|
# OS / editors
|
||||||
|
.DS_Store
|
||||||
|
*.swp
|
||||||
|
|
||||||
|
|
||||||
|
# Pi agent generated memory (local state, not source)
|
||||||
|
.pi/memory-vault/
|
||||||
18
.pi/settings.json
Normal file
18
.pi/settings.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"source": "/home/sam/.agents",
|
||||||
|
"extensions": [
|
||||||
|
"extensions/ask-user-question/index.ts",
|
||||||
|
"extensions/pi-config/index.ts",
|
||||||
|
"extensions/filechanges/index.ts"
|
||||||
|
],
|
||||||
|
"skills": [
|
||||||
|
"skills/system-architect",
|
||||||
|
"skills/typesafe-ai",
|
||||||
|
"skills/obsidian-cli",
|
||||||
|
"skills/npm-security"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
218
AGENTS.md
Normal file
218
AGENTS.md
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
# Email Agent
|
||||||
|
|
||||||
|
> Build spec for Paperclip. Follow the phases in section 11 in order. Do not skip
|
||||||
|
> a phase's Definition of Done. When something is unclear, ask Sam — do not guess.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Purpose and outcome
|
||||||
|
|
||||||
|
A web app that triages the family inbox. All mail lands in one Gmail account
|
||||||
|
(`samuelrolfe@gmail.com`); PurelyMail forwards every domain address there.
|
||||||
|
|
||||||
|
The app sorts mail, recommends actions, performs them in bulk, drafts replies, and
|
||||||
|
answers questions from the Hub.
|
||||||
|
|
||||||
|
**Outcome (the single success statement):**
|
||||||
|
> Sam opens one page, clears the inbox in a few minutes, and knows nothing
|
||||||
|
> important was missed.
|
||||||
|
|
||||||
|
## 2. Scope
|
||||||
|
|
||||||
|
**In scope**
|
||||||
|
- Read mail from Gmail.
|
||||||
|
- Classify each message: category, importance, suggested action.
|
||||||
|
- Recommend: delete, unsubscribe, reply, file, keep.
|
||||||
|
- Bulk actions: delete, move, mark.
|
||||||
|
- AI reply drafts from templates and rules.
|
||||||
|
- A rules editor (text) that the classifier uses.
|
||||||
|
- A daily summary.
|
||||||
|
- Answer the Hub.
|
||||||
|
|
||||||
|
**Out of scope**
|
||||||
|
- Sending marketing or bulk mail.
|
||||||
|
- DNS, MX, SPF, DKIM. That is infrastructure, not this app.
|
||||||
|
- The other domains' websites.
|
||||||
|
- Any auto-send without review unless a rule explicitly allows it.
|
||||||
|
|
||||||
|
## 3. Users and roles
|
||||||
|
|
||||||
|
- **One user: Sam.** No multi-user. A session login protects the app.
|
||||||
|
- Adding a second user is out of scope.
|
||||||
|
|
||||||
|
## 4. Functional requirements
|
||||||
|
|
||||||
|
Each item is testable. Numbering is stable; do not renumber.
|
||||||
|
|
||||||
|
| ID | Requirement |
|
||||||
|
|---|---|
|
||||||
|
| FR-1 | Fetch new mail from Gmail with the Gmail API, on an n8n schedule. Store metadata and body. |
|
||||||
|
| FR-2 | Record which address the mail came through (`sam@audasmedia.com.au`, `hello@where-woof.com`, `hello@kontra.day`, or catch-all), read from the headers. |
|
||||||
|
| FR-3 | Classify each message with JEV: category, importance, suggested action, and a confidence. |
|
||||||
|
| FR-14 | Edit the category list in the UI: add, rename, remove. JEV uses the current list. |
|
||||||
|
| FR-4 | Apply user-written rules from the rules editor to override or refine the classification. |
|
||||||
|
| FR-5 | Show a triage list grouped by category, with the recommended action on each row. |
|
||||||
|
| FR-6 | Bulk actions: select all in a group, then delete, move, or mark. |
|
||||||
|
| FR-7 | Detect unsubscribe links. Keep an "ignore unsubscribe" list. Record what was unsubscribed. |
|
||||||
|
| FR-8 | Draft a reply from a template or rule with the LLM. Let Sam edit, then send. |
|
||||||
|
| FR-9 | Move messages to folders (Gmail labels): replied-to by address, a delete folder, and a saved folder. Deletes are recoverable. |
|
||||||
|
| FR-10 | Produce a daily summary and deliver it **by email**. |
|
||||||
|
| FR-11 | Rules editor: a plain-text area, saved, versioned. JEV and the LLM read it. |
|
||||||
|
| FR-12 | Expose the Hub API: `GET /capabilities`, `POST /ask` (read-only), `POST /notify`. |
|
||||||
|
| FR-13 | Log every action: what, when, which message, which rule. |
|
||||||
|
| FR-15 | Reply identity: send as the address the mail arrived at. Otherwise send as `Sam Rolfe`. |
|
||||||
|
|
||||||
|
## 5. Screens and UX flow
|
||||||
|
|
||||||
|
**Screens**
|
||||||
|
1. **Login** — session form. One dark indigo band (the design system's login moment).
|
||||||
|
2. **Inbox (main)** — three areas: left nav (categories and folders), centre list
|
||||||
|
(grouped, with select-all and bulk actions), right detail pane.
|
||||||
|
3. **Message detail** — body, recommended action, reply draft, and history.
|
||||||
|
4. **Rules editor** — plain text, save, and a version list.
|
||||||
|
5. **Reports** — the daily summary, with delete-report.
|
||||||
|
|
||||||
|
**Main flow**
|
||||||
|
```
|
||||||
|
Login → Inbox → scan categories → bulk-delete junk → review "important"
|
||||||
|
→ approve or edit reply drafts → send → close
|
||||||
|
```
|
||||||
|
|
||||||
|
**Rules**
|
||||||
|
- Works on a phone browser. Touch targets ≥44px.
|
||||||
|
- No spinners on decorative elements. Subtle opacity pulse only.
|
||||||
|
|
||||||
|
## 6. Look and feel
|
||||||
|
|
||||||
|
Reuse the Family Home Lab design system. Do not invent a new one.
|
||||||
|
|
||||||
|
- Source: `/home/sam/Docker/Containers/family-home-lab/DESIGN.md`
|
||||||
|
- Tokens: `/home/sam/Docker/Containers/family-home-lab/portal/static/tokens.css`
|
||||||
|
- Copy `tokens.css` into this project. Do not import across projects.
|
||||||
|
|
||||||
|
Key rules: canvas `#f6f5f4`, one action blue `#0075de`, Inter, 8px grid, pill CTAs,
|
||||||
|
4px inputs, hairline borders, layered micro-shadows. Include dark mode.
|
||||||
|
|
||||||
|
## 7. Data model
|
||||||
|
|
||||||
|
SQLite, inside the service. Suggested tables:
|
||||||
|
|
||||||
|
| Table | Holds |
|
||||||
|
|---|---|
|
||||||
|
| `messages` | Gmail id, thread id, from, to, subject, date, body text, source address, received-at |
|
||||||
|
| `classifications` | message id, category, importance, action, confidence, model, created-at |
|
||||||
|
| `rules` | id, text, version, created-at |
|
||||||
|
| `actions_log` | message id, action, actor, rule id, created-at |
|
||||||
|
| `unsubscribe` | domain, status (`done`, `ignored`), created-at |
|
||||||
|
| `drafts` | message id, body, template id, status |
|
||||||
|
| `summaries` | date, text, delivered-to |
|
||||||
|
|
||||||
|
Retention: keep message bodies for **one month**. Logs are append-only and
|
||||||
|
size-capped.
|
||||||
|
|
||||||
|
## 8. Integrations
|
||||||
|
|
||||||
|
| System | Use |
|
||||||
|
|---|---|
|
||||||
|
| **Gmail API** | Read mail; apply labels; send replies ("Send mail as"). |
|
||||||
|
| **PurelyMail** | Source of the forwarding. Read-only reference. Not required for v1. |
|
||||||
|
| **Amazon SES** | App/website mail only. Not for personal replies in v1. |
|
||||||
|
| **JEV** | Classification (typed category + confidence). HTTP API. |
|
||||||
|
| **OmniRoute** | All other models. |
|
||||||
|
| **n8n** | Triggers the scan on a schedule. |
|
||||||
|
| **Apprise** | Delivery of the daily summary. |
|
||||||
|
| **Hub** | `POST /ask` (read-only), `POST /notify`, `GET /capabilities`. |
|
||||||
|
|
||||||
|
The Hub interface is frozen in `central_ai_llm_master/docs/ask-contract.md`.
|
||||||
|
Build to it. `/ask` never changes state.
|
||||||
|
|
||||||
|
## 9. Technology (locked)
|
||||||
|
|
||||||
|
| Item | Value |
|
||||||
|
|---|---|
|
||||||
|
| Backend | **Go** |
|
||||||
|
| UI | **HTMX** + server-rendered HTML |
|
||||||
|
| Database | **SQLite** |
|
||||||
|
| Design | Family Home Lab `tokens.css` |
|
||||||
|
| Host | `.13`, Docker |
|
||||||
|
| Container | **This project's own** image and compose file. Not shared with any other project. |
|
||||||
|
| Folder | `/home/sam/paseo/projects/email_agent/` |
|
||||||
|
| Port | Allocate one (suggest **8790**). Record it here once chosen. |
|
||||||
|
| URL | `mail.lab.audasmedia.com.au` via Caddy on `.35` |
|
||||||
|
| Auth | **Separate session login.** Not the console SSO. |
|
||||||
|
|
||||||
|
## 10. Rules and constraints
|
||||||
|
|
||||||
|
1. All models go through **OmniRoute**, except JEV.
|
||||||
|
2. Secrets come from `~/.config/environment.d/10-secrets.conf`. Never commit them.
|
||||||
|
3. `/ask` is read-only. It never changes state.
|
||||||
|
4. Sending a reply or deleting in bulk needs Sam's action in the UI. No auto-send
|
||||||
|
unless a rule explicitly allows it.
|
||||||
|
5. **Spam is recommended, never auto-deleted.** Important mail sometimes looks like
|
||||||
|
spam. Confirm every spam action.
|
||||||
|
6. Deletes are recoverable. Move to a delete folder or trash, never hard-delete.
|
||||||
|
6. Replies return to the source and stay short.
|
||||||
|
7. Every action is logged.
|
||||||
|
8. Do not merge this service into another. Own folder, own container, own port.
|
||||||
|
|
||||||
|
## 11. Build order
|
||||||
|
|
||||||
|
Each phase ends with a Definition of Done (DoD). Do not start the next phase until
|
||||||
|
the DoD passes.
|
||||||
|
|
||||||
|
**Phase 1 — Skeleton**
|
||||||
|
Go service, SQLite, config, session login, Dockerfile, compose, Caddy route.
|
||||||
|
*DoD:* the app runs on `.13`, login works, an empty dashboard renders, `/health` returns ok.
|
||||||
|
|
||||||
|
**Phase 2 — Read mail**
|
||||||
|
Gmail OAuth (one manual consent). Fetch, store, and list real messages.
|
||||||
|
*DoD:* the inbox shows real mail, with the source address from FR-2.
|
||||||
|
|
||||||
|
**Phase 3 — Classify**
|
||||||
|
JEV classification plus the rules table.
|
||||||
|
*DoD:* every message has a category, importance, and suggested action.
|
||||||
|
|
||||||
|
**Phase 4 — Triage actions**
|
||||||
|
Delete, move, mark; bulk select.
|
||||||
|
*DoD:* select-all then delete works, and the message is recoverable.
|
||||||
|
|
||||||
|
**Phase 5 — Reply drafts**
|
||||||
|
Templates, LLM draft, edit, send.
|
||||||
|
*DoD:* a reply is drafted, edited, and sent, and the thread updates.
|
||||||
|
|
||||||
|
**Phase 6 — Unsubscribe and rules editor**
|
||||||
|
*DoD:* a rule written in the editor changes the classification of a real message.
|
||||||
|
|
||||||
|
**Phase 7 — Summary and Hub**
|
||||||
|
Daily summary; `/capabilities`, `/ask`, `/notify`.
|
||||||
|
*DoD:* the Hub asks "anything important in my inbox?" and gets a correct short answer.
|
||||||
|
|
||||||
|
**Phase 8 — Documentation**
|
||||||
|
*DoD:* project-docs run; Gitea, Obsidian, Outline, Vikunja, and the map are updated.
|
||||||
|
|
||||||
|
## 12. Acceptance criteria
|
||||||
|
|
||||||
|
The project is done when:
|
||||||
|
|
||||||
|
- Sam can log in from a phone browser.
|
||||||
|
- Real mail appears, grouped, with the source address shown.
|
||||||
|
- Bulk delete works and is recoverable.
|
||||||
|
- A reply can be drafted, edited, and sent without leaving the app.
|
||||||
|
- The rules editor changes real classifications.
|
||||||
|
- The Hub gets a correct short answer to "anything important in my inbox?".
|
||||||
|
- Every action is in the log.
|
||||||
|
- The app runs in its own container on its own port, behind Caddy.
|
||||||
|
|
||||||
|
## 13. Decisions and remaining questions
|
||||||
|
|
||||||
|
Resolved:
|
||||||
|
|
||||||
|
1. **Reply identity:** the address the mail arrived at. Otherwise `Sam Rolfe`.
|
||||||
|
2. **Categories:** `action`, `important`, `info`, `spam`, `newsletter`,
|
||||||
|
`auto-reply`. **Editable in the UI** (FR-14).
|
||||||
|
3. **Spam:** recommend and confirm. Never auto-delete.
|
||||||
|
4. **Daily summary:** by email.
|
||||||
|
5. **Retention:** one month of message bodies.
|
||||||
|
6. **Gmail OAuth:** the agent chooses the moment during the build.
|
||||||
|
|
||||||
|
Remaining: none that block. Add new ones here as they appear.
|
||||||
52
ORIGINAL_NOTES.md
Normal file
52
ORIGINAL_NOTES.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# Email Agent — Original Notes (archived)
|
||||||
|
|
||||||
|
> Historical pre-planning notes. **Superseded by `AGENTS.md`.** Do not build from this.
|
||||||
|
|
||||||
|
|
||||||
|
> Kept for provenance. The sections above replace these. Do not build from this.
|
||||||
|
|
||||||
|
```
|
||||||
|
# Intro
|
||||||
|
To create an email agent that will be developed over time to handle the
|
||||||
|
samuelrolfe@gmail.com emails. This account will have emails come in from other
|
||||||
|
accounts linked into this using MX records including hello@where-woof.com,
|
||||||
|
hello@kontra.day, sam@audasmedia.com.au and samuelrolfe@gmail.com.
|
||||||
|
|
||||||
|
# Objective
|
||||||
|
- I am unsure how email agents work - need advise in planning stage.
|
||||||
|
- Create an admin interface.
|
||||||
|
* Ignore unsubscribe
|
||||||
|
* Recommend to Delete -> Delete
|
||||||
|
* Summarized Emails -> Delete
|
||||||
|
* Summary
|
||||||
|
* Email Rules for ai
|
||||||
|
* Auto replied emails -> Delete
|
||||||
|
* Other requirements as advised by ai planning bot
|
||||||
|
* Some emails will be responded to
|
||||||
|
- Collate emails into subjects and or, who they are for - which email did they come through on?
|
||||||
|
- Delete spam email or repetitive emails that can not be unsubscribed.
|
||||||
|
- Advise to unsubscribe to email spam, check if they are on ignore unsubscribe spam list
|
||||||
|
- unsubscribe
|
||||||
|
- create report. Delete report. Delete associated emails.
|
||||||
|
- A.i. interface - Respond to emails, delete spam, unsubscribe, add new rule etc.
|
||||||
|
- LLM skills style text editor, just input text, for LLM responses to categorized emails.
|
||||||
|
- Move for deletion to a delete folder or trash.
|
||||||
|
- Move replied to to replied to folder per email address
|
||||||
|
- Summary of emails daily. Possible concertina or left nav for replies. Deletions. Etc. Select all -> delete.
|
||||||
|
- Option to save specific emails or move to folder.
|
||||||
|
- Jev to analyze emails based on rules.
|
||||||
|
|
||||||
|
# Plan / Overview (historical)
|
||||||
|
- Build using Paperclip. N8N as trigger. Web interface on .13, /var/www.
|
||||||
|
- Obsidian on .27.
|
||||||
|
- Use JEV to assess emails (skill installed, key present). Build a JEV pipeline.
|
||||||
|
- Assess whether the interface can create rules JEV uses.
|
||||||
|
- Golang + HTMX interface for speed.
|
||||||
|
- Response templates / LLM skills per category.
|
||||||
|
- Document on Obsidian / Outline / Vikunja / Gitea via project-docs.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Email and DNS reference
|
||||||
|
|
||||||
|
Full setup: `.27:/home/sam/obsidian/300 areas/360 Dev-Ops Network Computers/Email & DNS Setup — Domains, Mailboxes & Records.md`.
|
||||||
|
Summary: Cloudflare DNS → PurelyMail mailboxes → forward to Gmail. Outbound via Amazon SES.
|
||||||
Reference in New Issue
Block a user