machinaos 0.0.12 → 0.0.13

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.
Files changed (41) hide show
  1. package/client/package.json +1 -1
  2. package/client/src/components/OutputPanel.tsx +3 -2
  3. package/client/src/components/parameterPanel/InputSection.tsx +4 -3
  4. package/package.json +1 -1
  5. package/server/routers/websocket.py +5 -5
  6. package/server/services/ai.py +38 -13
  7. package/server/services/deployment/manager.py +14 -0
  8. package/server/services/execution/executor.py +2 -0
  9. package/server/services/execution/models.py +8 -0
  10. package/server/services/handlers/ai.py +71 -23
  11. package/server/services/handlers/tools.py +103 -6
  12. package/server/skills/android_agent/app-launcher-skill/SKILL.md +137 -0
  13. package/server/skills/android_agent/app-list-skill/SKILL.md +148 -0
  14. package/server/skills/android_agent/audio-skill/SKILL.md +169 -0
  15. package/server/skills/android_agent/battery-skill/SKILL.md +114 -0
  16. package/server/skills/android_agent/bluetooth-skill/SKILL.md +151 -0
  17. package/server/skills/android_agent/camera-skill/SKILL.md +148 -0
  18. package/server/skills/android_agent/environmental-skill/SKILL.md +140 -0
  19. package/server/skills/android_agent/location-skill/SKILL.md +163 -0
  20. package/server/skills/android_agent/motion-skill/SKILL.md +141 -0
  21. package/server/skills/android_agent/screen-control-skill/SKILL.md +164 -0
  22. package/server/skills/android_agent/wifi-skill/SKILL.md +182 -0
  23. package/server/skills/assistant/subagent-skill/SKILL.md +62 -30
  24. package/server/skills/coding_agent/javascript-skill/SKILL.md +196 -0
  25. package/server/skills/coding_agent/python-skill/SKILL.md +165 -0
  26. package/server/skills/social_agent/whatsapp-db-skill/SKILL.md +284 -0
  27. package/server/skills/social_agent/whatsapp-send-skill/SKILL.md +180 -0
  28. package/server/skills/task_agent/cron-scheduler-skill/SKILL.md +215 -0
  29. package/server/skills/task_agent/task-manager-skill/SKILL.md +251 -0
  30. package/server/skills/task_agent/timer-skill/SKILL.md +168 -0
  31. package/server/skills/travel_agent/geocoding-skill/SKILL.md +186 -0
  32. package/server/skills/travel_agent/nearby-places-skill/SKILL.md +234 -0
  33. package/server/skills/web_agent/http-request-skill/SKILL.md +211 -0
  34. package/server/skills/android/skill/SKILL.md +0 -84
  35. package/server/skills/assistant/code-skill/SKILL.md +0 -176
  36. package/server/skills/assistant/http-skill/SKILL.md +0 -163
  37. package/server/skills/assistant/maps-skill/SKILL.md +0 -172
  38. package/server/skills/assistant/scheduler-skill/SKILL.md +0 -86
  39. package/server/skills/assistant/whatsapp-skill/SKILL.md +0 -285
  40. /package/server/skills/{android → android_agent}/personality/SKILL.md +0 -0
  41. /package/server/skills/{assistant → web_agent}/web-search-skill/SKILL.md +0 -0
