nothumanallowed 16.0.19 → 16.0.21

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": "16.0.19",
3
+ "version": "16.0.21",
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 = '16.0.19';
8
+ export const VERSION = '16.0.21';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -321,15 +321,50 @@ function isContinuationMessage(text, lastCtx) {
321
321
  function isCompletedAction(text) {
322
322
  if (!text) return false;
323
323
  const lower = text.toLowerCase();
324
+ // Specific high-confidence signals
324
325
  const DONE_SIGNALS = ['cancellato con successo','eliminato con successo','evento eliminato',
325
326
  'evento cancellato','deleted successfully','removed successfully','email inviata','email sent',
326
327
  'draft created','bozza creata','aggiornato con successo','updated successfully',
327
328
  'task completato','task done','creato con successo','created successfully',
328
329
  'spostato con successo','moved successfully'];
329
- return DONE_SIGNALS.some(s => lower.includes(s));
330
+ if (DONE_SIGNALS.some(s => lower.includes(s))) return true;
331
+ // Broader patterns (v16.0.21 guardrail): "è stato X", "l'ho fatto", "ho cancellato".
332
+ // These catch HERALD-style narrations like "L'appuntamento è stato spostato al 19 maggio".
333
+ const BROAD = /\b(è\s+(stat[ao]|stat[ei])\s+(cancellat[ao]i?|eliminat[ao]i?|rimoss[ao]i?|spostat[ao]i?|modificat[ao]i?|aggiornat[ao]i?|creat[ao]i?|inviat[ao]i?|inoltrat[ao]i?|archiviat[ao]i?|completat[ao]i?|rinominat[ao]i?|condivis[ao]i?|segnat[ao]i?))/i;
334
+ if (BROAD.test(text)) return true;
335
+ const HO = /\b(ho\s+(cancellato|eliminato|rimosso|spostato|modificato|aggiornato|creato|inviato|inoltrato|archiviato|completato|rinominato|condiviso|segnato|fissato|prenotato|programmato|cambiato|risolto))/i;
336
+ if (HO.test(text)) return true;
337
+ const EN = /\b(i\s+(have|just)\s+(deleted|removed|moved|created|updated|sent|forwarded|archived|completed|renamed|shared|marked))/i;
338
+ if (EN.test(text)) return true;
339
+ return false;
340
+ }
341
+
342
+ // Tool whitelist for "actually mutated state". If the agent claims a
343
+ // completed mutation but NONE of these were called, we treat it as fake.
344
+ const _MUTATION_TOOLS = new Set([
345
+ 'calendar_create', 'calendar_update', 'calendar_delete', 'calendar_move',
346
+ 'gmail_send', 'gmail_reply', 'gmail_forward', 'gmail_delete', 'gmail_archive',
347
+ 'gmail_label', 'gmail_mark_read', 'gmail_mark_unread', 'gmail_draft',
348
+ 'task_add', 'task_done', 'task_delete', 'task_edit',
349
+ 'note_add', 'note_delete',
350
+ 'reminder_create', 'reminder_cancel',
351
+ 'contact_create', 'contact_update', 'contact_delete',
352
+ 'drive_upload', 'drive_update', 'drive_delete', 'drive_rename', 'drive_move', 'drive_share',
353
+ 'gtask_complete', 'gtask_update', 'gtask_delete',
354
+ 'slack_send', 'notion_update', 'github_create_issue', 'github_close_issue',
355
+ 'imap_send', 'imap_reply', 'imap_delete',
356
+ ]);
357
+ function _toolResultLineIsMutation(line) {
358
+ if (typeof line !== 'string') return false;
359
+ const m = line.match(/^\[([\w_]+)\]\s+(.*)$/);
360
+ if (!m) return false;
361
+ const [, name, rest] = m;
362
+ if (!_MUTATION_TOOLS.has(name)) return false;
363
+ if (/Error:|^Error\b/i.test(rest)) return false; // failed → didn't actually mutate
364
+ return true;
330
365
  }
331
366
 
332
- async function callAgentWithTools(config, agentName, userMessage, languageOverride, preHistory) {
367
+ async function callAgentWithTools(config, agentName, userMessage, languageOverride, preHistory, chatId) {
333
368
  const today = new Date().toISOString().split('T')[0];
334
369
  const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
335
370
  const locale = Intl.DateTimeFormat().resolvedOptions().locale || 'en';
@@ -354,6 +389,9 @@ async function callAgentWithTools(config, agentName, userMessage, languageOverri
354
389
  // preHistory: full conversation history from previous turn (for sticky confirmations)
355
390
  const history = preHistory ? [...preHistory] : [];
356
391
  let finalText = '';
392
+ // Track EVERY tool call across ALL rounds. The final post-response
393
+ // guardrail uses this to detect "claimed action without actual tool call".
394
+ const _allToolResults = [];
357
395
 
358
396
  for (let round = 0; round < 5; round++) {
359
397
  const parts = history.map(h => (h.role === 'user' ? '[User]' : '[Assistant]') + ' ' + h.content);
@@ -397,14 +435,21 @@ async function callAgentWithTools(config, agentName, userMessage, languageOverri
397
435
  break;
398
436
  }
399
437
 
400
- // Execute all tools
438
+ // Execute all tools — use the remembering variant so list-tools auto-
439
+ // populate the anaphoric cache (lastList_*). Critical for "Si spostalo"
440
+ // pattern: HERALD calls calendar_find inside the loop, the result must
441
+ // land in lastCalendarEvents so the next turn's anaphoric dispatcher
442
+ // can resolve "spostalo" deterministically.
443
+ const { executeToolAndRemember: _exec } = await import('./tool-executor.mjs');
401
444
  const toolResults = [];
402
445
  let authError = null;
403
446
  for (const { action, params } of actions) {
404
447
  try {
405
- const result = await executeTool(action, params, config);
448
+ const result = await _exec(action, params, config, chatId);
406
449
  const resultStr = typeof result === 'string' ? result : JSON.stringify(result);
407
- toolResults.push(`[${action}] ${resultStr}`);
450
+ const line = `[${action}] ${resultStr}`;
451
+ toolResults.push(line);
452
+ _allToolResults.push(line);
408
453
  } catch (err) {
409
454
  // Detect Google/Microsoft OAuth token expiry — give user a clear fix instruction
410
455
  const msg = err.message || '';
@@ -413,7 +458,9 @@ async function callAgentWithTools(config, agentName, userMessage, languageOverri
413
458
  authError = action.startsWith('gmail') || action.startsWith('imap') || action.startsWith('calendar') || action.startsWith('contact') || action.startsWith('drive') || action.startsWith('gtask')
414
459
  ? 'google' : 'microsoft';
415
460
  }
416
- toolResults.push(`[${action}] Error: ${err.message}`);
461
+ const errLine = `[${action}] Error: ${err.message}`;
462
+ toolResults.push(errLine);
463
+ _allToolResults.push(errLine);
417
464
  }
418
465
  }
419
466
 
@@ -446,6 +493,24 @@ async function callAgentWithTools(config, agentName, userMessage, languageOverri
446
493
  `If an action was completed, say so clearly. REMEMBER: reply ONLY in ${language}.`;
447
494
  }
448
495
 
496
+ // ── POST-RESPONSE ANTI-HALLUCINATION GUARDRAIL (v16.0.21) ─────────────
497
+ // If the agent claims a completed mutation ("ho cancellato", "è stato
498
+ // spostato", "I have deleted") BUT no mutation tool was actually called
499
+ // across any of the 5 rounds — REPLACE the lie with an honest error.
500
+ // The user sees the truth: "Non sono riuscito a eseguire l'azione".
501
+ if (finalText) {
502
+ const claimsAction = isCompletedAction(finalText);
503
+ if (claimsAction) {
504
+ const didMutate = _allToolResults.some(_toolResultLineIsMutation);
505
+ if (!didMutate) {
506
+ try { console.warn(`[GUARDRAIL] Mutation claim without tool call. Tools used: [${_allToolResults.map(l => l.match(/^\[([\w_]+)\]/)?.[1]).filter(Boolean).join(', ')}]. Replacing fake response: "${finalText.slice(0, 160)}"`); } catch {}
507
+ finalText = language === 'Italian'
508
+ ? `⚠️ Attenzione: avevo dichiarato di aver eseguito un'azione, ma in realtà non ho chiamato nessun tool di modifica. NON è stato fatto nulla.\n\nPer favore ripeti la richiesta in modo specifico — es. "sposta l'appuntamento Tagliando macchina al 19 maggio alle 17:30" — così che io possa eseguire il comando esatto.`
509
+ : `⚠️ Warning: I claimed an action was completed but did not actually call any modification tool. NOTHING was changed.\n\nPlease restate your request precisely — e.g. "move the Car Service appointment to May 19 at 17:30" — so I can run the exact tool call.`;
510
+ }
511
+ }
512
+ }
513
+
449
514
  // Defensive language post-check: small models sometimes drop back to English
450
515
  // even when instructed otherwise (especially after tool execution, where the
451
516
  // English tool-result text biases the continuation). If the final reply is
@@ -1275,7 +1340,7 @@ class TelegramResponder {
1275
1340
  } catch {}
1276
1341
 
1277
1342
  if (TOOL_AGENTS.has(agent)) {
1278
- const result = await callAgentWithTools(this.config, agent, enrichedMessage, detectedLang, preHistory);
1343
+ const result = await callAgentWithTools(this.config, agent, enrichedMessage, detectedLang, preHistory, chatId);
1279
1344
  responseText = result.text;
1280
1345
  responseHistory = result.history;
1281
1346
  } else {