squidclaw 2.2.0 β 2.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/lib/channels/telegram/bot.js +12 -0
- package/lib/engine.js +6 -0
- package/lib/features/cron.js +217 -0
- package/lib/features/reminders.js +2 -1
- package/lib/middleware/commands.js +53 -15
- package/lib/middleware/response-sender.js +3 -12
- package/lib/tools/excel.js +136 -0
- package/lib/tools/html.js +148 -0
- package/lib/tools/pdf.js +133 -0
- package/lib/tools/pptx.js +681 -83
- package/lib/tools/router.js +162 -19
- package/package.json +3 -1
package/lib/tools/router.js
CHANGED
|
@@ -23,7 +23,9 @@ export class ToolRouter {
|
|
|
23
23
|
getToolDescriptions() {
|
|
24
24
|
const tools = [
|
|
25
25
|
'## Available Tools',
|
|
26
|
-
'You can use tools by including special tags in your response
|
|
26
|
+
'You can use tools by including special tags in your response.',
|
|
27
|
+
'IMPORTANT: You CAN send files, images, voice notes, and documents in this chat. The system handles delivery automatically.',
|
|
28
|
+
'When asked to create files (PowerPoint, etc), USE THE TOOL β do NOT say you cannot send files.',
|
|
27
29
|
'',
|
|
28
30
|
'### Web Search',
|
|
29
31
|
'---TOOL:search:your search query---',
|
|
@@ -59,19 +61,54 @@ export class ToolRouter {
|
|
|
59
61
|
'Send an email.');
|
|
60
62
|
}
|
|
61
63
|
|
|
62
|
-
tools.push('', '### Create PowerPoint',
|
|
63
|
-
'---TOOL:
|
|
64
|
-
'
|
|
65
|
-
'
|
|
66
|
-
'- Bullet point 1',
|
|
67
|
-
'- Bullet point 2',
|
|
64
|
+
tools.push('', '### Create PowerPoint (SENDS AS FILE IN CHAT!)',
|
|
65
|
+
'---TOOL:pptx_slides:Title|theme|slides content---',
|
|
66
|
+
'Creates a .pptx file and SENDS IT directly. You MUST use this when asked for PPT/presentation.',
|
|
67
|
+
'Format: title|theme|slides. Themes: dark, light, blue, green, corporate, red, gradient, minimal, saudi, ocean.',
|
|
68
68
|
'',
|
|
69
|
-
'##
|
|
70
|
-
'-
|
|
69
|
+
'SLIDE TYPES (use tags in ## title):',
|
|
70
|
+
'- Normal: ## Title then - bullet points',
|
|
71
|
+
'- Chart: ## Revenue [chart:bar] then - Label: value (types: bar, pie, line, doughnut, area)',
|
|
72
|
+
'- Table: ## Data [table] then | Col1 | Col2 | rows',
|
|
73
|
+
'- Stats: ## Metrics [stats] then - π 100M β Active users',
|
|
74
|
+
'- Timeline: ## History [timeline] then - 2020: Event happened',
|
|
75
|
+
'- Quote: ## [quote] then quote text then β Author Name',
|
|
76
|
+
'- Compare: ## Comparison [compare] then left items, ---, right items',
|
|
77
|
+
'- Section: ## New Section [section]',
|
|
71
78
|
'',
|
|
72
|
-
'
|
|
73
|
-
'
|
|
74
|
-
|
|
79
|
+
'Example:',
|
|
80
|
+
'---TOOL:pptx_slides:AI Report|dark|## Introduction\n- AI is transforming industries\n- Revenue growing 40% YoY\n\n## Growth [chart:bar]\n- 2020: 50\n- 2021: 80\n- 2022: 120\n- 2023: 200\n\n## Key Stats [stats]\n- π 195 β Countries using AI\n- π° $500B β Market size\n- π 40% β Annual growth---');
|
|
81
|
+
|
|
82
|
+
tools.push('', '### Create Excel Spreadsheet (SENDS AS FILE!)',
|
|
83
|
+
'---TOOL:excel:Title|theme|sheet content---',
|
|
84
|
+
'Creates .xlsx file and sends it. Theme: blue, green, dark, red, saudi, corporate.',
|
|
85
|
+
'Content: use | for columns. First row = headers. Separate sheets with ### SheetName.',
|
|
86
|
+
'Example: ---TOOL:excel:Sales Report|blue|Product|Revenue|Growth\nWidget A|50000|12%\nWidget B|80000|25%---',
|
|
87
|
+
'', '### Create PDF Document (SENDS AS FILE!)',
|
|
88
|
+
'---TOOL:pdf:Title|content in markdown---',
|
|
89
|
+
'Creates .pdf file and sends it. Use ## for headings, - for bullets, > for quotes, --- for dividers.',
|
|
90
|
+
'Example: ---TOOL:pdf:Meeting Notes|## Summary\n- Discussed Q1 results\n- Action items assigned---',
|
|
91
|
+
'', '### Create HTML Page (SENDS AS FILE!)',
|
|
92
|
+
'---TOOL:html:Title|theme|content in markdown---',
|
|
93
|
+
'Creates .html file and sends it. Themes: light, dark, ocean.',
|
|
94
|
+
'Example: ---TOOL:html:Report|dark|## Overview\n- Key findings\n- Recommendations---');
|
|
95
|
+
|
|
96
|
+
tools.push('', '### Schedule Cron Job',
|
|
97
|
+
'---TOOL:cron_add:name|schedule|action|data---',
|
|
98
|
+
'Create a recurring scheduled job. Schedule formats:',
|
|
99
|
+
'- every:5m / every:1h / every:24h (interval)',
|
|
100
|
+
'- daily:09:00 (every day at time UTC)',
|
|
101
|
+
'- weekly:fri:09:00 (every week on day)',
|
|
102
|
+
'- monthly:1:09:00 (every month on date)',
|
|
103
|
+
'Actions: message (send text), briefing (daily brief), remind (reminder), ai (run AI prompt)',
|
|
104
|
+
'Example: ---TOOL:cron_add:Morning Briefing|daily:06:00|briefing|---',
|
|
105
|
+
'Example: ---TOOL:cron_add:Weekly Report Reminder|weekly:fri:09:00|remind|Submit the weekly report!---',
|
|
106
|
+
'', '### List Cron Jobs',
|
|
107
|
+
'---TOOL:cron_list:all---',
|
|
108
|
+
'Show all scheduled jobs.',
|
|
109
|
+
'', '### Remove Cron Job',
|
|
110
|
+
'---TOOL:cron_remove:job_id---',
|
|
111
|
+
'Remove a scheduled job by ID.');
|
|
75
112
|
|
|
76
113
|
tools.push('', '### Allow User',
|
|
77
114
|
'---TOOL:allow:user_id_or_phone---',
|
|
@@ -181,9 +218,16 @@ export class ToolRouter {
|
|
|
181
218
|
'You already have vision β use it to read text from screenshots, documents, signs, etc.');
|
|
182
219
|
|
|
183
220
|
tools.push('', '### Set Reminder',
|
|
184
|
-
'---TOOL:remind:
|
|
185
|
-
'Set a reminder
|
|
186
|
-
'
|
|
221
|
+
'---TOOL:remind:TIME|Your reminder message---',
|
|
222
|
+
'Set a reminder. TIME can be:',
|
|
223
|
+
'- Relative: 5m, 30m, 1h, 2h, 1d (minutes, hours, days from now)',
|
|
224
|
+
'- Absolute: 2026-03-03T08:30 (UTC)',
|
|
225
|
+
'- Natural: tomorrow, tonight',
|
|
226
|
+
'Examples:',
|
|
227
|
+
'- ---TOOL:remind:30m|Check the oven!---',
|
|
228
|
+
'- ---TOOL:remind:2h|Call Ahmed---',
|
|
229
|
+
'- ---TOOL:remind:2026-03-03T15:00|Meeting time!---',
|
|
230
|
+
'ALWAYS use this tool when user says "remind me". Do NOT just acknowledge β actually set it.',
|
|
187
231
|
'The user will receive a proactive message at that time even if they are not chatting.');
|
|
188
232
|
|
|
189
233
|
tools.push('', '**Important:** Use tools when needed. The tool result will be injected into the conversation automatically. Only use one tool per response.');
|
|
@@ -202,7 +246,7 @@ export class ToolRouter {
|
|
|
202
246
|
}
|
|
203
247
|
|
|
204
248
|
async processResponse(response, agentId) {
|
|
205
|
-
const toolMatch = response.match(/---TOOL:(\w+):(
|
|
249
|
+
const toolMatch = response.match(/---TOOL:(\w+):([\s\S]+?)---/);
|
|
206
250
|
if (!toolMatch) return { toolUsed: false, toolResult: null, cleanResponse: response };
|
|
207
251
|
|
|
208
252
|
const [fullMatch, toolName, toolArg] = toolMatch;
|
|
@@ -223,12 +267,31 @@ export class ToolRouter {
|
|
|
223
267
|
try {
|
|
224
268
|
const pipeIdx = toolArg.indexOf('|');
|
|
225
269
|
if (pipeIdx === -1) {
|
|
226
|
-
toolResult = '
|
|
270
|
+
toolResult = 'Format: time|message. Example: 30m|Check oven';
|
|
227
271
|
break;
|
|
228
272
|
}
|
|
229
|
-
|
|
273
|
+
let timeStr = toolArg.slice(0, pipeIdx).trim();
|
|
230
274
|
const msg = toolArg.slice(pipeIdx + 1).trim();
|
|
231
|
-
|
|
275
|
+
|
|
276
|
+
// Parse relative times
|
|
277
|
+
const relMatch = timeStr.match(/^(\d+)\s*(m|min|minutes?|h|hr|hours?|d|days?)$/i);
|
|
278
|
+
if (relMatch) {
|
|
279
|
+
const num = parseInt(relMatch[1]);
|
|
280
|
+
const unit = relMatch[2][0].toLowerCase();
|
|
281
|
+
const ms = unit === 'm' ? num * 60000 : unit === 'h' ? num * 3600000 : num * 86400000;
|
|
282
|
+
const fireDate = new Date(Date.now() + ms);
|
|
283
|
+
timeStr = fireDate.toISOString().slice(0, 16);
|
|
284
|
+
} else if (timeStr.toLowerCase() === 'tomorrow') {
|
|
285
|
+
const d = new Date(Date.now() + 86400000);
|
|
286
|
+
d.setUTCHours(9, 0, 0, 0);
|
|
287
|
+
timeStr = d.toISOString().slice(0, 16);
|
|
288
|
+
} else if (timeStr.toLowerCase() === 'tonight') {
|
|
289
|
+
const d = new Date();
|
|
290
|
+
d.setUTCHours(18, 0, 0, 0);
|
|
291
|
+
if (d < new Date()) d.setDate(d.getDate() + 1);
|
|
292
|
+
timeStr = d.toISOString().slice(0, 16);
|
|
293
|
+
}
|
|
294
|
+
|
|
232
295
|
return { toolUsed: true, toolName: 'remind', reminderTime: timeStr, reminderMessage: msg, cleanResponse };
|
|
233
296
|
} catch (err) {
|
|
234
297
|
toolResult = 'Failed to set reminder: ' + err.message;
|
|
@@ -285,6 +348,86 @@ export class ToolRouter {
|
|
|
285
348
|
}
|
|
286
349
|
break;
|
|
287
350
|
}
|
|
351
|
+
case 'excel':
|
|
352
|
+
case 'xlsx':
|
|
353
|
+
case 'spreadsheet': {
|
|
354
|
+
try {
|
|
355
|
+
const { ExcelGenerator } = await import('./excel.js');
|
|
356
|
+
const gen = new ExcelGenerator();
|
|
357
|
+
const parts = toolArg.split('|');
|
|
358
|
+
let title, themeName, sheetContent;
|
|
359
|
+
if (parts.length >= 3) {
|
|
360
|
+
title = parts[0].trim();
|
|
361
|
+
themeName = parts[1].trim();
|
|
362
|
+
sheetContent = parts.slice(2).join('|');
|
|
363
|
+
} else {
|
|
364
|
+
title = parts[0]?.trim() || 'Spreadsheet';
|
|
365
|
+
sheetContent = parts.slice(1).join('|');
|
|
366
|
+
themeName = 'corporate';
|
|
367
|
+
}
|
|
368
|
+
const sheets = ExcelGenerator.parseContent(sheetContent);
|
|
369
|
+
const result = await gen.create(title, sheets, { theme: themeName });
|
|
370
|
+
return { toolUsed: true, toolName: 'excel', toolResult: 'Excel created: ' + result.filename, filePath: result.filepath, fileName: result.filename, cleanResponse };
|
|
371
|
+
} catch (err) { toolResult = 'Excel failed: ' + err.message; }
|
|
372
|
+
break;
|
|
373
|
+
}
|
|
374
|
+
case 'pdf':
|
|
375
|
+
case 'document': {
|
|
376
|
+
try {
|
|
377
|
+
const { PdfGenerator } = await import('./pdf.js');
|
|
378
|
+
const gen = new PdfGenerator();
|
|
379
|
+
const pipeIdx = toolArg.indexOf('|');
|
|
380
|
+
const title = pipeIdx > -1 ? toolArg.slice(0, pipeIdx).trim() : 'Document';
|
|
381
|
+
const content = pipeIdx > -1 ? toolArg.slice(pipeIdx + 1) : toolArg;
|
|
382
|
+
const result = await gen.create(title, content);
|
|
383
|
+
return { toolUsed: true, toolName: 'pdf', toolResult: 'PDF created: ' + result.filename, filePath: result.filepath, fileName: result.filename, cleanResponse };
|
|
384
|
+
} catch (err) { toolResult = 'PDF failed: ' + err.message; }
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
case 'html':
|
|
388
|
+
case 'webpage': {
|
|
389
|
+
try {
|
|
390
|
+
const { HtmlGenerator } = await import('./html.js');
|
|
391
|
+
const gen = new HtmlGenerator();
|
|
392
|
+
const parts = toolArg.split('|');
|
|
393
|
+
const title = parts[0]?.trim() || 'Page';
|
|
394
|
+
const themeName = parts.length >= 3 ? parts[1].trim() : 'light';
|
|
395
|
+
const content = parts.length >= 3 ? parts.slice(2).join('|') : parts.slice(1).join('|');
|
|
396
|
+
const result = gen.create(title, content, { theme: themeName });
|
|
397
|
+
return { toolUsed: true, toolName: 'html', toolResult: 'HTML created: ' + result.filename, filePath: result.filepath, fileName: result.filename, cleanResponse };
|
|
398
|
+
} catch (err) { toolResult = 'HTML failed: ' + err.message; }
|
|
399
|
+
break;
|
|
400
|
+
}
|
|
401
|
+
case 'cron_add': {
|
|
402
|
+
try {
|
|
403
|
+
if (!this._engine?.cron) { toolResult = 'Cron not available'; break; }
|
|
404
|
+
const parts = toolArg.split('|').map(p => p.trim());
|
|
405
|
+
const [name, schedule, action, ...dataParts] = parts;
|
|
406
|
+
const data = dataParts.join('|');
|
|
407
|
+
const result = this._engine.cron.add(agentId, this._currentContactId, name, schedule, action || 'message', data, this._currentPlatform);
|
|
408
|
+
toolResult = 'Cron job created! β
\nπ ' + result.name + '\nβ° Schedule: ' + result.schedule + '\nπ Next run: ' + result.nextRun;
|
|
409
|
+
} catch (err) { toolResult = 'Failed: ' + err.message; }
|
|
410
|
+
break;
|
|
411
|
+
}
|
|
412
|
+
case 'cron_list': {
|
|
413
|
+
try {
|
|
414
|
+
if (!this._engine?.cron) { toolResult = 'Cron not available'; break; }
|
|
415
|
+
const jobs = this._engine.cron.listAll(agentId);
|
|
416
|
+
if (jobs.length === 0) { toolResult = 'No scheduled jobs'; break; }
|
|
417
|
+
toolResult = jobs.map(j =>
|
|
418
|
+
(j.enabled ? 'β
' : 'βΈοΈ') + ' ' + j.name + ' (' + j.schedule + ')\n ID: ' + j.id + '\n Next: ' + (j.next_run || 'N/A')
|
|
419
|
+
).join('\n\n');
|
|
420
|
+
} catch (err) { toolResult = 'Failed: ' + err.message; }
|
|
421
|
+
break;
|
|
422
|
+
}
|
|
423
|
+
case 'cron_remove': {
|
|
424
|
+
try {
|
|
425
|
+
if (!this._engine?.cron) { toolResult = 'Cron not available'; break; }
|
|
426
|
+
this._engine.cron.remove(toolArg.trim());
|
|
427
|
+
toolResult = 'Cron job removed β
';
|
|
428
|
+
} catch (err) { toolResult = 'Failed: ' + err.message; }
|
|
429
|
+
break;
|
|
430
|
+
}
|
|
288
431
|
case 'allow': {
|
|
289
432
|
try {
|
|
290
433
|
const { AllowlistManager } = await import('../features/allowlist-manager.js');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "squidclaw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "π¦ AI agent platform β human-like agents for WhatsApp, Telegram & more",
|
|
5
5
|
"main": "lib/engine.js",
|
|
6
6
|
"bin": {
|
|
@@ -42,12 +42,14 @@
|
|
|
42
42
|
"commander": "^14.0.3",
|
|
43
43
|
"croner": "^10.0.1",
|
|
44
44
|
"dotenv": "^17.3.1",
|
|
45
|
+
"exceljs": "^4.4.0",
|
|
45
46
|
"express": "^5.2.1",
|
|
46
47
|
"file-type": "^21.3.0",
|
|
47
48
|
"grammy": "^1.40.1",
|
|
48
49
|
"linkedom": "^0.18.12",
|
|
49
50
|
"node-edge-tts": "^1.2.10",
|
|
50
51
|
"pdfjs-dist": "^5.4.624",
|
|
52
|
+
"pdfkit": "^0.17.2",
|
|
51
53
|
"pino": "^10.3.1",
|
|
52
54
|
"pptxgenjs": "^4.0.1",
|
|
53
55
|
"puppeteer-core": "^24.37.5",
|