diff --git a/src/aipass/ai_mail/README.md b/src/aipass/ai_mail/README.md index a6cd3cf5..7ae5e78c 100644 --- a/src/aipass/ai_mail/README.md +++ b/src/aipass/ai_mail/README.md @@ -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 # View email (marks as opened) +drone @ai_mail view latest # View most recent email + +# Resolve mail +drone @ai_mail reply "message" # Reply + close + archive original +drone @ai_mail close # Close single email +drone @ai_mail close # 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 `/.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)