@agenticmail/openclaw 0.3.1 → 0.4.0

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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [OpenClaw](https://github.com/openclaw/openclaw) plugin for [AgenticMail](https://github.com/agenticmail/agenticmail) — gives any OpenClaw agent full email capabilities, inter-agent messaging, task coordination, and outbound security.
4
4
 
5
- This plugin provides 54 tools, a complete email channel integration, automatic sub-agent provisioning, inter-agent message rate limiting, and a built-in follow-up system for blocked emails. It also includes a skill definition with system prompt guidelines that teach agents how to handle email professionally and securely.
5
+ This plugin provides 63 tools, a complete email channel integration, automatic sub-agent provisioning, inter-agent message rate limiting, and a built-in follow-up system for blocked emails. It also includes a skill definition with system prompt guidelines that teach agents how to handle email professionally and securely.
6
6
 
7
7
  ## Install
8
8
 
@@ -108,7 +108,7 @@ Plugin configuration lives in `~/.openclaw/openclaw.json` (user config), not in
108
108
 
109
109
  ---
110
110
 
111
- ## Tools (54 total)
111
+ ## Tools (61 total)
112
112
 
113
113
  ### Core Email (8 tools)
114
114
 
@@ -273,6 +273,19 @@ Self-messaging is also prevented — an agent cannot send a message to itself.
273
273
 
274
274
  ## Skill Definition
275
275
 
276
+ ### SMS / Phone Number (8 tools)
277
+
278
+ | Tool | Description |
279
+ |------|-------------|
280
+ | `agenticmail_sms_setup` | Configure Google Voice phone number for SMS access |
281
+ | `agenticmail_sms_send` | Record and send SMS via Google Voice |
282
+ | `agenticmail_sms_messages` | List inbound/outbound SMS messages |
283
+ | `agenticmail_sms_check_code` | Extract verification/OTP codes from recent SMS |
284
+ | `agenticmail_sms_read_voice` | Read SMS directly from Google Voice web (fastest method) |
285
+ | `agenticmail_sms_record` | Record an SMS read from Google Voice web or any source |
286
+ | `agenticmail_sms_parse_email` | Parse SMS from forwarded Google Voice email (fallback) |
287
+ | `agenticmail_sms_config` | Get current SMS configuration |
288
+
276
289
  The plugin includes a skill at `skill/SKILL.md` that gets injected into the agent's system prompt. It covers:
277
290
 
278
291
  ### Email Rules
package/dist/index.js CHANGED
@@ -2020,6 +2020,170 @@ ${orig.text || ""}`;
2020
2020
  }
2021
2021
  }
2022
2022
  });
2023
+ reg("agenticmail_sms_setup", {
2024
+ description: "Configure SMS/phone number access via Google Voice. The user must have a Google Voice account with SMS-to-email forwarding enabled. This gives the agent a phone number for receiving verification codes and sending texts.",
2025
+ parameters: {
2026
+ phoneNumber: { type: "string", required: true, description: "Google Voice phone number (e.g. +12125551234)" },
2027
+ forwardingEmail: { type: "string", description: "Email address Google Voice forwards SMS to (defaults to agent email)" }
2028
+ },
2029
+ handler: async (params) => {
2030
+ try {
2031
+ const c = await ctxForParams(ctx, params);
2032
+ return await apiRequest(c, "POST", "/sms/setup", {
2033
+ phoneNumber: params.phoneNumber,
2034
+ forwardingEmail: params.forwardingEmail,
2035
+ provider: "google_voice"
2036
+ });
2037
+ } catch (err) {
2038
+ return { success: false, error: err.message };
2039
+ }
2040
+ }
2041
+ });
2042
+ reg("agenticmail_sms_send", {
2043
+ description: "Send an SMS text message via Google Voice. Records the message and provides instructions for sending via Google Voice web interface. The agent can automate the actual send using the browser tool on voice.google.com.",
2044
+ parameters: {
2045
+ to: { type: "string", required: true, description: "Recipient phone number" },
2046
+ body: { type: "string", required: true, description: "Text message body" }
2047
+ },
2048
+ handler: async (params) => {
2049
+ try {
2050
+ const c = await ctxForParams(ctx, params);
2051
+ return await apiRequest(c, "POST", "/sms/send", {
2052
+ to: params.to,
2053
+ body: params.body
2054
+ });
2055
+ } catch (err) {
2056
+ return { success: false, error: err.message };
2057
+ }
2058
+ }
2059
+ });
2060
+ reg("agenticmail_sms_messages", {
2061
+ description: "List SMS messages (inbound and outbound). Use direction filter to see only received or sent messages.",
2062
+ parameters: {
2063
+ direction: { type: "string", description: 'Filter: "inbound" or "outbound" (default: both)' },
2064
+ limit: { type: "number", description: "Max messages (default: 20)" },
2065
+ offset: { type: "number", description: "Skip messages (default: 0)" }
2066
+ },
2067
+ handler: async (params) => {
2068
+ try {
2069
+ const c = await ctxForParams(ctx, params);
2070
+ const query = new URLSearchParams();
2071
+ if (params.direction) query.set("direction", params.direction);
2072
+ if (params.limit) query.set("limit", String(params.limit));
2073
+ if (params.offset) query.set("offset", String(params.offset));
2074
+ return await apiRequest(c, "GET", `/sms/messages?${query.toString()}`);
2075
+ } catch (err) {
2076
+ return { success: false, error: err.message };
2077
+ }
2078
+ }
2079
+ });
2080
+ reg("agenticmail_sms_check_code", {
2081
+ description: `Check for recent verification/OTP codes received via SMS. Scans inbound SMS for common code patterns (6-digit, 4-digit, alphanumeric). Use this after requesting a verification code during sign-up flows.
2082
+
2083
+ RECOMMENDED FLOW for reading verification codes:
2084
+ 1. FIRST (fastest): Open Google Voice directly in the browser:
2085
+ - Navigate to https://voice.google.com/u/0/messages
2086
+ - Take a screenshot or snapshot to read the latest messages
2087
+ - The code will be visible in the message list (no click needed for recent ones)
2088
+ - Use agenticmail_sms_record to save the SMS and extract the code
2089
+
2090
+ 2. FALLBACK: If browser is unavailable, this tool checks the SMS database
2091
+ (populated by email forwarding from Google Voice, which can be delayed 1-5 minutes)`,
2092
+ parameters: {
2093
+ minutes: { type: "number", description: "How many minutes back to check (default: 10)" }
2094
+ },
2095
+ handler: async (params) => {
2096
+ try {
2097
+ const c = await ctxForParams(ctx, params);
2098
+ const query = params.minutes ? `?minutes=${params.minutes}` : "";
2099
+ return await apiRequest(c, "GET", `/sms/verification-code${query}`);
2100
+ } catch (err) {
2101
+ return { success: false, error: err.message };
2102
+ }
2103
+ }
2104
+ });
2105
+ reg("agenticmail_sms_read_voice", {
2106
+ description: `Read SMS messages directly from Google Voice web interface (FASTEST method). Opens voice.google.com in the browser, reads recent messages, and returns any found SMS with verification codes extracted. This is the PRIMARY way to check for SMS - much faster than waiting for email forwarding.
2107
+
2108
+ Use this when:
2109
+ - Waiting for a verification code after signing up for a service
2110
+ - Checking for recent SMS messages
2111
+ - Email forwarding hasn't delivered the SMS yet
2112
+
2113
+ The agent must have browser access and a Google Voice session (logged into Google in the browser profile).`,
2114
+ parameters: {},
2115
+ handler: async (params) => {
2116
+ try {
2117
+ const c = await ctxForParams(ctx, params);
2118
+ const configResp = await apiRequest(c, "GET", "/sms/config");
2119
+ const phoneNumber = configResp?.sms?.phoneNumber || "unknown";
2120
+ return {
2121
+ method: "google_voice_web",
2122
+ phoneNumber,
2123
+ instructions: [
2124
+ "Open the browser to: https://voice.google.com/u/0/messages",
2125
+ "Take a screenshot to see the message list",
2126
+ "Recent SMS messages appear in the left sidebar with sender number and preview text",
2127
+ "For verification codes, the code is usually visible in the preview without clicking",
2128
+ "If you need the full message, click on the conversation",
2129
+ "After reading, use agenticmail_sms_record to save the SMS to the database"
2130
+ ],
2131
+ browserUrl: "https://voice.google.com/u/0/messages",
2132
+ tip: "This is much faster than email forwarding. Google Voice web shows messages instantly."
2133
+ };
2134
+ } catch (err) {
2135
+ return { success: false, error: err.message };
2136
+ }
2137
+ }
2138
+ });
2139
+ reg("agenticmail_sms_record", {
2140
+ description: "Record an SMS message that you read from Google Voice web or any other source. Saves it to the SMS database and extracts any verification codes. Use after reading a message from voice.google.com in the browser.",
2141
+ parameters: {
2142
+ from: { type: "string", required: true, description: "Sender phone number (e.g. +12065551234 or (206) 338-7285)" },
2143
+ body: { type: "string", required: true, description: "The SMS message text" }
2144
+ },
2145
+ handler: async (params) => {
2146
+ try {
2147
+ const c = await ctxForParams(ctx, params);
2148
+ return await apiRequest(c, "POST", "/sms/record", {
2149
+ from: params.from,
2150
+ body: params.body
2151
+ });
2152
+ } catch (err) {
2153
+ return { success: false, error: err.message };
2154
+ }
2155
+ }
2156
+ });
2157
+ reg("agenticmail_sms_parse_email", {
2158
+ description: "Parse an SMS from a forwarded Google Voice email. Use this when you receive an email from Google Voice containing an SMS. Extracts the sender number, message body, and any verification codes.",
2159
+ parameters: {
2160
+ emailBody: { type: "string", required: true, description: "The email body text to parse" },
2161
+ emailFrom: { type: "string", description: "The email sender address" }
2162
+ },
2163
+ handler: async (params) => {
2164
+ try {
2165
+ const c = await ctxForParams(ctx, params);
2166
+ return await apiRequest(c, "POST", "/sms/parse-email", {
2167
+ emailBody: params.emailBody,
2168
+ emailFrom: params.emailFrom
2169
+ });
2170
+ } catch (err) {
2171
+ return { success: false, error: err.message };
2172
+ }
2173
+ }
2174
+ });
2175
+ reg("agenticmail_sms_config", {
2176
+ description: "Get the current SMS/phone number configuration for this agent. Shows whether SMS is enabled, the phone number, and forwarding email.",
2177
+ parameters: {},
2178
+ handler: async (params) => {
2179
+ try {
2180
+ const c = await ctxForParams(ctx, params);
2181
+ return await apiRequest(c, "GET", "/sms/config");
2182
+ } catch (err) {
2183
+ return { success: false, error: err.message };
2184
+ }
2185
+ }
2186
+ });
2023
2187
  }
2024
2188
 
2025
2189
  // src/channel.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticmail/openclaw",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "OpenClaw plugin and skill for AgenticMail",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -14,6 +14,7 @@
14
14
  "files": [
15
15
  "dist",
16
16
  "openclaw.plugin.json",
17
+ "scripts",
17
18
  "skill",
18
19
  "README.md",
19
20
  "REFERENCE.md",
@@ -26,7 +27,8 @@
26
27
  },
27
28
  "scripts": {
28
29
  "build": "tsup index.ts --format esm --dts --clean",
29
- "test": "vitest run",
30
+ "test": "vitest run --passWithNoTests",
31
+ "preuninstall": "node scripts/uninstall.mjs",
30
32
  "prepublishOnly": "npm run build"
31
33
  },
32
34
  "dependencies": {},
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Cleanup script that runs on `npm uninstall @agenticmail/openclaw`.
5
+ * Removes the agenticmail plugin entry from ~/.openclaw/openclaw.json
6
+ * and cleans up the plugin load path so OpenClaw doesn't error on
7
+ * a missing plugin.
8
+ */
9
+
10
+ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
11
+ import { join } from 'node:path';
12
+ import { homedir } from 'node:os';
13
+
14
+ const configPath = join(homedir(), '.openclaw', 'openclaw.json');
15
+
16
+ if (!existsSync(configPath)) process.exit(0);
17
+
18
+ try {
19
+ const raw = readFileSync(configPath, 'utf-8');
20
+ const config = JSON.parse(raw);
21
+
22
+ let changed = false;
23
+
24
+ // Remove plugins.entries.agenticmail
25
+ if (config.plugins?.entries?.agenticmail) {
26
+ delete config.plugins.entries.agenticmail;
27
+ changed = true;
28
+
29
+ // Clean up empty entries object
30
+ if (Object.keys(config.plugins.entries).length === 0) {
31
+ delete config.plugins.entries;
32
+ }
33
+ }
34
+
35
+ // Remove our path from plugins.load.paths
36
+ if (Array.isArray(config.plugins?.load?.paths)) {
37
+ const before = config.plugins.load.paths.length;
38
+ config.plugins.load.paths = config.plugins.load.paths.filter(
39
+ (p) => !p.includes('@agenticmail/openclaw')
40
+ );
41
+ if (config.plugins.load.paths.length !== before) changed = true;
42
+
43
+ // Clean up empty paths array
44
+ if (config.plugins.load.paths.length === 0) {
45
+ delete config.plugins.load.paths;
46
+ if (Object.keys(config.plugins.load).length === 0) {
47
+ delete config.plugins.load;
48
+ }
49
+ }
50
+ }
51
+
52
+ if (changed) {
53
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
54
+ console.log('[agenticmail] Cleaned up OpenClaw config:', configPath);
55
+ }
56
+ } catch {
57
+ // Don't fail the uninstall if cleanup fails
58
+ }