calendar: add contacts system to prevent recipient address hallucination

Agent hallucinated "xiaojuzi@meta.com" instead of looking up the correct
address from USER.md. Prompting rules can't reliably prevent this, so
recipient validation is now enforced at the tool level.

- Add contact list/add/delete subcommands (vCard .vcf files synced via CardDAV)
- send --to now resolves through contacts: accepts names, name:type, or
  known emails; rejects unknown addresses with available contacts shown
- Multi-email contacts supported with type qualifier (e.g. "小橘子:work")
- Adding contacts and sending are separate operations (no chaining)
This commit is contained in:
Yanxin Lu
2026-03-31 10:43:06 -07:00
parent b7b99fdb61
commit cd1ee050ed
5 changed files with 506 additions and 56 deletions

View File

@@ -20,7 +20,16 @@ See `TESTING.md` for dry-run and live test steps, verification checklists, and t
- `khal` for reading calendar (optional but recommended)
- Runs via `uv run` (dependencies managed in `pyproject.toml`)
## Important: Email Sending Rules
## Important: Recipient Validation
The `send` command **only accepts recipients that exist in the contacts list**. This prevents hallucinated email addresses.
- `--to "小橘子:work"` — resolves contact name + email type
- `--to "小橘子"` — resolves by name (errors if contact has multiple emails)
- `--to "user@example.com"` — accepted only if the email exists in contacts
- Unknown addresses are **rejected** with the available contacts list shown
**Adding contacts and sending invites are separate operations.** Do not add a contact and send to it in the same request — contact additions should be a deliberate, user-initiated action.
Calendar invites are outbound emails. Follow the workspace email rules:
- **youlu@luyanxin.com -> mail@luyx.org**: send directly, no confirmation needed
@@ -43,6 +52,11 @@ $SKILL_DIR/scripts/calendar.sh reply [options]
$SKILL_DIR/scripts/calendar.sh event list [options]
$SKILL_DIR/scripts/calendar.sh event delete [options]
# Manage contacts (used for recipient validation)
$SKILL_DIR/scripts/calendar.sh contact list
$SKILL_DIR/scripts/calendar.sh contact add [options]
$SKILL_DIR/scripts/calendar.sh contact delete [options]
# Manage todos
$SKILL_DIR/scripts/calendar.sh todo add [options]
$SKILL_DIR/scripts/calendar.sh todo list [options]
@@ -57,8 +71,9 @@ $SKILL_DIR/scripts/calendar.sh todo check
## Sending Invites
```bash
# --to accepts contact names (resolved via contacts list)
$SKILL_DIR/scripts/calendar.sh send \
--to "friend@example.com" \
--to "小橘子:work" \
--subject "Lunch on Friday" \
--summary "Lunch at Tartine" \
--start "2026-03-20T12:00:00" \
@@ -71,7 +86,7 @@ $SKILL_DIR/scripts/calendar.sh send \
| Flag | Required | Description |
|-----------------|----------|------------------------------------------------|
| `--to` | Yes | Recipient(s), comma-separated |
| `--to` | Yes | Recipient(s) contact name, name:type, or known email |
| `--subject` | Yes | Email subject line |
| `--summary` | Yes | Event title (shown on calendar) |
| `--start` | Yes | Start time, ISO 8601 (`2026-03-20T14:00:00`) |
@@ -182,6 +197,42 @@ $SKILL_DIR/scripts/calendar.sh event delete --match "Allergy Shot" --all
---
## Managing Contacts
Contacts are stored as vCard (.vcf) files in `~/.openclaw/workspace/contacts/default/` and synced to Migadu CardDAV via vdirsyncer. The `send` command validates recipients against this contact list.
```bash
# List all contacts
$SKILL_DIR/scripts/calendar.sh contact list
# Add a contact (single email)
$SKILL_DIR/scripts/calendar.sh contact add --name "小鹿" --email "mail@luyx.org"
# Add with email type and nickname
$SKILL_DIR/scripts/calendar.sh contact add --name "小橘子" --email "Erica.Jiang@anderson.ucla.edu" --type work --nickname "小橘子"
# Add a second email to an existing contact
$SKILL_DIR/scripts/calendar.sh contact add --name "小橘子" --email "xueweijiang0313@gmail.com" --type home
# Delete a contact
$SKILL_DIR/scripts/calendar.sh contact delete --name "小橘子"
```
### Sending to Contacts
```bash
# By name (works when contact has a single email)
$SKILL_DIR/scripts/calendar.sh send --to "小鹿" ...
# By name + type (required when contact has multiple emails)
$SKILL_DIR/scripts/calendar.sh send --to "小橘子:work" ...
# By raw email (must exist in contacts)
$SKILL_DIR/scripts/calendar.sh send --to "Erica.Jiang@anderson.ucla.edu" ...
```
---
## Replying to Invites
```bash