nothumanallowed 15.1.62 → 15.1.63

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": "15.1.62",
3
+ "version": "15.1.63",
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 = '15.1.62';
8
+ export const VERSION = '15.1.63';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -9,7 +9,7 @@
9
9
  */
10
10
 
11
11
  import { callAgent, callLLM } from './llm.mjs';
12
- import { buildSystemPrompt, parseActions, executeTool, TOOL_DEFINITIONS, LIARA_TOOL_DEFINITIONS } from './tool-executor.mjs';
12
+ import { buildSystemPrompt, parseActions, executeTool, TOOL_DEFINITIONS, LIARA_TOOL_DEFINITIONS, DESTRUCTIVE_ACTIONS } from './tool-executor.mjs';
13
13
  import https from 'https';
14
14
  import http from 'http';
15
15
  import { URL } from 'url';
@@ -1047,13 +1047,17 @@ class TelegramResponder {
1047
1047
  this._lastDirectAuditChatId = chatId;
1048
1048
  // Run the per-domain direct-action dispatcher. First match wins; falls
1049
1049
  // through to LLM if no handler claims the message.
1050
+ // Fast-path specialised handlers (regex-driven, lower latency for the
1051
+ // common cases), then the universal dispatcher that covers ALL 50+
1052
+ // mutation tools via a single LLM-NLU+deterministic-execute pass.
1050
1053
  const directFresh =
1051
1054
  await this._tryDirectFreshCalendarAction(cleanText, this.config) ||
1052
1055
  await this._tryDirectFreshEmailAction(cleanText, this.config) ||
1053
1056
  await this._tryDirectFreshTaskAction(cleanText, this.config) ||
1054
1057
  await this._tryDirectFreshNoteAction(cleanText, this.config) ||
1055
1058
  await this._tryDirectFreshReminderAction(cleanText, this.config) ||
1056
- await this._tryDirectFreshSlackAction(cleanText, this.config);
1059
+ await this._tryDirectFreshSlackAction(cleanText, this.config) ||
1060
+ await this._tryDirectFreshUniversalAction(cleanText, this.config);
1057
1061
  if (directFresh) {
1058
1062
  this.log(`[Telegram] ${fromUser}: direct-fresh ${directFresh.action} → ${directFresh.success ? 'OK' : 'FAIL'}`);
1059
1063
  const personaName = this.config.responder?.telegram?.botName || this.config.responder?.botName || '';
@@ -1663,6 +1667,181 @@ class TelegramResponder {
1663
1667
  try { return this._extractJsonObject(await callLLM(config, sys, userMessage, { temperature: 0, maxTokens: 300 })); } catch { return null; }
1664
1668
  }
1665
1669
 
1670
+ // ════════════════════════════════════════════════════════════════════════
1671
+ // UNIVERSAL DIRECT-ACTION DISPATCHER
1672
+ // ════════════════════════════════════════════════════════════════════════
1673
+ // Covers ALL 22 mutation tools in `DESTRUCTIVE_ACTIONS` (gmail, imap,
1674
+ // calendar, contacts, tasks, slack, github, file, drive, notify).
1675
+ // The LLM is used ONLY to (a) decide if the message maps to a tool and
1676
+ // (b) extract the params as JSON. Tool execution is then deterministic
1677
+ // server-side. No tool block is parsed from natural language by the
1678
+ // model — the model can never "say done" without us actually doing it.
1679
+ async _tryDirectFreshUniversalAction(userMessage, config) {
1680
+ if (!userMessage || typeof userMessage !== 'string' || userMessage.length < 3) return null;
1681
+
1682
+ const todayIso = new Date().toISOString().slice(0, 10);
1683
+ const sys =
1684
+ `You are a tool-routing classifier. Given a user message in any language, decide whether it ` +
1685
+ `requests a state-changing action that maps to ONE of these tools, and extract the params.\n\n` +
1686
+ `ALLOWED TOOLS (you MUST pick one of these OR return null):\n` +
1687
+ // Calendar
1688
+ `- calendar_create(summary, start, end, description?) — start/end ISO "YYYY-MM-DDTHH:MM:00"\n` +
1689
+ `- calendar_move(eventId? OR title, newStart, newEnd?) — if no eventId, title is used to find it\n` +
1690
+ `- calendar_update(eventId? OR title, summary?, start?, end?, description?)\n` +
1691
+ `- calendar_delete(eventId? OR title, date?)\n` +
1692
+ // Email Gmail
1693
+ `- gmail_send(to, subject, body) — primary email account\n` +
1694
+ `- gmail_reply(messageId? OR threadHint, body)\n` +
1695
+ `- gmail_delete(messageId? OR query)\n` +
1696
+ `- gmail_mark_read(messageId, isRead?)\n` +
1697
+ `- gmail_mark_starred(messageId, starred?)\n` +
1698
+ `- gmail_archive(messageId)\n` +
1699
+ // Email IMAP
1700
+ `- imap_send(accountId?, to, subject, body) — custom IMAP account\n` +
1701
+ `- imap_reply(accountId?, messageId, body)\n` +
1702
+ `- imap_trash(messageId)\n` +
1703
+ `- imap_mark_read(messageId, isRead?)\n` +
1704
+ `- imap_draft(accountId?, to, subject, body)\n` +
1705
+ // Contacts
1706
+ `- contact_add(name, email?, phone?, company?, address?)\n` +
1707
+ `- contact_update(query, email?, phone?, company?, address?)\n` +
1708
+ `- contact_delete(query)\n` +
1709
+ // Tasks
1710
+ `- task_add(title, priority?, due?)\n` +
1711
+ `- task_done(title)\n` +
1712
+ `- task_delete(title)\n` +
1713
+ // Google Tasks
1714
+ `- gtask_add(title, notes?, due?)\n` +
1715
+ `- gtask_complete(title)\n` +
1716
+ // Notes
1717
+ `- note_add(title, content?)\n` +
1718
+ // Reminders
1719
+ `- notify_remind(message, when) — when = ISO datetime\n` +
1720
+ `- reminder_create(message, when)\n` +
1721
+ // Slack
1722
+ `- slack_send(channel, text, threadTs?) — channel "#name"\n` +
1723
+ `- slack_dm(user, text) — user = name, id, or email\n` +
1724
+ `- slack_react(channel, ts, emoji)\n` +
1725
+ `- slack_mark_read(channel, ts)\n` +
1726
+ // Notion
1727
+ `- notion_page(title, content) — create a new Notion page\n` +
1728
+ // GitHub
1729
+ `- github_create_issue(repo, title, body?, labels?) — repo "owner/name"\n` +
1730
+ // File system (local to user)
1731
+ `- file_write(path, content)\n` +
1732
+ `- file_move(from, to)\n` +
1733
+ `- file_delete(path)\n` +
1734
+ `- file_mkdir(path)\n` +
1735
+ // Google Drive
1736
+ `- drive_upload(name, content, mimeType?) — Google Drive\n` +
1737
+ `- drive_update(fileId, content)\n` +
1738
+ `- drive_delete(fileId? OR name)\n` +
1739
+ `- drive_move(fileId, newParentFolderId? OR newName?)\n` +
1740
+ `- drive_share(fileId, email, role?) — role = "reader"|"writer"|"commenter"\n` +
1741
+ // Birthdays
1742
+ `- birthday_add(name, date) — date "YYYY-MM-DD" or "MM-DD"\n` +
1743
+ `- birthday_delete(name)\n` +
1744
+ // Alexandria E2E
1745
+ `- alexandria_send(channel, message)\n` +
1746
+ // Cron
1747
+ `- cron_create(name, schedule, command) — schedule = cron expression\n` +
1748
+ `- cron_delete(name)\n\n` +
1749
+ `Today is ${todayIso}. Relative dates: "domani" = ${this._addDaysIso(todayIso, 1).slice(0, 10)}, ` +
1750
+ `"dopodomani" = ${this._addDaysIso(todayIso, 2).slice(0, 10)}, "lunedì/martedì/..." resolve to next occurrence.\n\n` +
1751
+ `OUTPUT FORMAT (strict JSON, no markdown, no prose, no fences):\n` +
1752
+ `{"tool": "tool_name" | null, "params": { ... }}\n\n` +
1753
+ `If the message is a READ/LIST/QUERY operation (e.g. "mostra…", "che ho oggi", "leggi email", "trova"), ` +
1754
+ `OR is conversational chat (greetings, questions, opinions) OR is ambiguous → return {"tool": null}.\n` +
1755
+ `If a required param is genuinely missing AND not inferable → return {"tool": null}.\n` +
1756
+ `Never invent emails, eventIds, or recipient addresses.`;
1757
+
1758
+ let raw;
1759
+ try {
1760
+ raw = await callLLM(config, sys, userMessage, { temperature: 0, maxTokens: 400 });
1761
+ } catch (e) {
1762
+ this.log(`[direct-universal] LLM call failed: ${e.message}`);
1763
+ return null;
1764
+ }
1765
+ const parsed = this._extractJsonObject(raw);
1766
+ if (!parsed || !parsed.tool || !DESTRUCTIVE_ACTIONS.has(parsed.tool)) return null;
1767
+ if (!parsed.params || typeof parsed.params !== 'object') return null;
1768
+
1769
+ // Per-tool param normalization (matches the executor's accepted shapes).
1770
+ const params = { ...parsed.params };
1771
+ if (parsed.tool === 'calendar_create' && !params.summary) params.summary = params.title || params.name || params.subject;
1772
+ if (parsed.tool === 'calendar_create' && params.start && !params.end) params.end = this._addMinutesIso(params.start, 60);
1773
+
1774
+ try {
1775
+ const result = await executeTool(parsed.tool, params, config);
1776
+ const resultStr = typeof result === 'string' ? result : JSON.stringify(result);
1777
+ const ok = !/error|failed|not\s+found|invalid|does\s+not\s+exist|placeholder/i.test(resultStr);
1778
+
1779
+ if (this._lastDirectAuditChatId) {
1780
+ this._recordAudit(this._lastDirectAuditChatId, {
1781
+ tool: parsed.tool,
1782
+ success: ok,
1783
+ summary: this._summarizeParamsForAudit(parsed.tool, params),
1784
+ });
1785
+ }
1786
+ const message = ok
1787
+ ? this._formatActionResultIT(parsed.tool, params, resultStr)
1788
+ : `Non sono riuscito a eseguire ${parsed.tool}: ${resultStr.slice(0, 240)}`;
1789
+ return { action: parsed.tool, success: ok, message };
1790
+ } catch (e) {
1791
+ return { action: parsed.tool, success: false, message: `Errore durante ${parsed.tool}: ${e.message}` };
1792
+ }
1793
+ }
1794
+
1795
+ /** Natural-language Italian summary for the audit log entry. */
1796
+ _summarizeParamsForAudit(tool, params) {
1797
+ if (tool.startsWith('calendar_')) {
1798
+ const t = params.summary || params.title || '';
1799
+ const s = params.start ? this._formatDateIT(String(params.start).slice(0, 10)) + ` ${String(params.start).slice(11, 16)}` : '';
1800
+ return `"${t}"${s ? ` · ${s}` : ''}`;
1801
+ }
1802
+ if (tool === 'gmail_send' || tool === 'imap_send') return `→ ${params.to || '?'} · "${(params.subject || '').slice(0, 60)}"`;
1803
+ if (tool === 'gmail_reply' || tool === 'imap_reply') return `reply → ${params.to || params.messageId || '?'}`;
1804
+ if (tool === 'slack_send') return `→ ${params.channel || '?'} · "${(params.text || '').slice(0, 60)}"`;
1805
+ if (tool === 'notify_remind') return `"${params.message || ''}" @ ${params.when || '?'}`;
1806
+ if (tool === 'github_create_issue') return `${params.repo || '?'}: "${params.title || ''}"`;
1807
+ if (tool === 'file_write') return `${params.path || '?'} (${(params.content || '').length} chars)`;
1808
+ if (tool === 'drive_upload') return `${params.name || '?'} (${(params.content || '').length} chars)`;
1809
+ if (tool === 'task_done' || tool === 'task_delete' || tool === 'contact_delete') return `"${params.title || params.name || '?'}"`;
1810
+ return JSON.stringify(params).slice(0, 80);
1811
+ }
1812
+
1813
+ /** Italian-language natural response for a successful action. */
1814
+ _formatActionResultIT(tool, params, result) {
1815
+ switch (tool) {
1816
+ case 'calendar_create': {
1817
+ const t = params.summary || params.title || 'evento';
1818
+ const when = params.start ? `${this._formatDateIT(String(params.start).slice(0, 10))} alle ${String(params.start).slice(11, 16)}` : '';
1819
+ return `Fatto. Ho creato l'appuntamento "${t}"${when ? ` il ${when}` : ''}.`;
1820
+ }
1821
+ case 'calendar_move': return `Fatto. Appuntamento spostato.`;
1822
+ case 'calendar_update': return `Fatto. Appuntamento aggiornato.`;
1823
+ case 'calendar_delete': return `Fatto. Appuntamento cancellato.`;
1824
+ case 'gmail_send':
1825
+ case 'imap_send': return `Fatto. Email inviata a ${params.to}.`;
1826
+ case 'gmail_reply':
1827
+ case 'imap_reply': return `Fatto. Risposta inviata.`;
1828
+ case 'gmail_delete': return `Fatto. Email eliminata.`;
1829
+ case 'imap_trash': return `Fatto. Email spostata nel cestino.`;
1830
+ case 'contact_delete': return `Fatto. Contatto "${params.name || ''}" cancellato.`;
1831
+ case 'task_done': return `Fatto. Task "${params.title || ''}" completato.`;
1832
+ case 'task_delete': return `Fatto. Task "${params.title || ''}" cancellato.`;
1833
+ case 'task_clear': return `Fatto. Task list pulita.`;
1834
+ case 'notify_remind': return `Promemoria impostato: "${params.message || ''}" per ${params.when || ''}.`;
1835
+ case 'slack_send': return `Fatto. Messaggio inviato a ${params.channel || ''}.`;
1836
+ case 'github_create_issue': return `Issue creata su ${params.repo}: "${params.title}".`;
1837
+ case 'file_write': return `Fatto. File ${params.path} scritto (${(params.content || '').length} caratteri).`;
1838
+ case 'drive_upload': return `Fatto. "${params.name}" caricato su Google Drive.`;
1839
+ case 'drive_update': return `Fatto. File Drive aggiornato.`;
1840
+ case 'drive_delete': return `Fatto. File Drive eliminato.`;
1841
+ default: return `Fatto. ${result.slice(0, 200)}`;
1842
+ }
1843
+ }
1844
+
1666
1845
  // ── Direct fresh calendar action (no LLM) ─────────────────────────────────
1667
1846
  // Detects DELETE / LIST_MONTH / LIST_WEEK / LIST_DAY / LIST_TODAY /
1668
1847
  // LIST_TOMORROW intents from a fresh user message and runs the proper tool
@@ -63,30 +63,39 @@ function getTsxPath() {
63
63
 
64
64
  /** Actions that mutate external state and require user confirmation. */
65
65
  export const DESTRUCTIVE_ACTIONS = new Set([
66
- 'gmail_send',
67
- 'gmail_send_attach',
68
- 'gmail_reply',
69
- 'gmail_delete',
70
- 'imap_send',
71
- 'imap_reply',
72
- 'imap_bulk_send',
73
- 'imap_send_template',
74
- 'imap_trash',
75
- 'calendar_create',
76
- 'calendar_move',
77
- 'calendar_update',
78
- 'calendar_delete',
79
- 'contact_delete',
80
- 'task_done',
81
- 'task_delete',
82
- 'task_clear',
83
- 'notify_remind',
84
- 'slack_send',
85
- 'github_create_issue',
86
- 'file_write',
87
- 'drive_upload',
88
- 'drive_update',
89
- 'drive_delete',
66
+ // Gmail
67
+ 'gmail_send', 'gmail_send_attach', 'gmail_reply', 'gmail_delete',
68
+ 'gmail_mark_read', 'gmail_mark_starred', 'gmail_archive', 'gmail_trash',
69
+ // IMAP (custom email accounts)
70
+ 'imap_send', 'imap_reply', 'imap_bulk_send', 'imap_send_template',
71
+ 'imap_trash', 'imap_mark_read', 'imap_mark_starred', 'imap_draft',
72
+ // Calendar
73
+ 'calendar_create', 'calendar_move', 'calendar_update', 'calendar_delete',
74
+ // Contacts
75
+ 'contact_add', 'contact_update', 'contact_delete',
76
+ // Tasks (local) + Google Tasks
77
+ 'task_add', 'task_done', 'task_delete', 'task_clear',
78
+ 'gtask_add', 'gtask_complete', 'gtask_delete',
79
+ // Notes
80
+ 'note_add',
81
+ // Reminders / notifications
82
+ 'notify_remind', 'reminder_create',
83
+ // Slack
84
+ 'slack_send', 'slack_dm', 'slack_react', 'slack_mark_read',
85
+ // Notion
86
+ 'notion_page', 'notion_update',
87
+ // GitHub
88
+ 'github_create_issue', 'github_comment',
89
+ // File system (local)
90
+ 'file_write', 'file_move', 'file_delete', 'file_mkdir',
91
+ // Google Drive
92
+ 'drive_upload', 'drive_update', 'drive_delete', 'drive_move', 'drive_share',
93
+ // Birthdays
94
+ 'birthday_add', 'birthday_update', 'birthday_delete',
95
+ // Alexandria messaging
96
+ 'alexandria_send',
97
+ // Cron / scheduling
98
+ 'cron_create', 'cron_delete',
90
99
  ]);
91
100
 
92
101
  // ── Tool Definitions (for system prompt) ─────────────────────────────────────