nothumanallowed 16.0.9 → 16.0.11

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.9",
3
+ "version": "16.0.11",
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.9';
8
+ export const VERSION = '16.0.11';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -1908,6 +1908,52 @@ class TelegramResponder {
1908
1908
  async _tryDirectFreshCalendarAction(userMessage, config) {
1909
1909
  if (!userMessage || typeof userMessage !== 'string') return null;
1910
1910
  const lower = userMessage.toLowerCase().normalize('NFD').replace(/[̀-ͯ]/g, '');
1911
+ const chatId = this._lastDirectAuditChatId;
1912
+ const { executeTool: _executeToolPre } = await import('./tool-executor.mjs');
1913
+
1914
+ // ─── ANAPHORIC delete + CONFIRMATION yes ────────────────────────────────
1915
+ // If the previous turn ran a LIST/LAST-SHOWN and the user now says
1916
+ // "cancellalo / eliminalo / quello / si / conferma / fallo", resolve the
1917
+ // referent from this._lastContextByChatId[chatId].lastCalendarEvents.
1918
+ const isAnaphoric = /\b(cancell|elimin|rimuov)[aeiloy]+(lo|la|li|le|gli)?\b/.test(lower)
1919
+ && !this._extractCalendarProposal(userMessage).date
1920
+ && !this._extractCalendarProposal(userMessage).title;
1921
+ const isYesConfirm = /^\s*(s[ìi]\b|si\s|sì\s|ok\b|okay\b|certo\b|certamente\b|d'?accordo\b|fai|fallo|procedi|esegui|conferm[oa]|yes\b|yep\b|confirm\b|do\s*it|go\s*ahead)/i.test(userMessage.trim());
1922
+ if (chatId && (isAnaphoric || isYesConfirm)) {
1923
+ const ctx = this._lastContextByChatId[chatId] || {};
1924
+ const pendingEvents = ctx.lastCalendarEvents || ctx.pendingDeleteEvents || [];
1925
+ // Strict: only auto-execute if the previous turn LIST/proposal had a
1926
+ // single deletable event, or if pendingDelete is explicitly set.
1927
+ const eligible = ctx.pendingDeleteEvents && ctx.pendingDeleteEvents.length > 0
1928
+ ? ctx.pendingDeleteEvents
1929
+ : (pendingEvents.length === 1 ? pendingEvents : null);
1930
+ if (eligible && eligible.length > 0) {
1931
+ let ok = 0, ko = 0;
1932
+ const failed = [];
1933
+ for (const ev of eligible) {
1934
+ if (!ev.eventId) { ko++; failed.push(ev.summary || '(no id)'); continue; }
1935
+ try {
1936
+ await _executeToolPre('calendar_delete', { eventId: ev.eventId }, config);
1937
+ ok++;
1938
+ } catch (e) {
1939
+ ko++;
1940
+ failed.push(`${ev.summary || ev.eventId} (${e.message?.slice(0, 60) || 'err'})`);
1941
+ }
1942
+ }
1943
+ // Clear the pending state so we don't double-delete on next yes.
1944
+ delete this._lastContextByChatId[chatId].pendingDeleteEvents;
1945
+ delete this._lastContextByChatId[chatId].lastCalendarEvents;
1946
+ try { (await import('./telegram-context.mjs')).saveTelegramContext(this._lastContextByChatId); } catch {}
1947
+
1948
+ const subject = eligible.length === 1 ? `"${eligible[0].summary}"` : `${eligible.length} appuntamenti`;
1949
+ const lines = [`Ho cancellato ${subject}.`];
1950
+ if (ko > 0) lines.push(`Non sono riuscito a cancellarne ${ko}: ${failed.slice(0, 3).join(', ')}`);
1951
+ this._recordAudit(chatId, { tool: 'calendar_delete', success: ok > 0, summary: `cancellati ${ok}` });
1952
+ return { action: 'calendar_delete', success: ok > 0, message: lines.join('\n') };
1953
+ }
1954
+ // No eligible referent — fall through; the LLM will ask for clarification.
1955
+ }
1956
+
1911
1957
 
1912
1958
  const MONTHS_IT = { gennaio:1, febbraio:2, marzo:3, aprile:4, maggio:5, giugno:6, luglio:7, agosto:8, settembre:9, ottobre:10, novembre:11, dicembre:12 };
1913
1959
  const MONTHS_EN = { january:1, february:2, march:3, april:4, may:5, june:6, july:7, august:8, september:9, october:10, november:11, december:12, jan:1, feb:2, mar:3, apr:4, jun:6, jul:7, aug:8, sep:9, oct:10, nov:11, dec:12 };
@@ -2090,54 +2136,44 @@ class TelegramResponder {
2090
2136
 
2091
2137
  // ─── LIST intents ──────────────────────────────────────────────────────
2092
2138
  if (isList && !isDelete && !isVerify && !isCreate && !isMove) {
2093
- // "appuntamenti di oggi"
2094
- if (/\b(oggi|today)\b/.test(lower)) {
2095
- try {
2096
- const out = await executeTool('calendar_today', {}, config);
2097
- return { action: 'calendar_today', success: true, message: String(out) };
2098
- } catch (e) { return { action: 'calendar_today', success: false, message: `Errore: ${e.message}` }; }
2099
- }
2100
- // "appuntamenti di domani"
2101
- if (/\b(domani|tomorrow)\b/.test(lower)) {
2102
- try {
2103
- const out = await executeTool('calendar_tomorrow', {}, config);
2104
- return { action: 'calendar_tomorrow', success: true, message: String(out) };
2105
- } catch (e) { return { action: 'calendar_tomorrow', success: false, message: `Errore: ${e.message}` }; }
2106
- }
2107
- // "appuntamenti della settimana"
2108
- if (/\b(settimana|week|questa\s+settimana|this\s+week)\b/.test(lower)) {
2139
+ // Helper that runs the tool, parses the events and remembers them for
2140
+ // anaphoric resolution in the NEXT turn ("cancellalo", "spostali tutti").
2141
+ const runListAndRemember = async (toolName, args, actionKey) => {
2109
2142
  try {
2110
- const out = await executeTool('calendar_week', {}, config);
2111
- return { action: 'calendar_week', success: true, message: String(out) };
2112
- } catch (e) { return { action: 'calendar_week', success: false, message: `Errore: ${e.message}` }; }
2113
- }
2114
- // "appuntamenti di maggio" — month name + optional year
2143
+ const out = await executeTool(toolName, args, config);
2144
+ const events = this._parseEventsFromToolOutput(String(out));
2145
+ if (chatId) {
2146
+ const prev = this._lastContextByChatId[chatId] || {};
2147
+ this._lastContextByChatId[chatId] = {
2148
+ ...prev,
2149
+ lastCalendarEvents: events,
2150
+ lastCalendarListAt: Date.now(),
2151
+ lastCalendarSource: { tool: toolName, args },
2152
+ };
2153
+ try { (await import('./telegram-context.mjs')).saveTelegramContext(this._lastContextByChatId); } catch {}
2154
+ }
2155
+ return { action: actionKey, success: true, message: String(out) };
2156
+ } catch (e) { return { action: actionKey, success: false, message: `Errore: ${e.message}` }; }
2157
+ };
2158
+
2159
+ if (/\b(oggi|today)\b/.test(lower))
2160
+ return await runListAndRemember('calendar_today', {}, 'calendar_today');
2161
+ if (/\b(domani|tomorrow)\b/.test(lower))
2162
+ return await runListAndRemember('calendar_tomorrow', {}, 'calendar_tomorrow');
2163
+ if (/\b(settimana|week|questa\s+settimana|this\s+week)\b/.test(lower))
2164
+ return await runListAndRemember('calendar_week', {}, 'calendar_week');
2115
2165
  const monthMatch = lower.match(new RegExp(`\\b(${Object.keys(MONTH_MAP).join('|')})(?:\\s+(20\\d{2}))?\\b`));
2116
2166
  if (monthMatch) {
2117
2167
  const monthNum = MONTH_MAP[monthMatch[1]];
2118
2168
  const yearNum = parseInt(monthMatch[2] || String(new Date().getFullYear()), 10);
2119
- // calendar_month accepts a month string like "2026-05"
2120
2169
  const monthStr = `${yearNum}-${String(monthNum).padStart(2, '0')}`;
2121
- try {
2122
- const out = await executeTool('calendar_month', { month: monthStr }, config);
2123
- return { action: 'calendar_month', success: true, message: String(out) };
2124
- } catch (e) { return { action: 'calendar_month', success: false, message: `Errore: ${e.message}` }; }
2170
+ return await runListAndRemember('calendar_month', { month: monthStr }, 'calendar_month');
2125
2171
  }
2126
- // "appuntamenti del 15 maggio" — specific date
2127
2172
  const dateExtracted = this._extractCalendarProposal(userMessage);
2128
- if (dateExtracted.date) {
2129
- try {
2130
- const out = await executeTool('calendar_date', { date: dateExtracted.date }, config);
2131
- return { action: 'calendar_date', success: true, message: String(out) };
2132
- } catch (e) { return { action: 'calendar_date', success: false, message: `Errore: ${e.message}` }; }
2133
- }
2134
- // Generic "appuntamenti prossimi" → upcoming 48h
2135
- if (/\b(prossim|next|upcoming|in\s+arrivo)/.test(lower)) {
2136
- try {
2137
- const out = await executeTool('calendar_upcoming', { hours: 48 }, config);
2138
- return { action: 'calendar_upcoming', success: true, message: String(out) };
2139
- } catch (e) { return { action: 'calendar_upcoming', success: false, message: `Errore: ${e.message}` }; }
2140
- }
2173
+ if (dateExtracted.date)
2174
+ return await runListAndRemember('calendar_date', { date: dateExtracted.date }, 'calendar_date');
2175
+ if (/\b(prossim|next|upcoming|in\s+arrivo)/.test(lower))
2176
+ return await runListAndRemember('calendar_upcoming', { hours: 48 }, 'calendar_upcoming');
2141
2177
  // Fall through — let the LLM handle ambiguous list requests.
2142
2178
  }
2143
2179
 
@@ -2145,6 +2181,72 @@ class TelegramResponder {
2145
2181
  // Reuse the same proposal extractor — it works on raw user text too,
2146
2182
  // because it just looks for title quotes / dates / times.
2147
2183
  if (isDelete) {
2184
+ // ─── BULK DELETE: "cancella TUTTI gli appuntamenti di MAGGIO"
2185
+ // Trigger when isDelete + "tutti/all/everything" + (month name | week | "oggi" | "domani"
2186
+ // | specific date). Iterate over the list and delete each one. Honest
2187
+ // reporting: tell the user how many were deleted vs failed.
2188
+ const isBulk = /\b(tutti|tutte|all|every(thing)?|in\s+tot)\b/.test(lower);
2189
+ const monthMatch = lower.match(new RegExp(`\\b(${Object.keys(MONTH_MAP).join('|')})(?:\\s+(20\\d{2}))?\\b`));
2190
+ const isOggi = /\b(oggi|today)\b/.test(lower);
2191
+ const isDomani = /\b(domani|tomorrow)\b/.test(lower);
2192
+ const isWeek = /\b(questa\s+settimana|della\s+settimana|this\s+week|della\s+week)\b/.test(lower);
2193
+ if (isBulk && (monthMatch || isOggi || isDomani || isWeek)) {
2194
+ // 1. Fetch the events in that timeframe via the right list tool.
2195
+ let listing = '';
2196
+ let scopeLabel = '';
2197
+ try {
2198
+ if (monthMatch) {
2199
+ const monthNum = MONTH_MAP[monthMatch[1]];
2200
+ const yearNum = parseInt(monthMatch[2] || String(new Date().getFullYear()), 10);
2201
+ const monthStr = `${yearNum}-${String(monthNum).padStart(2, '0')}`;
2202
+ listing = String(await executeTool('calendar_month', { month: monthStr }, config));
2203
+ scopeLabel = `di ${monthMatch[1]} ${yearNum}`;
2204
+ } else if (isOggi) {
2205
+ listing = String(await executeTool('calendar_today', {}, config));
2206
+ scopeLabel = 'di oggi';
2207
+ } else if (isDomani) {
2208
+ listing = String(await executeTool('calendar_tomorrow', {}, config));
2209
+ scopeLabel = 'di domani';
2210
+ } else if (isWeek) {
2211
+ listing = String(await executeTool('calendar_week', {}, config));
2212
+ scopeLabel = 'di questa settimana';
2213
+ }
2214
+ } catch (e) {
2215
+ return { action: 'calendar_delete_bulk', success: false, message: `Errore nel recupero degli appuntamenti: ${e.message}` };
2216
+ }
2217
+ const events = this._parseEventsFromToolOutput(listing);
2218
+ if (!events || events.length === 0) {
2219
+ return { action: 'calendar_delete_bulk', success: true,
2220
+ message: `Nessun appuntamento ${scopeLabel} da cancellare. Calendario già vuoto in quell'intervallo.` };
2221
+ }
2222
+ // 2. Delete each event with a real tool call. Track failures.
2223
+ let ok = 0, ko = 0;
2224
+ const failed = [];
2225
+ for (const ev of events) {
2226
+ if (!ev.eventId) { ko++; failed.push(ev.summary || '(no title)'); continue; }
2227
+ try {
2228
+ await executeTool('calendar_delete', { eventId: ev.eventId }, config);
2229
+ ok++;
2230
+ } catch (e) {
2231
+ ko++;
2232
+ failed.push(`${ev.summary || ev.eventId} (${e.message?.slice(0, 60) || 'err'})`);
2233
+ }
2234
+ }
2235
+ // 3. Honest, factual summary.
2236
+ const lines = [`Ho cancellato ${ok} appuntament${ok === 1 ? 'o' : 'i'} ${scopeLabel}.`];
2237
+ if (ko > 0) {
2238
+ lines.push(`Non sono riuscito a cancellarne ${ko}: ${failed.slice(0, 3).join(', ')}${failed.length > 3 ? '…' : ''}`);
2239
+ }
2240
+ if (this._lastDirectAuditChatId) {
2241
+ this._recordAudit(this._lastDirectAuditChatId, {
2242
+ tool: 'calendar_delete_bulk',
2243
+ success: ok > 0,
2244
+ summary: `${ok} cancellati ${scopeLabel}, ${ko} falliti`,
2245
+ });
2246
+ }
2247
+ return { action: 'calendar_delete_bulk', success: ok > 0, message: lines.join('\n') };
2248
+ }
2249
+
2148
2250
  const extracted = this._extractCalendarProposal(userMessage);
2149
2251
  if (!extracted.date && !extracted.title) return null;
2150
2252