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.192",
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.192';
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) break; // No tools — pure text response
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. schedule_meeting(clientName: string, subject: string, location: string, durationMinutes: number, dateFrom: string, dateTo: string, workdayStart?: number, workdayEnd?: number)
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(text)) !== null) {
638
- const before = text.slice(lastIndex, match.index).trim();
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 = text.slice(lastIndex).trim();
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, {