nothumanallowed 13.2.25 → 13.2.27

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": "13.2.25",
3
+ "version": "13.2.27",
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": {
@@ -2562,19 +2562,36 @@ Example output:
2562
2562
  {"steps":[{"icon":"🔍","agent":"WebSearchAgent","label":"Cerca notizie","prompt":"Cerca le ultime notizie su intelligenza artificiale oggi"},{"icon":"📰","agent":"HERALD","label":"Analisi notizie","prompt":"Analizza le notizie trovate e crea un briefing esecutivo"},{"icon":"📊","agent":"CanvasAgent","label":"Dashboard HTML","prompt":"Crea una dashboard HTML visuale con i risultati"}]}`;
2563
2563
 
2564
2564
  try {
2565
- const planRaw = await callLLM(config, 'You are a JSON workflow planner. Respond only with valid JSON, no markdown, no explanation.', planPrompt, { max_tokens: 1200 });
2565
+ // Force thinking OFF for planner we need deterministic JSON, not reasoning chains
2566
+ const planConfig = Object.assign({}, config, { thinking: 'off' });
2567
+ const planRaw = await callLLM(planConfig, 'You are a JSON workflow planner. Output ONLY valid JSON. No thinking, no explanation, no markdown.', planPrompt, { max_tokens: 1500 });
2568
+ process.stderr.write('[STUDIO PLAN RAW] ' + planRaw.slice(0, 400) + '\n');
2566
2569
  let steps;
2567
2570
  try {
2568
- // Strip <think>...</think> blocks and markdown fences before parsing
2569
- let clean = planRaw.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
2570
- clean = clean.replace(/^```[\w]*\n?/,'').replace(/\n?```$/,'').trim();
2571
+ // Strip ALL <think>...</think> blocks (greedy handles nested/multiple)
2572
+ let clean = planRaw;
2573
+ let prev = '';
2574
+ while (prev !== clean) { prev = clean; clean = clean.replace(/<think>[\s\S]*?<\/think>/g, ''); }
2575
+ clean = clean.trim();
2576
+ // Strip markdown fences
2577
+ clean = clean.replace(/^```[\w]*\r?\n?/,'').replace(/\r?\n?```$/,'').trim();
2578
+ // Extract first complete JSON object
2571
2579
  const jsonMatch = clean.match(/\{[\s\S]*\}/);
2572
2580
  const parsed = JSON.parse(jsonMatch ? jsonMatch[0] : clean);
2573
2581
  steps = parsed.steps;
2574
- } catch {
2575
- sendJSON(res, 500, { error: 'Failed to parse workflow plan' });
2576
- logRequest(method, pathname, 500, Date.now() - start);
2577
- return;
2582
+ } catch (parseErr) {
2583
+ process.stderr.write('[STUDIO PLAN PARSE ERR] ' + parseErr.message + '\n');
2584
+ // Fallback: build a sensible default plan from the task keywords
2585
+ const hasEmail = /email|mail/i.test(task);
2586
+ const hasCalendar = /calendar|agenda|calendari/i.test(task);
2587
+ const hasSearch = /cerca|search|notizie|news/i.test(task);
2588
+ const hasCanvas = /html|dashboard|visua|report/i.test(task);
2589
+ steps = [];
2590
+ if (hasEmail) steps.push({icon:'📧',agent:'EmailAgent',label:'Controlla email',prompt:task});
2591
+ if (hasCalendar) steps.push({icon:'📅',agent:'CalendarAgent',label:'Rivedi calendario',prompt:task});
2592
+ if (hasSearch || steps.length === 0) steps.push({icon:'🔍',agent:'WebSearchAgent',label:'Ricerca web',prompt:task});
2593
+ steps.push({icon:'📰',agent:'HERALD',label:'Analisi e briefing',prompt:task});
2594
+ if (hasCanvas) steps.push({icon:'📊',agent:'CanvasAgent',label:'Dashboard HTML',prompt:task});
2578
2595
  }
2579
2596
  if (!Array.isArray(steps) || !steps.length) {
2580
2597
  sendJSON(res, 500, { error: 'Empty workflow plan' });
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 = '13.2.25';
8
+ export const VERSION = '13.2.27';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -2749,6 +2749,104 @@ function stopVoiceInput() {
2749
2749
  }
2750
2750
 
2751
2751
  // ---- STUDIO ----
2752
+ // ── i18n — UI string translations ────────────────────────────────────────────
2753
+ var I18N = {
2754
+ en: {
2755
+ chat:'Chat', studio:'Studio', settings:'Settings', agents:'Agents',
2756
+ run:'▶ Run', stop:'⬛ Stop', reset:'New workflow',
2757
+ placeholder_chat:'Message NHA... (Enter to send)',
2758
+ placeholder_studio:'Describe what you want to accomplish... (Ctrl+Enter to run)',
2759
+ planning:'Planning...', workflow_complete:'Workflow complete.',
2760
+ workflow_stopped:'Workflow stopped by user.',
2761
+ canvas_open:'Open Canvas', canvas_generated:'HTML Dashboard generated in Canvas panel.',
2762
+ saved:'Saved!', lang_set:'Language set to',
2763
+ agents_respond:'All agents will respond in',
2764
+ examples:'Examples', recent_sessions:'Recent sessions',
2765
+ restore:'Restore', delete:'Delete',
2766
+ send:'Send', attach:'Attach',
2767
+ settings_save:'Save',
2768
+ no_output:'(no output)', done:'(done)',
2769
+ token_label:'Tokens',
2770
+ },
2771
+ it: {
2772
+ chat:'Chat', studio:'Studio', settings:'Impostazioni', agents:'Agenti',
2773
+ run:'▶ Avvia', stop:'⬛ Ferma', reset:'Nuovo workflow',
2774
+ placeholder_chat:'Scrivi a NHA... (Invio per inviare)',
2775
+ placeholder_studio:'Descrivi cosa vuoi fare... (Ctrl+Invio per avviare)',
2776
+ planning:'Pianificazione...', workflow_complete:'Workflow completato.',
2777
+ workflow_stopped:'Workflow fermato dall\u2019utente.',
2778
+ canvas_open:'Apri Canvas', canvas_generated:'Dashboard HTML generata nel pannello Canvas.',
2779
+ saved:'Salvato!', lang_set:'Lingua impostata su',
2780
+ agents_respond:'Tutti gli agenti risponderanno in',
2781
+ examples:'Esempi', recent_sessions:'Sessioni recenti',
2782
+ restore:'Ripristina', delete:'Elimina',
2783
+ send:'Invia', attach:'Allega',
2784
+ settings_save:'Salva',
2785
+ no_output:'(nessun output)', done:'(completato)',
2786
+ token_label:'Token',
2787
+ },
2788
+ es: {
2789
+ chat:'Chat', studio:'Studio', settings:'Configuración', agents:'Agentes',
2790
+ run:'▶ Ejecutar', stop:'⬛ Detener', reset:'Nuevo flujo',
2791
+ placeholder_chat:'Mensaje a NHA... (Enter para enviar)',
2792
+ placeholder_studio:'Describe lo que quieres hacer... (Ctrl+Enter para ejecutar)',
2793
+ planning:'Planificando...', workflow_complete:'Flujo completado.',
2794
+ workflow_stopped:'Flujo detenido por el usuario.',
2795
+ canvas_open:'Abrir Canvas', canvas_generated:'Panel HTML generado en Canvas.',
2796
+ saved:'¡Guardado!', lang_set:'Idioma establecido en',
2797
+ agents_respond:'Todos los agentes responderán en',
2798
+ examples:'Ejemplos', recent_sessions:'Sesiones recientes',
2799
+ restore:'Restaurar', delete:'Eliminar',
2800
+ send:'Enviar', attach:'Adjuntar',
2801
+ settings_save:'Guardar',
2802
+ no_output:'(sin salida)', done:'(hecho)',
2803
+ token_label:'Tokens',
2804
+ },
2805
+ fr: {
2806
+ chat:'Chat', studio:'Studio', settings:'Paramètres', agents:'Agents',
2807
+ run:'▶ Lancer', stop:'⬛ Arrêter', reset:'Nouveau flux',
2808
+ placeholder_chat:'Message à NHA... (Entrée pour envoyer)',
2809
+ placeholder_studio:'Décrivez ce que vous voulez faire... (Ctrl+Entrée pour lancer)',
2810
+ planning:'Planification...', workflow_complete:'Flux terminé.',
2811
+ workflow_stopped:'Flux arr\u00eat\u00e9 par l\u2019utilisateur.',
2812
+ canvas_open:'Ouvrir Canvas', canvas_generated:'Tableau de bord HTML généré dans Canvas.',
2813
+ saved:'Sauvegardé!', lang_set:'Langue définie sur',
2814
+ agents_respond:'Tous les agents répondront en',
2815
+ examples:'Exemples', recent_sessions:'Sessions récentes',
2816
+ restore:'Restaurer', delete:'Supprimer',
2817
+ send:'Envoyer', attach:'Joindre',
2818
+ settings_save:'Sauvegarder',
2819
+ no_output:'(aucune sortie)', done:'(terminé)',
2820
+ token_label:'Tokens',
2821
+ },
2822
+ de: {
2823
+ chat:'Chat', studio:'Studio', settings:'Einstellungen', agents:'Agenten',
2824
+ run:'▶ Starten', stop:'⬛ Stopp', reset:'Neuer Workflow',
2825
+ placeholder_chat:'Nachricht an NHA... (Enter zum Senden)',
2826
+ placeholder_studio:'Beschreibe was du tun möchtest... (Strg+Enter zum Starten)',
2827
+ planning:'Planung...', workflow_complete:'Workflow abgeschlossen.',
2828
+ workflow_stopped:'Workflow vom Benutzer gestoppt.',
2829
+ canvas_open:'Canvas öffnen', canvas_generated:'HTML-Dashboard im Canvas-Panel generiert.',
2830
+ saved:'Gespeichert!', lang_set:'Sprache auf',
2831
+ agents_respond:'Alle Agenten antworten auf',
2832
+ examples:'Beispiele', recent_sessions:'Letzte Sitzungen',
2833
+ restore:'Wiederherstellen', delete:'Löschen',
2834
+ send:'Senden', attach:'Anhängen',
2835
+ settings_save:'Speichern',
2836
+ no_output:'(keine Ausgabe)', done:'(erledigt)',
2837
+ token_label:'Token',
2838
+ },
2839
+ };
2840
+ // Fallback to 'en' for unmapped languages
2841
+ function t(key) {
2842
+ try {
2843
+ var cfg = JSON.parse(localStorage.getItem('nha_config_cache') || '{}');
2844
+ var lang = (cfg.lang || 'it').slice(0,2);
2845
+ var map = I18N[lang] || I18N.en;
2846
+ return map[key] || I18N.en[key] || key;
2847
+ } catch(e) { return I18N.en[key] || key; }
2848
+ }
2849
+
2752
2850
  var studioState = {
2753
2851
  task: '',
2754
2852
  nodes: [], // [{icon,agent,label,status:'waiting'|'running'|'done'|'error'}]
@@ -2765,10 +2863,10 @@ function stopStudio() {
2765
2863
  if (studioAbortController) { try { studioAbortController.abort(); } catch(e) {} studioAbortController = null; }
2766
2864
  studioState.running = false;
2767
2865
  var btn = document.getElementById('studioRunBtn');
2768
- if (btn) { btn.disabled = false; btn.textContent = '▶ Run'; }
2866
+ if (btn) { btn.disabled = false; btn.textContent = t('run'); }
2769
2867
  var stopBtn = document.getElementById('studioStopBtn');
2770
2868
  if (stopBtn) stopBtn.style.display = 'none';
2771
- studioLog('Studio', '⬛', 'Workflow stopped by user.', 'system');
2869
+ studioLog('Studio', '⬛', t('workflow_stopped'), 'system');
2772
2870
  // Mark any still-running nodes as error
2773
2871
  studioState.nodes.forEach(function(n) { if (n.status === 'running') n.status = 'error'; });
2774
2872
  renderStudioNodes();
@@ -2875,7 +2973,7 @@ function renderStudioResult() {
2875
2973
  el.style.display = 'block';
2876
2974
  var isHtml = studioState.result.trimStart().startsWith('<');
2877
2975
  var body = isHtml
2878
- ? '<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap"><span style="color:var(--dim);font-size:13px">&#10003; Dashboard HTML generata nel pannello Canvas.</span><button onclick="openCanvasPanel()" style="padding:6px 14px;background:var(--greendim);border:1px solid var(--green3);border-radius:8px;color:var(--green);font-size:12px;cursor:pointer;font-weight:700">&#x25A3; Apri Canvas</button></div>'
2976
+ ? '<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap"><span style="color:var(--dim);font-size:13px">&#10003; ' + t('canvas_generated') + '</span><button onclick="openCanvasPanel()" style="padding:6px 14px;background:var(--greendim);border:1px solid var(--green3);border-radius:8px;color:var(--green);font-size:12px;cursor:pointer;font-weight:700">&#x25A3; ' + t('canvas_open') + '</button></div>'
2879
2977
  : '<div class="md-body">' + renderMd(studioState.result) + '</div>';
2880
2978
  el.innerHTML = '<div class="studio-result__title">&#10003; Workflow completato</div>' + body;
2881
2979
  }
@@ -2906,7 +3004,7 @@ async function runStudio() {
2906
3004
  studioAbortController = new AbortController();
2907
3005
 
2908
3006
  var btn = document.getElementById('studioRunBtn');
2909
- if (btn) { btn.disabled = true; btn.textContent = 'Planning...'; }
3007
+ if (btn) { btn.disabled = true; btn.textContent = t('planning'); }
2910
3008
  var stopBtn = document.getElementById('studioStopBtn');
2911
3009
  if (stopBtn) stopBtn.style.display = '';
2912
3010
 
@@ -2969,7 +3067,7 @@ async function runStudio() {
2969
3067
  // Final result is the last step's output
2970
3068
  studioState.result = context;
2971
3069
  renderStudioResult();
2972
- studioLog('Studio', '&#127881;', 'Workflow complete.', 'system');
3070
+ studioLog('Studio', '&#127881;', t('workflow_complete'), 'system');
2973
3071
 
2974
3072
  // Save session to localStorage for reuse in Chat
2975
3073
  saveStudioSession(task, studioState.nodes, studioState.log, context);
@@ -3015,7 +3113,7 @@ function renderStudioSessionsBar() {
3015
3113
  var sessions = loadStudioSessions();
3016
3114
  if (!sessions.length) { el.style.display = 'none'; return; }
3017
3115
  el.style.display = 'block';
3018
- el.innerHTML = '<div style="font-size:10px;color:var(--dim);margin-bottom:8px;text-transform:uppercase;letter-spacing:1px">Recent sessions</div>' +
3116
+ el.innerHTML = '<div style="font-size:10px;color:var(--dim);margin-bottom:8px;text-transform:uppercase;letter-spacing:1px">' + t('recent_sessions') + '</div>' +
3019
3117
  '<div style="max-height:220px;overflow-y:auto;padding-right:4px">' +
3020
3118
  sessions.map(function(s,i) {
3021
3119
  return '<div class="studio-session-item">' +
@@ -3262,15 +3360,15 @@ function renderStudio(el) {
3262
3360
  // ── AUTO MODE ──
3263
3361
  '<div id="studioAutoMode">' +
3264
3362
  '<div style="margin-bottom:10px">' +
3265
- '<div style="font-size:10px;color:var(--dim);margin-bottom:6px;text-transform:uppercase;letter-spacing:1px">Examples</div>' +
3363
+ '<div style="font-size:10px;color:var(--dim);margin-bottom:6px;text-transform:uppercase;letter-spacing:1px">' + t('examples') + '</div>' +
3266
3364
  examplesHtml +
3267
3365
  '</div>' +
3268
3366
  '<div class="studio-input-row">' +
3269
- '<textarea id="studioTaskInput" placeholder="Describe what you want to accomplish... (Ctrl+Enter to run)" onkeydown="if(event.key===\\x27Enter\\x27&&(event.ctrlKey||event.metaKey)){runStudio();event.preventDefault()}">' + esc(studioState.task) + '</textarea>' +
3367
+ '<textarea id="studioTaskInput" placeholder="' + t('placeholder_studio') + '" onkeydown="if(event.key===\\x27Enter\\x27&&(event.ctrlKey||event.metaKey)){runStudio();event.preventDefault()}">' + esc(studioState.task) + '</textarea>' +
3270
3368
  '<div style="display:flex;gap:6px">' +
3271
- '<button id="studioRunBtn" class="studio-run-btn" onclick="runStudio()" style="flex:1" ' + (studioState.running ? 'disabled' : '') + '>&#9654; Run</button>' +
3272
- '<button id="studioStopBtn" onclick="stopStudio()" title="Stop workflow" style="padding:8px 14px;background:#7f1d1d;border:1px solid #ef4444;border-radius:8px;color:#ef4444;cursor:pointer;font-size:13px;font-weight:700;white-space:nowrap;' + (studioState.running ? '' : 'display:none') + '">&#9632; Stop</button>' +
3273
- '<button onclick="studioReset()" title="New workflow" style="padding:8px 12px;background:none;border:1px solid var(--border);border-radius:8px;color:var(--dim);cursor:pointer;font-size:16px;line-height:1" ' + (studioState.running ? 'disabled' : '') + '>&#8635;</button>' +
3369
+ '<button id="studioRunBtn" class="studio-run-btn" onclick="runStudio()" style="flex:1" ' + (studioState.running ? 'disabled' : '') + '>' + t('run') + '</button>' +
3370
+ '<button id="studioStopBtn" onclick="stopStudio()" title="' + t('stop') + '" style="padding:8px 14px;background:#7f1d1d;border:1px solid #ef4444;border-radius:8px;color:#ef4444;cursor:pointer;font-size:13px;font-weight:700;white-space:nowrap;' + (studioState.running ? '' : 'display:none') + '">&#9632; ' + t('stop') + '</button>' +
3371
+ '<button onclick="studioReset()" title="' + t('reset') + '" style="padding:8px 12px;background:none;border:1px solid var(--border);border-radius:8px;color:var(--dim);cursor:pointer;font-size:16px;line-height:1" ' + (studioState.running ? 'disabled' : '') + '>&#8635;</button>' +
3274
3372
  '</div>' +
3275
3373
  '</div>' +
3276
3374
  '</div>' +