@ross/openclaw-channel-agentmail (1.0.0-alpha.6)
Installation
@ross:registry=npm install @ross/openclaw-channel-agentmail@1.0.0-alpha.6"@ross/openclaw-channel-agentmail": "1.0.0-alpha.6"About this package
AgentMail Channel
🦊 OpenClaw channel plugin for AgentMail — turn email inboxes into OpenClaw chat conversations.
This plugin lets your OpenClaw agent send and receive emails through AgentMail's API, treating each email thread as a conversation channel. Built on OpenClaw's channel plugin SDK.
Features
- Inbox as channel — Each AgentMail inbox becomes a messaging channel
- Threaded conversations — AgentMail threads map to OpenClaw session threads
- Two-way email — Send and receive emails directly from OpenClaw
- DM security — Configurable sender allowlists and pairing flows
- Webhook-ready — Supports AgentMail webhooks for real-time inbound email
- 📧 Smart classification — Rule-based classifier sorts emails into marketing / notification / personal / unknown
- 📨 Marketing digest — Batches marketing emails into scheduled digests instead of spamming the agent
- 🔔 Notification handling — Allowlisted notifications get context injection so the agent knows when to act vs. stay silent
- 🤖 AI fallback — Optional lightweight LLM classification for ambiguous emails (configurable, default off)
- 🔇 Silent mode — Suppress non-essential log noise
Installation
openclaw plugins install @ross/openclaw-channel-agentmail
Or for development:
git clone https://git.reslate.solutions/ross/agentmail-channel.git
cd agentmail-channel
npm install
npm run build
Configuration
- Get an API key from the AgentMail Console
- Set up the channel in your OpenClaw config:
{
"channels": {
"agentmail": {
"accounts": {
"default": {
"apiKey": "your-api-key",
"inboxId": "your-inbox-id",
"ownerEmail": "you@example.com",
"forwardUnrecognized": true,
"ignoreFrom": ["*@spamdomain.com", "noreply-*@badsender.com"],
"allowFrom": ["trusted@partner.com"],
"classifyInbound": true,
"aiClassificationFallback": false,
"marketingDigest": {
"enabled": true,
"schedule": "0 9 * * *",
"maxQueueSize": 100
},
"notificationAction": {
"enabled": true,
"autoAction": false
},
"silentMode": false
}
}
}
}
}
Or via environment variable:
export AGENTMAIL_API_KEY="your-api-key"
Classification & Routing
When classifyInbound: true, every inbound email is classified before routing:
| Category | Rule triggers | What happens |
|---|---|---|
| Marketing | Subject keywords (sale, newsletter, promo…), sender patterns (marketing@, mailchimp…), body signals (unsubscribe, opt-out…) | Queued for digest — never forwarded or dispatched to AI |
| Notification | Subject keywords (issue #, pull request, alert…), sender patterns (notifications@, noreply@github.com…), structural cues (no salutation, action buttons…) | Allowlisted senders → inject context, agent decides action. Unknown sender → forwarded to owner if forwardUnrecognized is enabled |
| Personal / Business | Low scores on both marketing and notification sides | Dispatched to AI normally |
| Unknown | Ambiguous signals, or empty content | If aiClassificationFallback: true, a lightweight LLM prompt classifies it. Otherwise treated as personal |
Marketing Digest
Marketing emails are batched and sent as a single digest email to ownerEmail on a schedule (default daily at 9 AM). Configure with:
marketingDigest.enabled— turn digest on/offmarketingDigest.schedule— cron-like string or shorthand (daily,hourly,2h,30m)marketingDigest.maxQueueSize— cap the queue to prevent unbounded growth
Forward Unrecognized
When an unknown sender (not in allowFrom or ignoreFrom) sends an email:
- If
forwardUnrecognized: trueandownerEmailis set → forward the email to owner with instructions - Owner replies to the forward → agent sees the reply with context about the original sender
- Owner can say "approve", "ignore", or "reply to sender: …"
Silent Mode
Set silentMode: true to suppress non-essential classification and routing log messages.
Development
npm install # Install dependencies
npm run build # Compile TypeScript
npm run dev # Watch mode
npm test # Run tests
npm run lint # Lint code
npm run format # Format code
Commit Convention
This project uses Conventional Commits with commitlint enforcement.
feat: add outbound email sending
fix: resolve inboxId parsing for multi-tenant pods
docs: update README with setup instructions
Release Workflow
Automated via semantic-release on push to main:
- Analyzes commits for version bump (fix → patch, feat → minor, BREAKING → major)
- Generates CHANGELOG
- Publishes to git.reslate.solutions npm registry
Project Structure
src/
├── index.ts # Plugin entry point
├── channel-plugin.ts # Channel plugin definition
├── config.ts # Config resolution
├── outbound.ts # Outbound messaging (send email)
├── websocket.ts # WebSocket client for real-time inbound
├── gateway.ts # Gateway adapter (start/stop accounts, inbound routing)
├── classification.ts # Rule-based email classifier + AI fallback hook
├── digest.ts # Marketing digest builder + scheduled sender
├── security.ts # Allowlist / ignore-list logic
├── session-grammar.ts # Conversation/thread mapping
└── types.ts # Public type exports
tests/
├── unit/ # Unit tests
└── integration/ # Integration tests
License
MIT — see LICENSE
Dependencies
Development dependencies
| ID | Version |
|---|---|
| @commitlint/cli | ^19.0.0 |
| @commitlint/config-conventional | ^19.0.0 |
| @semantic-release/changelog | ^6.0.3 |
| @semantic-release/commit-analyzer | ^13.0.0 |
| @semantic-release/exec | ^7.1.0 |
| @semantic-release/git | ^10.0.1 |
| @semantic-release/npm | ^12.0.0 |
| @semantic-release/release-notes-generator | ^14.0.0 |
| @types/node | ^22.0.0 |
| conventional-changelog-conventionalcommits | ^8.0.0 |
| eslint | ^9.0.0 |
| eslint-config-prettier | ^10.1.8 |
| husky | ^9.0.0 |
| prettier | ^3.0.0 |
| semantic-release | ^25.0.0 |
| typescript | ^5.5.0 |
| typescript-eslint | ^8.59.2 |
| vitest | ^3.0.0 |
Peer dependencies
| ID | Version |
|---|---|
| openclaw | * |