openclaw-agentmail-listener 0.4.1 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -51
- package/dist/index.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,52 +5,27 @@ An [OpenClaw](https://openclaw.ai) plugin that listens for incoming emails via [
|
|
|
5
5
|
## What it does
|
|
6
6
|
|
|
7
7
|
- Registers a background service at gateway startup
|
|
8
|
-
- Connects to AgentMail via WebSocket
|
|
8
|
+
- Connects to AgentMail via raw WebSocket (`wss://ws.agentmail.to/v0`)
|
|
9
9
|
- Subscribes to a configured inbox (e.g. `nickbot@agentmail.to`)
|
|
10
|
-
- When a `message.received` event fires, injects a system event
|
|
10
|
+
- When a `message.received` event fires, injects a system event with email metadata (from, subject, preview)
|
|
11
|
+
- Wakes the agent immediately via `requestHeartbeatNow()` so emails are processed right away
|
|
11
12
|
- Auto-reconnects with exponential backoff on disconnect
|
|
13
|
+
- Keepalive pings every 30 seconds
|
|
12
14
|
- Stops cleanly when the gateway shuts down
|
|
13
15
|
|
|
14
16
|
This is **not** a channel plugin — it doesn't handle replies. It just triggers system events so the agent notices new emails and can decide what to do (e.g. read the full message via the AgentMail skill and reply).
|
|
15
17
|
|
|
16
18
|
## Installation
|
|
17
19
|
|
|
18
|
-
### Option 1: Install from npm (recommended)
|
|
19
|
-
|
|
20
20
|
```bash
|
|
21
21
|
openclaw plugins install openclaw-agentmail-listener
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
Restart the gateway afterwards.
|
|
25
25
|
|
|
26
|
-
### Option 2: Install from GitHub
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
git clone https://github.com/thisnick/openclaw-agentmail ~/.openclaw/extensions/agentmail-listener
|
|
30
|
-
cd ~/.openclaw/extensions/agentmail-listener
|
|
31
|
-
npm install
|
|
32
|
-
openclaw gateway restart
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
### Option 3: Load via config path
|
|
36
|
-
|
|
37
|
-
In your OpenClaw config (`~/.openclaw/openclaw.json`):
|
|
38
|
-
|
|
39
|
-
```json
|
|
40
|
-
{
|
|
41
|
-
"plugins": {
|
|
42
|
-
"load": {
|
|
43
|
-
"paths": ["/path/to/openclaw-agentmail"]
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
Then run `npm install` in the plugin directory and restart the gateway.
|
|
50
|
-
|
|
51
26
|
## Configuration
|
|
52
27
|
|
|
53
|
-
Add to your OpenClaw config under `plugins.entries
|
|
28
|
+
Add to your OpenClaw config under `plugins.entries`:
|
|
54
29
|
|
|
55
30
|
```json
|
|
56
31
|
{
|
|
@@ -60,9 +35,7 @@ Add to your OpenClaw config under `plugins.entries.agentmail-listener.config`:
|
|
|
60
35
|
"enabled": true,
|
|
61
36
|
"config": {
|
|
62
37
|
"apiKey": "am_us_your_key_here",
|
|
63
|
-
"inboxId": "yourbot@agentmail.to"
|
|
64
|
-
"eventTypes": ["message.received"],
|
|
65
|
-
"sessionKey": "agent:main:main"
|
|
38
|
+
"inboxId": "yourbot@agentmail.to"
|
|
66
39
|
}
|
|
67
40
|
}
|
|
68
41
|
}
|
|
@@ -78,7 +51,6 @@ Add to your OpenClaw config under `plugins.entries.agentmail-listener.config`:
|
|
|
78
51
|
| `inboxId` | string | ✅ | — | Inbox to subscribe (e.g. `nickbot@agentmail.to`) |
|
|
79
52
|
| `eventTypes` | string[] | — | `["message.received"]` | Event types to subscribe to |
|
|
80
53
|
| `sessionKey` | string | — | `"agent:main:main"` | Agent session key for routing system events |
|
|
81
|
-
| `wake` | string | — | `"tools-invoke"` | Wake method: `"tools-invoke"` (default), `"hooks"`, or `"off"` |
|
|
82
54
|
|
|
83
55
|
## System event format
|
|
84
56
|
|
|
@@ -91,33 +63,49 @@ Subject: Hello there
|
|
|
91
63
|
Preview: This is the first 200 chars of the email body...
|
|
92
64
|
```
|
|
93
65
|
|
|
94
|
-
The event is keyed with `contextKey: agentmail:<messageId>`
|
|
66
|
+
The event is keyed with `contextKey: cron:agentmail:<messageId>` so it is both deduplicated and surfaced in the heartbeat prompt (see [How wake works](#how-wake-works) for details).
|
|
67
|
+
|
|
68
|
+
## How wake works
|
|
69
|
+
|
|
70
|
+
After enqueuing a system event, the plugin calls `requestHeartbeatNow()` with reason `"exec-event"`. This does two things:
|
|
95
71
|
|
|
96
|
-
|
|
72
|
+
1. **Bypasses file gates** — the heartbeat fires immediately without requiring HEARTBEAT.md
|
|
73
|
+
2. **Inspects pending events** — the enqueued system event is included in the heartbeat prompt
|
|
97
74
|
|
|
98
|
-
|
|
75
|
+
The `cron:` prefix on the contextKey ensures the event passes through OpenClaw's `hasTaggedCronEvents` check, which enables event inspection and renders the email content via `buildCronEventPrompt`. Without this prefix, the event would be enqueued but silently discarded from the prompt.
|
|
99
76
|
|
|
100
|
-
|
|
77
|
+
> **Why not `reason: "wake"`?** The `"wake"` reason bypasses file gates but does _not_ enable `shouldInspectPendingEvents` in the heartbeat runner, so system events are ignored. `"exec-event"` enables both.
|
|
101
78
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
79
|
+
**Important config for proactive delivery:**
|
|
80
|
+
|
|
81
|
+
The heartbeat `target` must be set to a channel (e.g. `"whatsapp"`, `"telegram"`) or `"last"` — otherwise the agent processes the email but the response is silently dropped (default target is `"none"`).
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"agents": {
|
|
86
|
+
"defaults": {
|
|
87
|
+
"heartbeat": {
|
|
88
|
+
"every": "1h",
|
|
89
|
+
"target": "whatsapp"
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
107
95
|
|
|
108
|
-
The
|
|
96
|
+
**Note:** The `requests-in-flight` check is global — if any conversation is active on any channel, the wake is deferred (retries every 1s until the queue clears).
|
|
109
97
|
|
|
110
|
-
## Architecture
|
|
98
|
+
## Architecture
|
|
111
99
|
|
|
112
|
-
-
|
|
113
|
-
- Reconnection
|
|
114
|
-
-
|
|
115
|
-
-
|
|
116
|
-
-
|
|
100
|
+
- **Raw WebSocket** — connects directly to `wss://ws.agentmail.to/v0` with API key as query param (no SDK dependency for the WebSocket layer)
|
|
101
|
+
- **Reconnection** — exponential backoff (1s → 2s → 4s → ... → 60s max) with ±10% jitter
|
|
102
|
+
- **Keepalive** — sends WebSocket pings every 30 seconds
|
|
103
|
+
- **In-process wake** — uses `api.runtime.system.requestHeartbeatNow()` with `reason: "exec-event"` (no HTTP round-trips)
|
|
104
|
+
- **System events** — uses `api.runtime.system.enqueueSystemEvent()` with `cron:`-prefixed contextKey to ensure prompt visibility
|
|
117
105
|
|
|
118
106
|
## Dependencies
|
|
119
107
|
|
|
120
|
-
- `
|
|
108
|
+
- `ws` ^8.18.0 — WebSocket client for Node.js
|
|
121
109
|
|
|
122
110
|
## License
|
|
123
111
|
|
package/dist/index.js
CHANGED
|
@@ -192,10 +192,10 @@ function handleEventInner(api, cfg, event) {
|
|
|
192
192
|
const sessionKey = cfg.sessionKey ?? "agent:main:main";
|
|
193
193
|
api.runtime.system.enqueueSystemEvent(eventText, {
|
|
194
194
|
sessionKey,
|
|
195
|
-
contextKey: `agentmail:${messageId}`
|
|
195
|
+
contextKey: `cron:agentmail:${messageId}`
|
|
196
196
|
});
|
|
197
197
|
api.runtime.system.requestHeartbeatNow({
|
|
198
|
-
reason: "
|
|
198
|
+
reason: "exec-event",
|
|
199
199
|
sessionKey
|
|
200
200
|
});
|
|
201
201
|
api.logger.info("agentmail-listener: heartbeat wake requested");
|