@@ -0,0 +1,165 @@
1
+ ---
2
+ name: python-skill
3
+ description: Execute Python code for calculations, data processing, and automation. Access to math, json, datetime, collections, and more.
4
+ allowed-tools: python_code
5
+ metadata:
6
+ author: machina
7
+ version: "1.0"
8
+ category: code
9
+ icon: "🐍"
10
+ color: "#3776AB"
11
+ ---
12
+
13
+ # Python Code Execution Tool
14
+
15
+ Execute Python code for calculations, data processing, and automation tasks.
16
+
17
+ ## How It Works
18
+
19
+ This skill provides instructions for the **Python Executor** tool node. Connect the **Python Executor** node to Zeenie's `input-tools` handle to enable Python code execution.
20
+
21
+ ## python_code Tool
22
+
23
+ Execute Python code and return results.
24
+
25
+ ### Schema Fields
26
+
27
+ | Field | Type | Required | Description |
28
+ |-------|------|----------|-------------|
29
+ | code | string | Yes | Python code to execute |
30
+
31
+ ### Available Libraries
32
+
33
+ | Library | Description |
34
+ |---------|-------------|
35
+ | `math` | Mathematical functions (sqrt, pow, sin, cos, etc.) |
36
+ | `json` | JSON encoding/decoding |
37
+ | `datetime` | Date and time manipulation |
38
+ | `timedelta` | Time duration calculations |
39
+ | `re` | Regular expressions |
40
+ | `random` | Random number generation |
41
+ | `Counter` | Count hashable objects |
42
+ | `defaultdict` | Dictionary with default values |
43
+
44
+ ### Built-in Variables
45
+
46
+ | Variable | Description |
47
+ |----------|-------------|
48
+ | `input_data` | Data from connected workflow nodes (dict) |
49
+ | `output` | Set this to return a result |
50
+
51
+ ### Output Methods
52
+
53
+ 1. **Set `output` variable**: Returns structured data to the workflow
54
+ 2. **Use `print()`**: Captured as console output
55
+
56
+ ### Examples
57
+
58
+ **Basic calculation:**
59
+ ```json
60
+ {
61
+ "code": "result = 25 * 4 + 10\nprint(f'Result: {result}')\noutput = result"
62
+ }
63
+ ```
64
+
65
+ **Calculate tip:**
66
+ ```json
67
+ {
68
+ "code": "bill = 85.50\ntip_percent = 15\ntip = bill * (tip_percent / 100)\ntotal = bill + tip\nprint(f'Tip: ${tip:.2f}')\nprint(f'Total: ${total:.2f}')\noutput = {'tip': tip, 'total': total}"
69
+ }
70
+ ```
71
+
72
+ **Generate random numbers:**
73
+ ```json
74
+ {
75
+ "code": "import random\nnumbers = [random.randint(1, 100) for _ in range(5)]\nprint(f'Random numbers: {numbers}')\noutput = numbers"
76
+ }
77
+ ```
78
+
79
+ **Date calculations:**
80
+ ```json
81
+ {
82
+ "code": "from datetime import datetime, timedelta\ntoday = datetime.now()\nfuture = today + timedelta(days=30)\nresult = future.strftime('%Y-%m-%d')\nprint(f'30 days from now: {result}')\noutput = result"
83
+ }
84
+ ```
85
+
86
+ **Process data:**
87
+ ```json
88
+ {
89
+ "code": "data = input_data.get('numbers', [1, 2, 3, 4, 5])\ntotal = sum(data)\naverage = total / len(data)\nprint(f'Total: {total}, Average: {average}')\noutput = {'total': total, 'average': average}"
90
+ }
91
+ ```
92
+
93
+ **Parse JSON:**
94
+ ```json
95
+ {
96
+ "code": "import json\njson_str = '{\"name\": \"John\", \"age\": 30}'\ndata = json.loads(json_str)\nprint(f'Name: {data[\"name\"]}')\noutput = data"
97
+ }
98
+ ```
99
+
100
+ **Count items:**
101
+ ```json
102
+ {
103
+ "code": "from collections import Counter\nitems = ['apple', 'banana', 'apple', 'cherry', 'banana', 'apple']\ncounts = dict(Counter(items))\nprint(f'Counts: {counts}')\noutput = counts"
104
+ }
105
+ ```
106
+
107
+ **Text processing:**
108
+ ```json
109
+ {
110
+ "code": "import re\ntext = 'Contact: john@example.com or jane@test.org'\nemails = re.findall(r'\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b', text)\nprint(f'Found emails: {emails}')\noutput = emails"
111
+ }
112
+ ```
113
+
114
+ ### Response Format
115
+
116
+ **Success:**
117
+ ```json
118
+ {
119
+ "success": true,
120
+ "result": {"tip": 12.825, "total": 98.325},
121
+ "output": "Tip: $12.83\nTotal: $98.33"
122
+ }
123
+ ```
124
+
125
+ **Error:**
126
+ ```json
127
+ {
128
+ "error": "name 'undefined_var' is not defined"
129
+ }
130
+ ```
131
+
132
+ ## Use Cases
133
+
134
+ | Use Case | Approach |
135
+ |----------|----------|
136
+ | Math calculations | Use `math` library functions |
137
+ | Date/time operations | Use `datetime` and `timedelta` |
138
+ | Data analysis | Use list comprehensions, sum, len |
139
+ | Random generation | Use `random` library |
140
+ | Text parsing | Use `re` regular expressions |
141
+ | JSON manipulation | Use `json.loads()` and `json.dumps()` |
142
+ | Counting | Use `Counter` from collections |
143
+
144
+ ## Guidelines
145
+
146
+ 1. **Always set `output`**: This returns data to the workflow
147
+ 2. **Use `print()` for debugging**: Output is captured and returned
148
+ 3. **Keep code focused**: One task per execution
149
+ 4. **Handle errors**: Use try/except for robust code
150
+ 5. **No network access**: Use http-skill for web requests
151
+ 6. **No file system access**: Restricted to safe operations
152
+ 7. **Timeout**: Default 30 seconds max execution time
153
+
154
+ ## Security Restrictions
155
+
156
+ - No network/socket operations
157
+ - No file system access outside designated areas
158
+ - No subprocess/shell commands
159
+ - Limited execution time (30 seconds)
160
+ - Sandboxed environment
161
+
162
+ ## Setup Requirements
163
+
164
+ 1. Connect the **Python Executor** node to Zeenie's `input-tools` handle
165
+ 2. Python must be installed on the server
@@ -0,0 +1,284 @@
1
+ ---
2
+ name: whatsapp-db-skill
3
+ description: Query WhatsApp database for contacts, groups, and chat history. Look up contact info, search groups, retrieve messages.
4
+ allowed-tools: whatsapp_db
5
+ metadata:
6
+ author: machina
7
+ version: "1.0"
8
+ category: messaging
9
+ icon: "🗃️"
10
+ color: "#25D366"
11
+ ---
12
+
13
+ # WhatsApp Database Tool
14
+
15
+ Query WhatsApp database for contacts, groups, and message history.
16
+
17
+ ## How It Works
18
+
19
+ This skill provides instructions for the **WhatsApp DB** tool node. Connect the **WhatsApp DB** node to Zeenie's `input-tools` handle to enable database queries.
20
+
21
+ ## whatsapp_db Tool
22
+
23
+ Query contacts, groups, and chat history.
24
+
25
+ ### Schema Fields
26
+
27
+ | Field | Type | Required | Description |
28
+ |-------|------|----------|-------------|
29
+ | operation | string | Yes | Operation type (see below) |
30
+ | chat_type | string | For chat_history | `"individual"` or `"group"` |
31
+ | phone | string | Varies | Phone number (for chat_history individual, get_contact_info) |
32
+ | group_id | string | Varies | Group JID (for chat_history group, get_group_info) |
33
+ | query | string | No | Search query (for search_groups, list_contacts) |
34
+ | limit | int | No | Max results (varies by operation) |
35
+ | offset | int | No | Pagination offset (for chat_history) |
36
+ | message_filter | string | No | `"all"` or `"text_only"` (for chat_history) |
37
+ | group_filter | string | No | `"all"` or `"contact"` (for group chat_history) |
38
+ | sender_phone | string | No | Filter by sender (when group_filter="contact") |
39
+ | phones | string | For check_contacts | Comma-separated phone numbers |
40
+ | participant_limit | int | No | Max participants (for get_group_info, 1-100) |
41
+
42
+ ### Operations
43
+
44
+ | Operation | Description | Required Fields |
45
+ |-----------|-------------|-----------------|
46
+ | `list_contacts` | List contacts with saved names | query (optional), limit |
47
+ | `get_contact_info` | Get full contact details | phone |
48
+ | `search_groups` | Search groups by name | query, limit |
49
+ | `get_group_info` | Get group details with participants | group_id |
50
+ | `chat_history` | Retrieve message history | chat_type, phone/group_id |
51
+ | `check_contacts` | Check WhatsApp registration | phones |
52
+
53
+ ### Limits
54
+
55
+ | Operation | Default | Max |
56
+ |-----------|---------|-----|
57
+ | chat_history | 50 | 500 |
58
+ | search_groups | 20 | 50 |
59
+ | list_contacts | 50 | 100 |
60
+ | get_group_info participants | 50 | 100 |
61
+
62
+ **Important:** Always use small limits and specific queries to avoid context overflow.
63
+
64
+ ## Operation Examples
65
+
66
+ ### list_contacts
67
+
68
+ Find contacts by name.
69
+
70
+ ```json
71
+ {
72
+ "operation": "list_contacts",
73
+ "query": "mom",
74
+ "limit": 10
75
+ }
76
+ ```
77
+
78
+ **Response:**
79
+ ```json
80
+ {
81
+ "success": true,
82
+ "operation": "list_contacts",
83
+ "contacts": [
84
+ {"phone": "919876543210", "name": "Mom", "jid": "919876543210@s.whatsapp.net"}
85
+ ],
86
+ "total": 1
87
+ }
88
+ ```
89
+
90
+ ### get_contact_info
91
+
92
+ Get full contact details for sending/replying.
93
+
94
+ ```json
95
+ {
96
+ "operation": "get_contact_info",
97
+ "phone": "919876543210"
98
+ }
99
+ ```
100
+
101
+ **Response:**
102
+ ```json
103
+ {
104
+ "success": true,
105
+ "operation": "get_contact_info",
106
+ "phone": "919876543210",
107
+ "name": "Mom",
108
+ "jid": "919876543210@s.whatsapp.net",
109
+ "profile_picture": "https://..."
110
+ }
111
+ ```
112
+
113
+ ### search_groups
114
+
115
+ Search groups by name.
116
+
117
+ ```json
118
+ {
119
+ "operation": "search_groups",
120
+ "query": "family",
121
+ "limit": 10
122
+ }
123
+ ```
124
+
125
+ **Response:**
126
+ ```json
127
+ {
128
+ "success": true,
129
+ "operation": "search_groups",
130
+ "groups": [
131
+ {"group_id": "120363123456789@g.us", "name": "Family Group", "participant_count": 5}
132
+ ],
133
+ "total": 1
134
+ }
135
+ ```
136
+
137
+ ### get_group_info
138
+
139
+ Get group details with participant list.
140
+
141
+ ```json
142
+ {
143
+ "operation": "get_group_info",
144
+ "group_id": "120363123456789@g.us",
145
+ "participant_limit": 20
146
+ }
147
+ ```
148
+
149
+ **Response:**
150
+ ```json
151
+ {
152
+ "success": true,
153
+ "operation": "get_group_info",
154
+ "name": "Family Group",
155
+ "jid": "120363123456789@g.us",
156
+ "participants": [
157
+ {"phone": "919876543210", "name": "Mom", "is_admin": true},
158
+ {"phone": "919876543211", "name": "Dad", "is_admin": false}
159
+ ],
160
+ "total_participants": 5
161
+ }
162
+ ```
163
+
164
+ ### chat_history (Individual)
165
+
166
+ Get messages from an individual chat.
167
+
168
+ ```json
169
+ {
170
+ "operation": "chat_history",
171
+ "chat_type": "individual",
172
+ "phone": "919876543210",
173
+ "limit": 20
174
+ }
175
+ ```
176
+
177
+ ### chat_history (Group)
178
+
179
+ Get messages from a group chat.
180
+
181
+ ```json
182
+ {
183
+ "operation": "chat_history",
184
+ "chat_type": "group",
185
+ "group_id": "120363123456789@g.us",
186
+ "message_filter": "text_only",
187
+ "limit": 50
188
+ }
189
+ ```
190
+
191
+ **Response:**
192
+ ```json
193
+ {
194
+ "success": true,
195
+ "operation": "chat_history",
196
+ "messages": [
197
+ {
198
+ "index": 1,
199
+ "message_id": "ABC123",
200
+ "sender": "919876543210@s.whatsapp.net",
201
+ "sender_name": "Mom",
202
+ "text": "Hello!",
203
+ "timestamp": "2025-01-30T12:00:00",
204
+ "is_from_me": false
205
+ }
206
+ ],
207
+ "total": 50,
208
+ "has_more": true
209
+ }
210
+ ```
211
+
212
+ ### check_contacts
213
+
214
+ Check if phone numbers have WhatsApp.
215
+
216
+ ```json
217
+ {
218
+ "operation": "check_contacts",
219
+ "phones": "1234567890,0987654321"
220
+ }
221
+ ```
222
+
223
+ **Response:**
224
+ ```json
225
+ {
226
+ "success": true,
227
+ "operation": "check_contacts",
228
+ "results": [
229
+ {"phone": "1234567890", "registered": true, "jid": "1234567890@s.whatsapp.net"},
230
+ {"phone": "0987654321", "registered": false}
231
+ ]
232
+ }
233
+ ```
234
+
235
+ ## Common Workflows
236
+
237
+ ### Find and message someone by name
238
+
239
+ 1. Use `list_contacts` with query to find the person
240
+ 2. Get their phone number from the result
241
+ 3. Use `whatsapp_send` tool with the phone number
242
+
243
+ ### Get recent messages from a contact
244
+
245
+ 1. Use `chat_history` with chat_type="individual"
246
+ 2. Set appropriate limit for desired history depth
247
+
248
+ ### Find and message a group
249
+
250
+ 1. Use `search_groups` to find the group by name
251
+ 2. Get the group_id from the result
252
+ 3. Use `whatsapp_send` tool with recipient_type="group"
253
+
254
+ ### See who's in a group
255
+
256
+ 1. Use `get_group_info` with the group_id
257
+ 2. Set participant_limit to control output size
258
+
259
+ ## Guidelines
260
+
261
+ 1. **Phone numbers**: Always use without + prefix, just digits
262
+ 2. **Group IDs**: JID format ending in `@g.us`
263
+ 3. **Limits**: Use small limits to avoid context overflow
264
+ 4. **Queries**: Be specific to narrow results
265
+ 5. **Pagination**: Use offset to page through chat_history
266
+
267
+ ## Error Responses
268
+
269
+ ```json
270
+ {
271
+ "error": "Phone number is required for chat_type='individual'"
272
+ }
273
+ ```
274
+
275
+ ```json
276
+ {
277
+ "error": "group_id is required for get_group_info"
278
+ }
279
+ ```
280
+
281
+ ## Setup Requirements
282
+
283
+ 1. Connect the **WhatsApp DB** node to Zeenie's `input-tools` handle
284
+ 2. Ensure WhatsApp is connected (green status indicator in Credentials)
@@ -0,0 +1,180 @@
1
+ ---
2
+ name: whatsapp-send-skill
3
+ description: Send WhatsApp messages to contacts or groups. Supports text, images, videos, audio, documents, stickers, locations, and contacts.
4
+ allowed-tools: whatsapp_send
5
+ metadata:
6
+ author: machina
7
+ version: "1.0"
8
+ category: messaging
9
+ icon: "📤"
10
+ color: "#25D366"
11
+ ---
12
+
13
+ # WhatsApp Send Tool
14
+
15
+ Send messages to WhatsApp contacts or groups.
16
+
17
+ ## How It Works
18
+
19
+ This skill provides instructions for the **WhatsApp Send** tool node. Connect the **WhatsApp Send** node to Zeenie's `input-tools` handle to enable message sending.
20
+
21
+ ## whatsapp_send Tool
22
+
23
+ Send messages to individual contacts or groups.
24
+
25
+ ### Schema Fields
26
+
27
+ | Field | Type | Required | Description |
28
+ |-------|------|----------|-------------|
29
+ | recipient_type | string | Yes | `"phone"` for individual or `"group"` for group chat |
30
+ | phone | string | If phone | Phone number without + prefix (e.g., `1234567890`) |
31
+ | group_id | string | If group | Group JID (e.g., `123456789@g.us`) |
32
+ | message_type | string | Yes | Message type (see below) |
33
+ | message | string | If text | Text message content |
34
+ | media_url | string | If media | URL for image/video/audio/document/sticker |
35
+ | caption | string | No | Caption for media messages |
36
+ | latitude | float | If location | Latitude coordinate |
37
+ | longitude | float | If location | Longitude coordinate |
38
+ | location_name | string | No | Display name for location |
39
+ | address | string | No | Address text for location |
40
+ | contact_name | string | If contact | Contact display name |
41
+ | vcard | string | If contact | vCard 3.0 format string |
42
+
43
+ ### Message Types
44
+
45
+ | Type | Required Fields | Description |
46
+ |------|-----------------|-------------|
47
+ | `text` | message | Plain text message |
48
+ | `image` | media_url | Image file (JPG, PNG, GIF) |
49
+ | `video` | media_url | Video file (MP4) |
50
+ | `audio` | media_url | Audio file (MP3, OGG, WAV) |
51
+ | `document` | media_url | Any file type |
52
+ | `sticker` | media_url | Sticker image (WebP) |
53
+ | `location` | latitude, longitude | GPS coordinates |
54
+ | `contact` | contact_name, vcard | Contact card |
55
+
56
+ ### Examples
57
+
58
+ **Send text message to contact:**
59
+ ```json
60
+ {
61
+ "recipient_type": "phone",
62
+ "phone": "1234567890",
63
+ "message_type": "text",
64
+ "message": "Hello! How are you?"
65
+ }
66
+ ```
67
+
68
+ **Send image with caption:**
69
+ ```json
70
+ {
71
+ "recipient_type": "phone",
72
+ "phone": "1234567890",
73
+ "message_type": "image",
74
+ "media_url": "https://example.com/photo.jpg",
75
+ "caption": "Check out this photo!"
76
+ }
77
+ ```
78
+
79
+ **Send to group:**
80
+ ```json
81
+ {
82
+ "recipient_type": "group",
83
+ "group_id": "123456789012345678@g.us",
84
+ "message_type": "text",
85
+ "message": "Hello everyone!"
86
+ }
87
+ ```
88
+
89
+ **Send video:**
90
+ ```json
91
+ {
92
+ "recipient_type": "phone",
93
+ "phone": "1234567890",
94
+ "message_type": "video",
95
+ "media_url": "https://example.com/video.mp4",
96
+ "caption": "Check this video"
97
+ }
98
+ ```
99
+
100
+ **Send document:**
101
+ ```json
102
+ {
103
+ "recipient_type": "phone",
104
+ "phone": "1234567890",
105
+ "message_type": "document",
106
+ "media_url": "https://example.com/report.pdf",
107
+ "caption": "Here's the report"
108
+ }
109
+ ```
110
+
111
+ **Send location:**
112
+ ```json
113
+ {
114
+ "recipient_type": "phone",
115
+ "phone": "1234567890",
116
+ "message_type": "location",
117
+ "latitude": 37.7749,
118
+ "longitude": -122.4194,
119
+ "location_name": "San Francisco",
120
+ "address": "San Francisco, CA, USA"
121
+ }
122
+ ```
123
+
124
+ **Send contact card:**
125
+ ```json
126
+ {
127
+ "recipient_type": "phone",
128
+ "phone": "1234567890",
129
+ "message_type": "contact",
130
+ "contact_name": "John Doe",
131
+ "vcard": "BEGIN:VCARD\nVERSION:3.0\nFN:John Doe\nTEL:+1234567890\nEND:VCARD"
132
+ }
133
+ ```
134
+
135
+ ### Response Format
136
+
137
+ ```json
138
+ {
139
+ "success": true,
140
+ "recipient": "1234567890",
141
+ "recipient_type": "phone",
142
+ "message_type": "text",
143
+ "details": {
144
+ "status": "sent",
145
+ "preview": "Hello! How are you?",
146
+ "timestamp": "2025-01-30T12:00:00"
147
+ }
148
+ }
149
+ ```
150
+
151
+ ### Error Response
152
+
153
+ ```json
154
+ {
155
+ "error": "Phone number is required for recipient_type='phone'"
156
+ }
157
+ ```
158
+
159
+ ## Guidelines
160
+
161
+ 1. **Phone numbers**: Always use without + prefix, just digits (e.g., `919876543210`)
162
+ 2. **Group IDs**: Use JID format ending in `@g.us` (e.g., `123456789@g.us`)
163
+ 3. **Media URLs**: Must be publicly accessible URLs (https://)
164
+ 4. **vCard format**: Use vCard 3.0 specification for contact cards
165
+ 5. **Message length**: Text messages can be up to 4096 characters
166
+ 6. **Media size**: Check WhatsApp limits for media file sizes
167
+
168
+ ## Common Use Cases
169
+
170
+ - Send automated notifications to contacts
171
+ - Forward messages to groups
172
+ - Share media files
173
+ - Send location information
174
+ - Share contact information
175
+
176
+ ## Setup Requirements
177
+
178
+ 1. Connect the **WhatsApp Send** node to Zeenie's `input-tools` handle
179
+ 2. Ensure WhatsApp is connected (green status indicator in Credentials)
180
+ 3. The recipient must have WhatsApp installed