nothumanallowed 15.1.11 → 15.1.13
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.
|
|
3
|
+
"version": "15.1.13",
|
|
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.
|
|
8
|
+
export const VERSION = '15.1.13';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|
|
@@ -36,6 +36,16 @@ const ROUTING_TABLE = [
|
|
|
36
36
|
'promemoria', 'ricordami', 'prenotazione', 'evento', 'eventi',
|
|
37
37
|
'disponibilità', 'domani', 'settimana', 'oggi', 'questa settimana',
|
|
38
38
|
'prossima settimana', 'orario', 'orari', 'stamattina', 'stasera',
|
|
39
|
+
// Calendar action verbs IT/EN — without these, "cancella appuntamento"
|
|
40
|
+
// falls through to CONDUCTOR which has no calendar context.
|
|
41
|
+
'cancella', 'cancellare', 'cancellalo', 'cancellali',
|
|
42
|
+
'elimina', 'eliminare', 'eliminalo', 'eliminali',
|
|
43
|
+
'rimuovi', 'rimuovere',
|
|
44
|
+
'sposta', 'spostare', 'sposto',
|
|
45
|
+
'modifica', 'modificare', 'modificalo',
|
|
46
|
+
'correggi', 'correggere',
|
|
47
|
+
'rinomina', 'rinominare',
|
|
48
|
+
'delete', 'remove', 'move', 'update', 'rename', 'reschedule',
|
|
39
49
|
// Email EN+IT
|
|
40
50
|
'email', 'emails', 'mail', 'inbox', 'unread', 'posta',
|
|
41
51
|
'non lette', 'da leggere', 'controlla', 'controllare',
|
|
@@ -201,10 +211,19 @@ function isContinuationMessage(text, lastCtx) {
|
|
|
201
211
|
// Short messages (≤ 6 words) without clear new-request keywords are likely continuations
|
|
202
212
|
const words = lower.split(/\s+/);
|
|
203
213
|
if (words.length <= 6) {
|
|
214
|
+
// ONLY truly fresh-topic verbs go here. Action verbs that operate on a
|
|
215
|
+
// referenced item ("cancella X", "elimina Y", "sposta Z") are NOT fresh
|
|
216
|
+
// requests — they continue the previous turn. Previously `delete` and
|
|
217
|
+
// `cancel` were here and broke sticky-mode for Italian users (because
|
|
218
|
+
// `cancel` is a substring of `cancella`).
|
|
204
219
|
const NEW_REQUEST_KEYWORDS = ['calendario','appuntamento','email','posta','meteo','tempo',
|
|
205
220
|
'crea','aggiungi','cerca','trova','mostra','mandami','dimmi','quanto','quando','dove',
|
|
206
|
-
'create','add','find','search','show','send','
|
|
207
|
-
|
|
221
|
+
'create','add','find','search','show','send','weather','mail','event'];
|
|
222
|
+
// Use word-boundary matching to avoid false positives (e.g. "cancel" inside "cancella")
|
|
223
|
+
const hasNewKeyword = NEW_REQUEST_KEYWORDS.some(k => {
|
|
224
|
+
const re = new RegExp(`\\b${k}\\b`, 'i');
|
|
225
|
+
return re.test(lower);
|
|
226
|
+
});
|
|
208
227
|
if (!hasNewKeyword) return true;
|
|
209
228
|
}
|
|
210
229
|
|
|
@@ -174,7 +174,7 @@ TOOLS:
|
|
|
174
174
|
|
|
175
175
|
IMPORTANT: When user says "inserisci appuntamento" or "crea evento" → use calendar_create, NOT calendar_find.
|
|
176
176
|
Extract the summary, date, and time from the user message. If end time is not specified, default to 1 hour after start.
|
|
177
|
-
The tool RESPONSE will include
|
|
177
|
+
The tool RESPONSE will include the real eventId in parentheses (a long lowercase alphanumeric Google ID — NEVER invent one). REMEMBER this exact value. If the user later says "correggi", "modifica", "cambia", "sposta", "elimina" referring to this same event, use calendar_update / calendar_move / calendar_delete with that exact eventId — do NOT create a second event. Never use placeholder IDs like "ABC123" or "event_123" — those are illustrative only.
|
|
178
178
|
|
|
179
179
|
15. calendar_move(eventId: string, newStart: string, newEnd: string)
|
|
180
180
|
Reschedule an event. ALWAYS confirm before moving.
|
|
@@ -191,13 +191,13 @@ TOOLS:
|
|
|
191
191
|
Only include fields that need to change.
|
|
192
192
|
|
|
193
193
|
HOW TO GET eventId — IN ORDER OF PREFERENCE:
|
|
194
|
-
1. From your OWN previous tool response in this conversation: when you ran calendar_create earlier, the response
|
|
195
|
-
2. If step 1 doesn't apply
|
|
194
|
+
1. From your OWN previous tool response in this conversation: when you ran calendar_create / calendar_find / calendar_date earlier, the response contained the REAL eventId (a long lowercase alphanumeric Google-issued string). Use that exact value verbatim.
|
|
195
|
+
2. If step 1 doesn't apply, call calendar_find or calendar_date FIRST to look it up.
|
|
196
|
+
3. NEVER fabricate an eventId. NEVER copy placeholder strings from this documentation. Real eventIds come from Google Calendar tool responses only.
|
|
196
197
|
|
|
197
198
|
CRITICAL — "CORREGGI" / "MODIFICA" / "CAMBIA TITOLO" mappings:
|
|
198
|
-
When the user says "correggi", "modifica", "cambia", "rinomina", "sposta", "aggiorna" referring to the most recent event you just created,
|
|
199
|
+
When the user says "correggi", "modifica", "cambia", "rinomina", "sposta", "aggiorna" referring to the most recent event you just created, use calendar_update with the real eventId returned by your previous calendar_create call.
|
|
199
200
|
Do NOT call calendar_create a second time — that would create a DUPLICATE event.
|
|
200
|
-
Example: you just created event ABC123 ("Visita BMW"). User says "correggi, il titolo è Tagliando BMW" → call calendar_update(eventId: "ABC123", summary: "Tagliando BMW"). Don't create a new event, don't delete-then-create.
|
|
201
201
|
|
|
202
202
|
19. calendar_delete(eventId: string)
|
|
203
203
|
Delete (permanently remove) a calendar event by its eventId.
|
|
@@ -691,20 +691,20 @@ calendar_create(summary, start, end, description?, attendees?, location?)
|
|
|
691
691
|
- summary = SHORT title (e.g. "Tagliando BMW"). REQUIRED. Do NOT leave empty.
|
|
692
692
|
- description = optional notes only. Do NOT put the title here.
|
|
693
693
|
- start/end = ISO 8601 datetimes. Default end = start + 1 hour.
|
|
694
|
-
- The tool response includes "(eventId:
|
|
695
|
-
Example — user
|
|
694
|
+
- The tool response includes the REAL eventId in parentheses, e.g. "(eventId: <some-google-id>)". Use that exact value verbatim for subsequent operations. **The eventId is a long lowercase alphanumeric string returned by Google — NEVER make one up.**
|
|
695
|
+
Example — user says "fissa tagliando BMW 15 maggio ore 17":
|
|
696
696
|
{"action":"calendar_create","params":{"summary":"Tagliando BMW","start":"2026-05-15T17:00:00","end":"2026-05-15T18:00:00"}}
|
|
697
697
|
|
|
698
698
|
calendar_update(eventId, summary?, location?, description?, start?, end?)
|
|
699
699
|
- Use this for "correggi", "modifica", "rinomina", "cambia titolo", "sposta", "aggiorna" referring to an event you JUST created or found.
|
|
700
|
-
- eventId
|
|
701
|
-
-
|
|
702
|
-
|
|
700
|
+
- eventId MUST come from a real tool response (calendar_create / calendar_find / calendar_date). NEVER invent it.
|
|
701
|
+
- DO NOT use placeholder strings like "ABC123", "DEF456", "abc123", "event_123" — those are not real eventIds.
|
|
702
|
+
- If you don't have a real eventId, call calendar_find or calendar_date FIRST. Do not guess.
|
|
703
703
|
|
|
704
|
-
calendar_delete(eventId) — Delete an event you have
|
|
704
|
+
calendar_delete(eventId) — Delete an event you have a REAL eventId for. Same rules as update: never invent, never use placeholders.
|
|
705
705
|
calendar_move(eventId, newStart, newEnd) — Reschedule.
|
|
706
706
|
calendar_find(query, daysAhead?) — Search events by name. Returns eventIds. Use ONLY when you don't already have the id.
|
|
707
|
-
calendar_today() / calendar_tomorrow() / calendar_date(date) / calendar_upcoming(hours?) / calendar_week(startDate?) / calendar_month(month?) — Read-only listings.
|
|
707
|
+
calendar_today() / calendar_tomorrow() / calendar_date(date) / calendar_upcoming(hours?) / calendar_week(startDate?) / calendar_month(month?) — Read-only listings. **When the user says "elimina gli appuntamenti di [date]", call calendar_date(date) FIRST to get real eventIds, then calendar_delete with each real id.**
|
|
708
708
|
schedule_meeting(clientName, subject, location, durationMinutes, dateFrom, dateTo) — Find optimal slots considering travel time.
|
|
709
709
|
|
|
710
710
|
### Gmail / IMAP
|
|
@@ -1532,9 +1532,21 @@ export async function executeTool(action, params, config) {
|
|
|
1532
1532
|
}
|
|
1533
1533
|
|
|
1534
1534
|
case 'calendar_update': {
|
|
1535
|
+
let eventId = String(params.eventId || '').trim();
|
|
1536
|
+
if (!eventId) return 'eventId required. Call calendar_find or calendar_date first to get the REAL eventId.';
|
|
1537
|
+
|
|
1538
|
+
// Reject obvious placeholder IDs (model copied from prompt examples)
|
|
1539
|
+
const PLACEHOLDERS = new Set([
|
|
1540
|
+
'abc123', 'def456', 'xyz789', 'event_123', 'event_123456789',
|
|
1541
|
+
'123', '456', 'foo', 'bar', 'baz', 'example', 'placeholder',
|
|
1542
|
+
'eventid', 'event_id', 'id', 'null', 'undefined',
|
|
1543
|
+
]);
|
|
1544
|
+
if (PLACEHOLDERS.has(eventId.toLowerCase())) {
|
|
1545
|
+
return `"${eventId}" is a placeholder, not a real eventId. Call calendar_find or calendar_date FIRST to get real eventIds.`;
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1535
1548
|
// Smart eventId resolution: if it looks like a name instead of a Google Calendar ID, search for it
|
|
1536
|
-
|
|
1537
|
-
if (eventId && (eventId.includes(' ') || eventId.length < 10 || /[A-Z]/.test(eventId))) {
|
|
1549
|
+
if (eventId.includes(' ') || eventId.length < 10 || /[A-Z]/.test(eventId)) {
|
|
1538
1550
|
const from = new Date();
|
|
1539
1551
|
const to = new Date(from.getTime() + 14 * 86400000);
|
|
1540
1552
|
const events = await listEvents(config, 'primary', from, to);
|
|
@@ -1566,16 +1578,39 @@ export async function executeTool(action, params, config) {
|
|
|
1566
1578
|
if (Object.keys(patch).length === 0) {
|
|
1567
1579
|
return 'No fields to update. Specify at least one of: summary (title), location, description, start, end.';
|
|
1568
1580
|
}
|
|
1569
|
-
|
|
1581
|
+
try {
|
|
1582
|
+
await updateEvent(config, 'primary', eventId, patch);
|
|
1583
|
+
} catch (err) {
|
|
1584
|
+
const msg = String(err?.message || err);
|
|
1585
|
+
if (msg.includes('404') || msg.toLowerCase().includes('not found')) {
|
|
1586
|
+
return `eventId "${eventId}" does NOT exist in Google Calendar. The ID looks invented — DO NOT retry with this value. Call calendar_date or calendar_find FIRST to retrieve real eventIds, then call calendar_update with one of those.`;
|
|
1587
|
+
}
|
|
1588
|
+
throw err;
|
|
1589
|
+
}
|
|
1570
1590
|
const changes = Object.keys(patch).join(', ');
|
|
1571
1591
|
return `Event updated successfully (changed: ${changes})${newSummary ? `. New title: "${newSummary}"` : ''}${params.location ? `. New location: ${params.location}` : ''}`;
|
|
1572
1592
|
}
|
|
1573
1593
|
|
|
1574
1594
|
case 'calendar_delete': {
|
|
1575
|
-
if (!params.eventId) return 'eventId required. Call calendar_find first to get the eventId.';
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1595
|
+
if (!params.eventId) return 'eventId required. Call calendar_find or calendar_date first to get the REAL eventId.';
|
|
1596
|
+
let delEventId = String(params.eventId).trim();
|
|
1597
|
+
|
|
1598
|
+
// Reject placeholder IDs the model may have copied verbatim from prompt
|
|
1599
|
+
// examples or hallucinated wholesale. These are obvious giveaways — real
|
|
1600
|
+
// Google Calendar eventIds are long lowercase alphanumeric strings, not
|
|
1601
|
+
// dictionary words.
|
|
1602
|
+
const PLACEHOLDER_IDS = new Set([
|
|
1603
|
+
'abc123', 'def456', 'xyz789', 'event_123', 'event_123456789',
|
|
1604
|
+
'123', '456', 'foo', 'bar', 'baz', 'example', 'placeholder',
|
|
1605
|
+
'eventid', 'event_id', 'id', 'null', 'undefined',
|
|
1606
|
+
]);
|
|
1607
|
+
if (PLACEHOLDER_IDS.has(delEventId.toLowerCase())) {
|
|
1608
|
+
return `"${delEventId}" is a placeholder, not a real eventId. Call calendar_date(YYYY-MM-DD) or calendar_find(query) FIRST to get the real eventIds, then call calendar_delete with one of those values.`;
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
// Branch A — looks like a free-text search query (spaces, caps, very short).
|
|
1612
|
+
// The model passed a NAME instead of an ID — resolve to ID via listEvents.
|
|
1613
|
+
if (delEventId.includes(' ') || delEventId.length < 10 || /[A-Z]/.test(delEventId)) {
|
|
1579
1614
|
const fromD = new Date();
|
|
1580
1615
|
const toD = new Date(fromD.getTime() + 60 * 86400000);
|
|
1581
1616
|
const evts = await listEvents(config, 'primary', fromD, toD);
|
|
@@ -1583,11 +1618,39 @@ export async function executeTool(action, params, config) {
|
|
|
1583
1618
|
if (m) {
|
|
1584
1619
|
delEventId = m.id;
|
|
1585
1620
|
} else {
|
|
1586
|
-
return `Could not find event matching "${params.eventId}" in the next 60 days. Use calendar_find to
|
|
1621
|
+
return `Could not find event matching "${params.eventId}" in the next 60 days. Use calendar_find or calendar_date to get the real eventId first.`;
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
// Branch B — try the delete. If Google returns 404 the ID is invalid
|
|
1626
|
+
// (almost always means the model hallucinated it). Surface this clearly
|
|
1627
|
+
// to the LLM so its next turn doesn't try the same fake ID again.
|
|
1628
|
+
try {
|
|
1629
|
+
await deleteEvent(config, 'primary', delEventId);
|
|
1630
|
+
return `Event ${delEventId} deleted successfully.`;
|
|
1631
|
+
} catch (err) {
|
|
1632
|
+
const msg = String(err?.message || err);
|
|
1633
|
+
const is404 = msg.includes('404') || msg.toLowerCase().includes('not found');
|
|
1634
|
+
if (is404) {
|
|
1635
|
+
// Try to look up real events near the requested date (if user hinted one)
|
|
1636
|
+
const hintDate = params.date || params.day || params.on;
|
|
1637
|
+
let hint = '';
|
|
1638
|
+
if (hintDate) {
|
|
1639
|
+
try {
|
|
1640
|
+
const d = new Date(hintDate + 'T00:00:00');
|
|
1641
|
+
const from = d;
|
|
1642
|
+
const to = new Date(d.getTime() + 86400000);
|
|
1643
|
+
const evts = await listEvents(config, 'primary', from, to);
|
|
1644
|
+
if (evts.length > 0) {
|
|
1645
|
+
hint = `\n\nReal events on ${hintDate} (use these IDs, NOT invented ones):\n` +
|
|
1646
|
+
evts.map(e => `- [eventId: ${e.id}] ${formatTime(e.start)} — ${e.summary || '(no title)'}`).join('\n');
|
|
1647
|
+
}
|
|
1648
|
+
} catch { /* ignore */ }
|
|
1649
|
+
}
|
|
1650
|
+
return `eventId "${delEventId}" does NOT exist in Google Calendar. Looks like it was invented — DO NOT retry with this ID. Call calendar_date(YYYY-MM-DD) or calendar_find(query) FIRST to get the real eventId, then call calendar_delete with the value you receive.${hint}`;
|
|
1587
1651
|
}
|
|
1652
|
+
throw err;
|
|
1588
1653
|
}
|
|
1589
|
-
await deleteEvent(config, 'primary', delEventId);
|
|
1590
|
-
return `Event deleted successfully.`;
|
|
1591
1654
|
}
|
|
1592
1655
|
|
|
1593
1656
|
// ── Smart Scheduling ──────────────────────────────────────────────────
|