nothumanallowed 15.1.16 → 15.1.17
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.17",
|
|
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.17';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|
|
@@ -278,6 +278,35 @@ async function callAgentWithTools(config, agentName, userMessage, languageOverri
|
|
|
278
278
|
|
|
279
279
|
if (actions.length === 0) {
|
|
280
280
|
finalText = textParts.join('\n').trim();
|
|
281
|
+
|
|
282
|
+
// ── HALLUCINATED SUCCESS DETECTION ────────────────────────────────
|
|
283
|
+
// The model sometimes writes "X è stato cancellato/creato/inviato con
|
|
284
|
+
// successo" WITHOUT actually emitting a tool block — pure hallucination.
|
|
285
|
+
// If that happens and we're early in the round budget, force ONE retry
|
|
286
|
+
// with an explicit instruction to emit the tool JSON. If it still
|
|
287
|
+
// refuses, replace the fake success with a warning so the user knows
|
|
288
|
+
// the action did NOT actually happen.
|
|
289
|
+
const claimsSuccess = isCompletedAction(finalText);
|
|
290
|
+
const hasReferenceToTool = /\b(eventid|event id|id evento)\b/i.test(finalText);
|
|
291
|
+
const looksFakeSuccess = claimsSuccess || (hasReferenceToTool && round === 0);
|
|
292
|
+
if (looksFakeSuccess && round < 1) {
|
|
293
|
+
// Push the bad response as history so the model sees what it just did,
|
|
294
|
+
// then ask it to actually emit the tool.
|
|
295
|
+
history.push({ role: 'assistant', content: response });
|
|
296
|
+
userMessage =
|
|
297
|
+
`STOP. Hai dichiarato che un'azione è stata completata ma NON hai emesso nessun blocco tool JSON. ` +
|
|
298
|
+
`Senza il blocco tool, NESSUNA azione viene eseguita davvero. ` +
|
|
299
|
+
`Ora emetti il blocco JSON corretto (\`\`\`json ... \`\`\`) per l'azione richiesta dall'utente, usando i parametri corretti. ` +
|
|
300
|
+
`Se ti serve un eventId, chiama prima calendar_find o calendar_date. Non scrivere altro testo finché non hai eseguito davvero il tool.`;
|
|
301
|
+
continue; // restart the loop with the corrective user message
|
|
302
|
+
}
|
|
303
|
+
if (looksFakeSuccess && round >= 1) {
|
|
304
|
+
// Second attempt also lied — refuse to forward the fake success.
|
|
305
|
+
finalText =
|
|
306
|
+
`Non sono riuscito a eseguire l'azione automaticamente (il modello ha dichiarato un successo senza eseguire il tool). ` +
|
|
307
|
+
`Riprova riformulando la richiesta, oppure dimmi esattamente cosa vuoi che faccia.`;
|
|
308
|
+
}
|
|
309
|
+
|
|
281
310
|
break;
|
|
282
311
|
}
|
|
283
312
|
|
|
@@ -696,7 +725,7 @@ class TelegramResponder {
|
|
|
696
725
|
return d.text || '';
|
|
697
726
|
}
|
|
698
727
|
|
|
699
|
-
throw new Error('
|
|
728
|
+
throw new Error('servizio di trascrizione vocale momentaneamente non disponibile (NHA proxy irraggiungibile)');
|
|
700
729
|
}
|
|
701
730
|
|
|
702
731
|
async _handleMessage(message) {
|
|
@@ -730,7 +759,7 @@ class TelegramResponder {
|
|
|
730
759
|
this.log(`[Telegram] Voice transcription failed: ${err.message}`);
|
|
731
760
|
await this._telegramCall('sendMessage', {
|
|
732
761
|
chat_id: chatId,
|
|
733
|
-
text: `Non riesco a trascrivere il vocale
|
|
762
|
+
text: `Non riesco a trascrivere il vocale (${err.message}).\n\nPer abilitare la trascrizione vocale gratuita, dal computer esegui:\nnha config set groqKey TUA_CHIAVE_GROQ\n\nLa chiave si ottiene gratis su https://console.groq.com/keys`,
|
|
734
763
|
});
|
|
735
764
|
return;
|
|
736
765
|
}
|
|
@@ -687,9 +687,13 @@ Never output a JSON block as a suggestion — every block executes immediately.
|
|
|
687
687
|
## ABSOLUTE RULES (violate these and the user loses trust)
|
|
688
688
|
|
|
689
689
|
1. NEVER invent tool output. If you need data (an event ID, a price, a search result), you MUST emit a tool JSON block and wait for its real response. Do NOT write fake "(eventId: 123456789)" or fake results.
|
|
690
|
-
2. NEVER
|
|
691
|
-
3. NEVER
|
|
692
|
-
4.
|
|
690
|
+
2. NEVER claim an action was completed unless you have just received a SUCCESS tool result for that action in this same turn. NEVER write "cancellato con successo", "creato con successo", "inviato" etc. without a real tool execution behind it. If you didn't emit the tool block, the action didn't happen.
|
|
691
|
+
3. NEVER duplicate. If the user says "correggi/modifica/sposta/aggiorna" referring to an item you just created in this conversation, use the corresponding *_update / *_move tool with the eventId/taskId from your previous tool response. Do NOT create a second item.
|
|
692
|
+
4. NEVER put the TITLE in the description field. The "summary" / "title" field is the SHORT name (what shows on a calendar grid). The "description" field is ONLY for extra notes.
|
|
693
|
+
5. NEVER invent times. If the user did not specify a time, ASK them ("A che ora?"). Do not default to 10:00 or any other time silently. Same for missing date, duration, attendees.
|
|
694
|
+
6. When the user confirms ("procedi", "sì", "fallo", "ok", "vai") AND there is a pending action from the IMMEDIATELY PREVIOUS assistant turn (you proposed something like "Posso cancellare X. Procedo?"), EXECUTE that exact pending action with the same parameters — do NOT search again, do NOT propose again, do NOT ask again. Emit the tool block for the action you just proposed.
|
|
695
|
+
7. When in doubt, ASK ONE concise question — do not invent details.
|
|
696
|
+
8. Tool JSON blocks MUST be wrapped in \`\`\`json ... \`\`\` fences. If you emit raw JSON without fences, the system can sometimes still parse it but it's unreliable — always use the fences.
|
|
693
697
|
|
|
694
698
|
## TOOL SIGNATURES (parameters and how to use them)
|
|
695
699
|
|
|
@@ -814,12 +818,53 @@ export function parseActions(text) {
|
|
|
814
818
|
const trailing = normalized.slice(lastIndex).trim();
|
|
815
819
|
if (trailing) textParts.push(trailing);
|
|
816
820
|
|
|
817
|
-
// Fallback: if no fenced blocks found, scan for bare {"action": ...} objects
|
|
821
|
+
// Fallback: if no fenced blocks found, scan for bare {"action": ...} objects
|
|
822
|
+
// in the text. The previous regex was naive and stopped at the first `}`,
|
|
823
|
+
// breaking on nested params like {"action":"X","params":{"summary":"Y"}}.
|
|
824
|
+
// Now we do proper brace balancing so we always capture the FULL object.
|
|
818
825
|
if (actions.length === 0) {
|
|
826
|
+
const consumed = new Set();
|
|
827
|
+
const findBareActions = (src) => {
|
|
828
|
+
let i = 0;
|
|
829
|
+
while (i < src.length) {
|
|
830
|
+
// Quick filter: only consider candidates that start with {"action"
|
|
831
|
+
const idx = src.indexOf('{"action"', i);
|
|
832
|
+
if (idx < 0) break;
|
|
833
|
+
// Walk forward balancing braces, respecting strings
|
|
834
|
+
let depth = 0;
|
|
835
|
+
let inStr = false;
|
|
836
|
+
let escape = false;
|
|
837
|
+
let end = -1;
|
|
838
|
+
for (let j = idx; j < src.length; j++) {
|
|
839
|
+
const c = src[j];
|
|
840
|
+
if (escape) { escape = false; continue; }
|
|
841
|
+
if (c === '\\' && inStr) { escape = true; continue; }
|
|
842
|
+
if (c === '"' && !escape) { inStr = !inStr; continue; }
|
|
843
|
+
if (inStr) continue;
|
|
844
|
+
if (c === '{') depth++;
|
|
845
|
+
else if (c === '}') {
|
|
846
|
+
depth--;
|
|
847
|
+
if (depth === 0) { end = j; break; }
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
if (end < 0) break;
|
|
851
|
+
const candidate = src.slice(idx, end + 1);
|
|
852
|
+
try {
|
|
853
|
+
const parsed = JSON.parse(candidate);
|
|
854
|
+
if (parsed.action && typeof parsed.action === 'string' && !consumed.has(candidate)) {
|
|
855
|
+
actions.push({ action: parsed.action, params: parsed.params || {} });
|
|
856
|
+
consumed.add(candidate);
|
|
857
|
+
}
|
|
858
|
+
} catch { /* not valid JSON, skip */ }
|
|
859
|
+
i = end + 1;
|
|
860
|
+
}
|
|
861
|
+
};
|
|
862
|
+
findBareActions(text);
|
|
863
|
+
// Legacy fallback kept for very loose JSON shapes — only fires if the
|
|
864
|
+
// brace scanner found nothing.
|
|
819
865
|
const bareRegex = /\{[\s\S]*?"action"\s*:\s*"[^"]+[\s\S]*?\}/g;
|
|
820
866
|
let bareMatch;
|
|
821
|
-
|
|
822
|
-
while ((bareMatch = bareRegex.exec(text)) !== null) {
|
|
867
|
+
while (actions.length === 0 && (bareMatch = bareRegex.exec(text)) !== null) {
|
|
823
868
|
try {
|
|
824
869
|
const parsed = JSON.parse(bareMatch[0]);
|
|
825
870
|
if (parsed.action && typeof parsed.action === 'string' && !consumed.has(bareMatch[0])) {
|