feat(ai_mail): docs(ai_mail): comprehensive README update
Co-Authored-By: @ai_mail <ai_mail@aipass>
This commit is contained in:
+140
-31
@@ -9,16 +9,35 @@
|
||||
|
||||
---
|
||||
|
||||
**Status:** Operational. Core email workflow (send/inbox/reply/close), dispatch system (with startup timeout + auto-retry), daemon, desktop notifications all working. Seedgo 99%.
|
||||
**Status:** Operational | **Seedgo:** 100% (34/34) | **Tests:** 355 pass | **Battle Tested:** S62
|
||||
|
||||
## Commands / Usage
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Dispatch (send + wake in one step)
|
||||
drone @ai_mail dispatch @target "Subject" "Body" # Send dispatch email + wake
|
||||
drone @ai_mail dispatch @target "Subject" "Body" --fresh # Send + fresh wake
|
||||
drone @ai_mail email @target "Subject" "Body" # Send email (no wake)
|
||||
drone @ai_mail dispatch @target "Subject" "Body" --fresh # Send + fresh wake (new session)
|
||||
drone @ai_mail dispatch wake @target # Wake only (no email)
|
||||
drone @ai_mail inbox # Check inbox
|
||||
|
||||
# Send mail (no wake)
|
||||
drone @ai_mail email @target "Subject" "Body" # Send to one branch
|
||||
drone @ai_mail email @all "Subject" "Body" # Broadcast to all branches
|
||||
drone @ai_mail email @target "Subj" "Body" --from @spawn # Explicit sender override
|
||||
|
||||
# Read mail
|
||||
drone @ai_mail inbox # List all emails (new + opened)
|
||||
drone @ai_mail view <id> # View email (marks as opened)
|
||||
drone @ai_mail view latest # View most recent email
|
||||
|
||||
# Resolve mail
|
||||
drone @ai_mail reply <id> "message" # Reply + close + archive original
|
||||
drone @ai_mail close <id> # Close single email
|
||||
drone @ai_mail close <id1> <id2> <id3> # Close multiple emails
|
||||
drone @ai_mail close all # Close all emails
|
||||
|
||||
# Other
|
||||
drone @ai_mail sent # View sent messages
|
||||
drone @ai_mail contacts # List all known branches
|
||||
drone @ai_mail --help # Full help
|
||||
```
|
||||
|
||||
@@ -34,16 +53,55 @@ new → opened → closed
|
||||
- **opened** — Viewed by recipient, awaiting action
|
||||
- **closed** — Replied or dismissed, archived automatically
|
||||
|
||||
Each branch's mailbox lives at `<branch_path>/.ai_mail.local/inbox.json`. Sent copies go to `.ai_mail.local/sent/`. File locking (`fcntl`/`msvcrt`) protects concurrent inbox writes.
|
||||
|
||||
## Dispatch System
|
||||
|
||||
The `dispatch` command sends an email and wakes the target branch in one step. A polling daemon can also watch inboxes and spawn agents for `auto_execute` dispatch emails automatically.
|
||||
The `dispatch` command sends an email and wakes the target branch in one step. Dispatch emails carry `auto_execute: true` and a task header, signaling the target agent to process them as work items.
|
||||
|
||||
### Wake Pipeline
|
||||
|
||||
1. `dispatch.py` orchestrates: send email via `send_to_single()`, then wake via `wake_branch()`
|
||||
2. `wake.py` resolves the branch from the registry, finds the `claude` binary, spawns a subprocess
|
||||
3. `dispatch_monitor.py` wraps the claude process with safety features:
|
||||
- **Startup health check** — monitors JSONL session files for 90s, kills if no activity
|
||||
- **Auto-retry** — 3 strikes: attempt 1+2 resume, attempt 3 fresh (new session)
|
||||
- **Bounce email** — on final failure, sends error report back to sender
|
||||
- **Lock cleanup** — removes `.dispatch.lock` when agent exits
|
||||
4. After wake, `_spawn_watchdog()` auto-launches `drone @devpulse watchdog agent @target` as a detached background process
|
||||
|
||||
### Safety Limits
|
||||
|
||||
- PID-based locking prevents concurrent agents per branch (`.dispatch.lock`)
|
||||
- Max turns per wake, max dispatches per branch per day
|
||||
- `WAKE_BLOCKLIST` protects `@devpulse` from cross-branch manual wakes
|
||||
- `dispatch_monitor.py` strips `AIPASS_CALLER_*` env vars to prevent parent context leaking into agent identity
|
||||
- `AIPASS_BRANCH_NAME` env var set in spawn_env for CWD-independent identity
|
||||
|
||||
### Daemon
|
||||
|
||||
The polling daemon (`daemon.py`) watches inboxes for `auto_execute` dispatch emails and spawns agents automatically. It also runs the AIPASS-TEST token protocol: `scan_and_ack_test_emails()` intercepts ping-test messages and auto-replies with "ack" before dispatch processing.
|
||||
|
||||
## Sender Identity
|
||||
|
||||
Branch identity detection follows a priority chain in `detect_branch_from_pwd()`:
|
||||
|
||||
1. `AIPASS_CALLER_BRANCH` env var (set by drone router from passport or `AIPASS_BRANCH_NAME`)
|
||||
2. Contacts address book lookup (fastest path for registered branches)
|
||||
3. Registry lookup by name
|
||||
4. `AIPASS_CALLER_CWD` / `Path.cwd()` walk-up to find `.trinity/passport.json`
|
||||
5. Registry lookup by path
|
||||
|
||||
If all fail, detection returns `None` and the operation fails loudly. Wrong identity is worse than no identity.
|
||||
|
||||
The `--from @branch` flag on send/email commands provides an explicit sender override for callers outside branch directories.
|
||||
|
||||
## Cross-Project Email
|
||||
|
||||
External projects (outside the AIPass repo) can send to AIPass branches. On delivery, `delivery.py` stores a `reply_path` on the message (the sender's `inbox.json` path, resolved from `AIPASS_CALLER_CWD`). Replies use `_deliver_via_reply_path()` to write directly to the external inbox without needing registry lookup.
|
||||
|
||||
The contacts system (`contacts.py`) maintains an address book at `.ai_mail.local/contacts.json`, auto-registering branches on every send/receive. This enables fast sender detection for known branches without CWD walking or registry lookups.
|
||||
|
||||
- Agents are ephemeral (wake, do work, exit)
|
||||
- Safety limits: max turns per wake, max dispatches per branch per day
|
||||
- PID-based locking prevents concurrent agents per branch
|
||||
- Startup health check: monitors JSONL session files for 90s, kills if no activity
|
||||
- Auto-retry: 3 strikes (resume, resume, fresh) before bounce
|
||||
- Failed agents trigger bounce emails back to sender
|
||||
## Architecture
|
||||
|
||||
Follows the standard AIPass 3-layer pattern:
|
||||
@@ -51,20 +109,65 @@ Follows the standard AIPass 3-layer pattern:
|
||||
```
|
||||
ai_mail/
|
||||
├── apps/
|
||||
│ ├── ai_mail.py # Entry point (auto-discovers modules)
|
||||
│ ├── ai_mail.py # Entry point (auto-discovers modules)
|
||||
│ ├── modules/
|
||||
│ │ ├── email.py # Inbox, view, reply, close, contacts, routing
|
||||
│ │ ├── email_send.py # Send orchestration (direct, interactive, broadcast)
|
||||
│ │ ├── dispatch.py # Dispatch status, daemon, wake
|
||||
│ │ ├── email.py # Inbox, view, reply, close, contacts, routing
|
||||
│ │ ├── email_send.py # Send orchestration (direct, interactive, broadcast)
|
||||
│ │ └── dispatch.py # Dispatch send+wake, status, daemon control
|
||||
│ └── handlers/
|
||||
│ ├── email/ # Delivery, formatting, inbox ops, purge, reply
|
||||
│ ├── dispatch/ # Daemon, wake, dispatch_monitor, status, test_token
|
||||
│ ├── registry/ # Branch registry read
|
||||
│ ├── users/ # Branch detection, user lookup
|
||||
│ ├── json_utils/ # JSON I/O helpers (load_json, save_json)
|
||||
│ ├── paths.py # Shared find_repo_root() utility
|
||||
│ ├── notify.py # Desktop notifications (dbus)
|
||||
│ └── central_writer.py # Central inbox stats aggregation
|
||||
│ ├── email/
|
||||
│ │ ├── delivery.py # Core delivery pipeline (write to recipient inbox)
|
||||
│ │ ├── send.py # Sender resolution + send helpers
|
||||
│ │ ├── send_args.py # Argument parsing for send command
|
||||
│ │ ├── inbox_ops.py # Inbox loading + v1→v2 migration
|
||||
│ │ ├── inbox_cleanup.py # Mark opened/closed + archive
|
||||
│ │ ├── inbox_lock.py # File locking (fcntl/msvcrt cross-platform)
|
||||
│ │ ├── inbox_resolve.py # Resolve inbox path from args or caller
|
||||
│ │ ├── reply.py # Reply + auto-close original
|
||||
│ │ ├── close_ops.py # Batch close operations
|
||||
│ │ ├── contacts.py # Address book for branch routing
|
||||
│ │ ├── create.py # Email file creation (sent/ folder)
|
||||
│ │ ├── format.py # Display formatting
|
||||
│ │ ├── header.py # Dispatch header injection
|
||||
│ │ ├── footer.py # Email footer
|
||||
│ │ ├── purge.py # Auto-purge sent/deleted folders
|
||||
│ │ ├── error_dispatch.py # Error reporting via email
|
||||
│ │ └── dashboard_sync.py # Dashboard integration
|
||||
│ ├── dispatch/
|
||||
│ │ ├── daemon.py # Polls inboxes, spawns agents for dispatch emails
|
||||
│ │ ├── wake.py # Wakes branches via claude subprocess
|
||||
│ │ ├── dispatch_monitor.py # Wraps claude process (bounce + lock cleanup)
|
||||
│ │ ├── status.py # Dispatch log I/O
|
||||
│ │ └── test_token.py # AIPASS-TEST ping protocol (auto-ack)
|
||||
│ ├── registry/
|
||||
│ │ └── read.py # Registry reading + get_all_branches()
|
||||
│ ├── users/
|
||||
│ │ ├── branch_detection.py # CWD/env-based branch identity detection
|
||||
│ │ └── user.py # Current user detection (get_current_user)
|
||||
│ ├── json_utils/
|
||||
│ │ └── json_handler.py # Auto-creating JSON system
|
||||
│ ├── paths.py # Shared find_repo_root() utility
|
||||
│ ├── notify.py # Desktop notifications (dbus direct)
|
||||
│ └── central_writer.py # Central inbox stats aggregation
|
||||
└── tests/ # 355 tests across 16 test files
|
||||
├── conftest.py # Shared fixtures (mock_logger, mock_json_handler)
|
||||
├── test_daemon.py # Daemon config, state, kill switch, dispatch check
|
||||
├── test_dispatch_monitor.py # Monitor safety features, env stripping
|
||||
├── test_dispatch_status.py # Log I/O, age calculation
|
||||
├── test_dispatch_watchdog.py # Watchdog auto-spawn
|
||||
├── test_wake.py # Branch resolution, PID checks, lock files
|
||||
├── test_wake_blocklist.py # Wake protection for @devpulse
|
||||
├── test_delivery.py # Inbox migration, private branches, pipeline
|
||||
├── test_send_identity.py # Sender identity chain (36 tests)
|
||||
├── test_user_paths.py # Mailbox path resolution (13 tests)
|
||||
├── test_contacts.py # Address book operations
|
||||
├── test_inbox_ops.py # Inbox loading + migration
|
||||
├── test_registry_read.py # Registry parsing + branch lookup
|
||||
├── test_central_writer.py # Central stats aggregation
|
||||
├── test_cli_routing.py # CLI routing + help/version
|
||||
├── test_json_handler.py # JSON I/O helpers
|
||||
├── test_notify.py # Desktop notification dbus calls
|
||||
└── test_paths.py # find_repo_root() utility
|
||||
```
|
||||
|
||||
## Integration Points
|
||||
@@ -73,16 +176,22 @@ ai_mail/
|
||||
- `aipass.prax` — Logging via `system_logger`
|
||||
- `aipass.cli` — Console output and display formatting
|
||||
- `aipass.drone` — Command routing and `@branch` resolution
|
||||
- Python stdlib (`pathlib`, `json`, `argparse`, `importlib`)
|
||||
- `aipass.trigger` — `trigger.fire()` for `email_dispatched` events
|
||||
- Python stdlib (`pathlib`, `json`, `argparse`, `importlib`, `subprocess`, `fcntl`)
|
||||
|
||||
### Provides To
|
||||
- All modules — inter-branch messaging (send/receive/reply/close)
|
||||
- Dispatch system — autonomous task execution via `--dispatch` flag
|
||||
- Branch contacts — address book for `@branch` routing
|
||||
- **All branches** — inter-branch messaging (send/receive/reply/close)
|
||||
- **Dispatch system** — autonomous task execution via `auto_execute` emails
|
||||
- **Branch contacts** — address book for `@branch` routing
|
||||
- **trigger branch** — `deliver_email_to_branch()` imported directly for event-driven delivery
|
||||
- **Desktop** — dbus notifications for delivery, wake, completion events
|
||||
|
||||
## Known Issues
|
||||
|
||||
- **DPLAN-0138**: Inbox backdoor audit identified 2 write path classes — ad-hoc direct writes (detectable by non-UUID ID format) and `_deliver_via_reply_path()` bypass (no lock, no notification). Fix pending.
|
||||
- **Caller detection**: `BRANCH DETECTION FAILED` when callers don't set `AIPASS_CALLER_BRANCH` (low severity, caller-side fix — use `--from` flag)
|
||||
- **Cross-branch writes**: ai_mail not in trusted cross-writers list for `system-pr`
|
||||
|
||||
---
|
||||
|
||||
*Last Updated: 2026-04-07*
|
||||
|
||||
---
|
||||
[← Back to AIPass](../../../README.md)
|
||||
|
||||
Reference in New Issue
Block a user