nothumanallowed 13.5.192 → 13.5.193
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "13.5.
|
|
3
|
+
"version": "13.5.193",
|
|
4
4
|
"description": "NotHumanAllowed — 38 AI agents, 80 tools, Studio (visual agentic workflows). Email, calendar, browser automation, screen capture, canvas, cron/heartbeat, Alexandria E2E messaging, GitHub, Notion, Slack, voice chat, free AI (Liara), 28 languages. Zero-dependency CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/constants.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url';
|
|
|
5
5
|
const __filename = fileURLToPath(import.meta.url);
|
|
6
6
|
const __dirname = path.dirname(__filename);
|
|
7
7
|
|
|
8
|
-
export const VERSION = '13.5.
|
|
8
|
+
export const VERSION = '13.5.193';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|
|
@@ -207,9 +207,13 @@ async function callAgentWithTools(config, agentName, userMessage, languageOverri
|
|
|
207
207
|
|
|
208
208
|
const response = await callLLM(config, systemPrompt, serialized);
|
|
209
209
|
const { textParts, actions } = parseActions(response);
|
|
210
|
-
finalText = textParts.join('\n').trim();
|
|
211
210
|
|
|
212
|
-
if (actions.length === 0)
|
|
211
|
+
if (actions.length === 0) {
|
|
212
|
+
// Final round — no tools, just text for the user
|
|
213
|
+
finalText = textParts.join('\n').trim();
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
// Intermediate round with tools — don't expose tool-call JSON to the user
|
|
213
217
|
|
|
214
218
|
// Execute all tools and collect results
|
|
215
219
|
const toolResults = [];
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
getEventsForDate,
|
|
22
22
|
createEvent,
|
|
23
23
|
updateEvent,
|
|
24
|
+
deleteEvent,
|
|
24
25
|
listEvents,
|
|
25
26
|
markAsRead,
|
|
26
27
|
markAsUnread,
|
|
@@ -74,6 +75,7 @@ export const DESTRUCTIVE_ACTIONS = new Set([
|
|
|
74
75
|
'calendar_create',
|
|
75
76
|
'calendar_move',
|
|
76
77
|
'calendar_update',
|
|
78
|
+
'calendar_delete',
|
|
77
79
|
'contact_delete',
|
|
78
80
|
'task_done',
|
|
79
81
|
'task_delete',
|
|
@@ -171,7 +173,11 @@ TOOLS:
|
|
|
171
173
|
Update ANY field of an existing calendar event: title, location, description, start time, end time.
|
|
172
174
|
You MUST call calendar_find first to get the eventId. Only include fields that need to change. ALWAYS confirm before updating.
|
|
173
175
|
|
|
174
|
-
19.
|
|
176
|
+
19. calendar_delete(eventId: string)
|
|
177
|
+
Delete (permanently remove) a calendar event by its eventId.
|
|
178
|
+
You MUST call calendar_find first to get the eventId. ALWAYS confirm with the user before deleting.
|
|
179
|
+
|
|
180
|
+
20. schedule_meeting(clientName: string, subject: string, location: string, durationMinutes: number, dateFrom: string, dateTo: string, workdayStart?: number, workdayEnd?: number)
|
|
175
181
|
Find optimal meeting slots considering existing calendar events, locations, and estimated travel time between appointments. Returns ranked slots with travel info. dateFrom and dateTo are YYYY-MM-DD.
|
|
176
182
|
|
|
177
183
|
20. schedule_draft_email(clientName: string, subject: string, location: string, durationMinutes: number, dateFrom: string, dateTo: string)
|
|
@@ -600,7 +606,7 @@ Never output a JSON block as a suggestion — every block executes immediately.
|
|
|
600
606
|
AVAILABLE TOOLS:
|
|
601
607
|
gmail_list · gmail_read · gmail_send · gmail_draft · gmail_reply · gmail_mark_read · gmail_mark_unread · gmail_archive · gmail_delete · gmail_send_attach
|
|
602
608
|
imap_accounts · imap_list · imap_read · imap_send · imap_sync · imap_labels · imap_mark_read · imap_reply · imap_thread · imap_search · imap_mark_starred · imap_trash · imap_draft · imap_send_template · imap_bulk_send
|
|
603
|
-
calendar_today · calendar_tomorrow · calendar_date · calendar_upcoming · calendar_week · calendar_create · calendar_move · calendar_find · calendar_update · schedule_meeting · schedule_draft_email
|
|
609
|
+
calendar_today · calendar_tomorrow · calendar_date · calendar_upcoming · calendar_week · calendar_create · calendar_move · calendar_find · calendar_update · calendar_delete · schedule_meeting · schedule_draft_email
|
|
604
610
|
task_list · task_add · task_done · task_move · task_delete · task_clear · task_edit
|
|
605
611
|
contact_search · contact_add · contact_update · contact_delete
|
|
606
612
|
gtask_list · gtask_add · gtask_complete
|
|
@@ -630,12 +636,19 @@ maps_directions · notify_remind · birthdays_upcoming · birthday_add · execut
|
|
|
630
636
|
export function parseActions(text) {
|
|
631
637
|
const actions = [];
|
|
632
638
|
const textParts = [];
|
|
639
|
+
|
|
640
|
+
// Normalize: some LLMs output "json ... " (double-quote fences) instead of ```json ... ```
|
|
641
|
+
// Replace "json\n{...}\n" patterns with proper ```json fences before parsing
|
|
642
|
+
const normalized = text
|
|
643
|
+
.replace(/"json\s*\n([\s\S]*?)\n\s*"/g, (_, body) => '```json\n' + body.trim() + '\n```')
|
|
644
|
+
.replace(/'json\s*\n([\s\S]*?)\n\s*'/g, (_, body) => '```json\n' + body.trim() + '\n```');
|
|
645
|
+
|
|
633
646
|
const fenceRegex = /```json\s*\n?([\s\S]*?)```/g;
|
|
634
647
|
let lastIndex = 0;
|
|
635
648
|
let match;
|
|
636
649
|
|
|
637
|
-
while ((match = fenceRegex.exec(
|
|
638
|
-
const before =
|
|
650
|
+
while ((match = fenceRegex.exec(normalized)) !== null) {
|
|
651
|
+
const before = normalized.slice(lastIndex, match.index).trim();
|
|
639
652
|
if (before) textParts.push(before);
|
|
640
653
|
|
|
641
654
|
try {
|
|
@@ -650,9 +663,30 @@ export function parseActions(text) {
|
|
|
650
663
|
lastIndex = match.index + match[0].length;
|
|
651
664
|
}
|
|
652
665
|
|
|
653
|
-
const trailing =
|
|
666
|
+
const trailing = normalized.slice(lastIndex).trim();
|
|
654
667
|
if (trailing) textParts.push(trailing);
|
|
655
668
|
|
|
669
|
+
// Fallback: if no fenced blocks found, scan for bare {"action": ...} objects in the text
|
|
670
|
+
if (actions.length === 0) {
|
|
671
|
+
const bareRegex = /\{[\s\S]*?"action"\s*:\s*"[^"]+[\s\S]*?\}/g;
|
|
672
|
+
let bareMatch;
|
|
673
|
+
const consumed = new Set();
|
|
674
|
+
while ((bareMatch = bareRegex.exec(text)) !== null) {
|
|
675
|
+
try {
|
|
676
|
+
const parsed = JSON.parse(bareMatch[0]);
|
|
677
|
+
if (parsed.action && typeof parsed.action === 'string' && !consumed.has(bareMatch[0])) {
|
|
678
|
+
actions.push({ action: parsed.action, params: parsed.params || {} });
|
|
679
|
+
consumed.add(bareMatch[0]);
|
|
680
|
+
}
|
|
681
|
+
} catch { /* not valid JSON, skip */ }
|
|
682
|
+
}
|
|
683
|
+
// If we found bare actions, rebuild textParts stripping out the JSON blobs
|
|
684
|
+
if (actions.length > 0) {
|
|
685
|
+
const cleaned = text.replace(/\{[\s\S]*?"action"\s*:\s*"[^"]+[\s\S]*?\}/g, '').trim();
|
|
686
|
+
return { textParts: cleaned ? [cleaned] : [], actions };
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
656
690
|
return { textParts, actions };
|
|
657
691
|
}
|
|
658
692
|
|
|
@@ -1318,6 +1352,25 @@ export async function executeTool(action, params, config) {
|
|
|
1318
1352
|
return `Event updated successfully (${changes}). ${params.location ? `New location: ${params.location}` : ''}`;
|
|
1319
1353
|
}
|
|
1320
1354
|
|
|
1355
|
+
case 'calendar_delete': {
|
|
1356
|
+
if (!params.eventId) return 'eventId required. Call calendar_find first to get the eventId.';
|
|
1357
|
+
// Smart eventId resolution: if it looks like a name instead of a Google Calendar ID, search for it
|
|
1358
|
+
let delEventId = params.eventId;
|
|
1359
|
+
if (delEventId && (delEventId.includes(' ') || delEventId.length < 10 || /[A-Z]/.test(delEventId))) {
|
|
1360
|
+
const fromD = new Date();
|
|
1361
|
+
const toD = new Date(fromD.getTime() + 60 * 86400000);
|
|
1362
|
+
const evts = await listEvents(config, 'primary', fromD, toD);
|
|
1363
|
+
const m = evts.find(e => (e.summary || '').toLowerCase().includes(delEventId.toLowerCase()));
|
|
1364
|
+
if (m) {
|
|
1365
|
+
delEventId = m.id;
|
|
1366
|
+
} else {
|
|
1367
|
+
return `Could not find event matching "${params.eventId}" in the next 60 days. Use calendar_find to search first.`;
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
await deleteEvent(config, 'primary', delEventId);
|
|
1371
|
+
return `Event deleted successfully.`;
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1321
1374
|
// ── Smart Scheduling ──────────────────────────────────────────────────
|
|
1322
1375
|
case 'schedule_meeting': {
|
|
1323
1376
|
const slots = await findAvailableSlots(config, {
|