nothumanallowed 13.2.40 → 13.2.42

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.40",
3
+ "version": "13.2.42",
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": {
@@ -2546,25 +2546,30 @@ export async function cmdUI(args) {
2546
2546
  const plannerLang = (() => { const LANG_MAP2 = {en:'English',it:'Italian',es:'Spanish',fr:'French',de:'German',pt:'Portuguese',zh:'Chinese',ja:'Japanese',ar:'Arabic',hi:'Hindi',ru:'Russian',nl:'Dutch',pl:'Polish',tr:'Turkish',ko:'Korean'}; const lc = (config?.language||'it').slice(0,2); return LANG_MAP2[lc]||'Italian'; })();
2547
2547
 
2548
2548
  // ── Fast keyword-based planning (no LLM call needed for common patterns) ──────
2549
- // Detect task intent from keywords — avoids sending the full task through SENTINEL
2550
2549
  const taskLow = task.toLowerCase();
2551
- const hasEmail = /email|mail|inbox|posta/i.test(taskLow);
2552
- const hasCalendar = /calendar|agenda|calendari|eventi|schedule/i.test(taskLow);
2553
- const hasSearch = /cerca|search|notizie|news|ultime|latest|web|internet/i.test(taskLow);
2554
- const hasCanvas = /html|dashboard|visua|report|grafico|chart/i.test(taskLow);
2555
- const hasGitHub = /github|git|issue|pr|pull request/i.test(taskLow);
2556
- const hasSlack = /slack|channel|messag/i.test(taskLow);
2557
- const hasNotion = /notion|note|page/i.test(taskLow);
2558
- const hasBriefing = /briefing|analisi|analizza|summary|sommario|riassunto|riepiloga/i.test(taskLow);
2559
- const hasFinance = /finance|mercato|market|stock|trading|finanz/i.test(taskLow);
2560
- const hasSecurity = /security|sicurezza|vulnerabilit|audit|pentest/i.test(taskLow);
2550
+ const hasEmail = /email|mail|inbox|posta/i.test(taskLow);
2551
+ const hasCalendar = /calendar|agenda|calendari|eventi|schedule/i.test(taskLow);
2552
+ const hasSearch = /cerca|search|notizie|news|ultime|latest|web|internet|tendenz|trend/i.test(taskLow);
2553
+ const hasCanvas = /html|dashboard|visua|report|grafico|chart/i.test(taskLow);
2554
+ const hasGitHub = /github|git|issue|pr|pull request/i.test(taskLow);
2555
+ const hasSlack = /slack|channel|messag/i.test(taskLow);
2556
+ const hasNotion = /notion|note|page/i.test(taskLow);
2557
+ const hasBriefing = /briefing|analisi|analizza|summary|sommario|riassunto|riepiloga|valutazione|valuta/i.test(taskLow);
2558
+ const hasFinance = /finance|mercato|market|stock|trading|finanz|investiment|cripto/i.test(taskLow);
2559
+ const hasSecurity = /security|sicurezza|vulnerabilit|audit|pentest|rischi|dipendenz/i.test(taskLow);
2560
+ const hasStrategy = /strateg|competitiv|posizionament|raccomandaz|competitive|positioning/i.test(taskLow);
2561
+ const hasReputation = /reputazion|reputation|online|brand|review|recension/i.test(taskLow);
2562
+ const hasCode = /codice|code|refactor|debug|bug|sviluppo|software|npm|package/i.test(taskLow);
2563
+ const hasWriting = /scrivi|write|articolo|article|blog|testo|text|documento|document/i.test(taskLow);
2564
+ const hasData = /dati|data|dataset|csv|json|analizza i dati|pattern|statistich/i.test(taskLow);
2565
+ const hasTranslate = /traduci|translate|traduzione|translation/i.test(taskLow);
2566
+
2567
+ const it = plannerLang === 'Italian';
2561
2568
 
2562
2569
  // Extract a clean short search query from the task (to avoid SENTINEL flagging long task strings)
2563
2570
  const extractSearchQuery = (t) => {
2564
- // Try to find an explicit search topic after keywords like "cerca", "search", "notizie su"
2565
- const m = t.match(/(?:cerca|search|find|ricerca|notizie su|news about|latest on|aggiornamenti su|ultime su)\s+(.{5,80}?)(?:\s+(?:e |and |per |for |poi |then )|[,\n]|$)/i);
2571
+ const m = t.match(/(?:cerca|search|find|ricerca|notizie su|news about|latest on|aggiornamenti su|ultime su|tendenz|trend)\s+(.{5,80}?)(?:\s+(?:e |and |per |for |poi |then )|[,\n]|$)/i);
2566
2572
  if (m) return m[1].trim();
2567
- // Strip leading instruction prefix (everything before the first colon)
2568
2573
  const stripped = t.replace(/^[^:]+:\s*/,'').split(/[,\n]/)[0].slice(0,100).trim();
2569
2574
  return stripped || t.slice(0,80).trim();
2570
2575
  };
@@ -2573,29 +2578,35 @@ export async function cmdUI(args) {
2573
2578
  // Build plan directly from keywords — reliable, fast, no SENTINEL risk
2574
2579
  const buildKeywordPlan = () => {
2575
2580
  const steps = [];
2576
- if (hasEmail) steps.push({icon:'\u{1F4E7}',agent:'EmailAgent',label:plannerLang==='Italian'?'Controlla email':'Check emails',prompt:'Read the latest unread emails and identify urgent items, deadlines, and required actions'});
2577
- if (hasCalendar) steps.push({icon:'\u{1F4C5}',agent:'CalendarAgent',label:plannerLang==='Italian'?'Rivedi calendario':'Review calendar',prompt:'Check today\'s events and identify any scheduling conflicts or important meetings'});
2578
- if (hasGitHub) steps.push({icon:'\u{1F4BB}',agent:'GitHubAgent',label:'GitHub',prompt:'Read open issues and pull requests, identify what needs attention'});
2579
- if (hasSlack) steps.push({icon:'\u{1F4AC}',agent:'SlackAgent',label:'Slack',prompt:'Check recent Slack messages and identify important conversations'});
2580
- if (hasNotion) steps.push({icon:'\u{1F4DD}',agent:'NotionAgent',label:'Notion',prompt:'Search Notion for relevant pages and notes'});
2581
- if (hasSearch || (!hasEmail && !hasCalendar && !hasGitHub && !hasSlack)) {
2582
- steps.push({icon:'\u{1F50D}',agent:'WebSearchAgent',label:plannerLang==='Italian'?'Ricerca web':'Web search',prompt:searchQuery});
2581
+ if (hasEmail) steps.push({icon:'\u{1F4E7}',agent:'EmailAgent', label:it?'Controlla email':'Check emails', prompt:'Read the latest unread emails and identify urgent items, deadlines, and required actions'});
2582
+ if (hasCalendar) steps.push({icon:'\u{1F4C5}',agent:'CalendarAgent', label:it?'Rivedi calendario':'Review calendar', prompt:'Check today\'s events and identify any scheduling conflicts or important meetings'});
2583
+ if (hasGitHub) steps.push({icon:'\u{1F4BB}',agent:'GitHubAgent', label:'GitHub', prompt:'Read open issues and pull requests, identify what needs attention'});
2584
+ if (hasSlack) steps.push({icon:'\u{1F4AC}',agent:'SlackAgent', label:'Slack', prompt:'Check recent Slack messages and identify important conversations'});
2585
+ if (hasNotion) steps.push({icon:'\u{1F4DD}',agent:'NotionAgent', label:'Notion', prompt:'Search Notion for relevant pages and notes'});
2586
+ if (hasSearch || hasReputation || (!hasEmail && !hasCalendar && !hasGitHub && !hasSlack)) {
2587
+ steps.push({icon:'\u{1F50D}',agent:'WebSearchAgent',label:it?'Ricerca web':'Web search',prompt:searchQuery});
2583
2588
  }
2584
- if (hasSecurity) {
2585
- steps.push({icon:'\u{1F6E1}',agent:'cassandra',label:plannerLang==='Italian'?'Analisi rischi':'Risk analysis',prompt:'Analyze the data and identify security risks and recommendations'});
2586
- } else if (hasFinance) {
2587
- steps.push({icon:'\u{1F4B0}',agent:'mercury',label:plannerLang==='Italian'?'Analisi finanziaria':'Financial analysis',prompt:'Analyze the financial data and market information'});
2588
- } else if (hasBriefing || steps.length > 0) {
2589
- steps.push({icon:'\u{1F4F0}',agent:'HERALD',label:plannerLang==='Italian'?'Analisi e briefing':'Analysis & briefing',prompt:'Based on ALL the data collected by the previous steps, write a complete executive briefing with priorities, findings, and strategic recommendations. Do NOT invent data — only use what was provided.'});
2589
+ // Specialist agents — can stack multiple
2590
+ if (hasSecurity) steps.push({icon:'\u{1F6E1}',agent:'cassandra', label:it?'CASSANDRA \u2014 Rischi sicurezza':'CASSANDRA \u2014 Security risks', prompt:'Analyze the collected data and identify security risks, vulnerabilities and concrete recommendations'});
2591
+ if (hasFinance) steps.push({icon:'\u{1F4B0}',agent:'mercury', label:it?'MERCURY \u2014 Analisi mercato':'MERCURY \u2014 Market analysis', prompt:'Analyze the financial data and market trends from the collected information'});
2592
+ if (hasStrategy) steps.push({icon:'\u{265F}', agent:'athena', label:it?'ATHENA \u2014 Strategia':'ATHENA \u2014 Strategy', prompt:'Based on the collected data, produce strategic analysis with competitive positioning and concrete recommendations'});
2593
+ if (hasReputation) steps.push({icon:'\u{1F52D}',agent:'oracle', label:it?'ORACLE \u2014 Reputazione':'ORACLE \u2014 Reputation', prompt:'Analyze the online reputation data, sentiment and brand positioning from the collected information'});
2594
+ if (hasCode) steps.push({icon:'\u{1F527}',agent:'forge', label:it?'FORGE \u2014 Analisi codice':'FORGE \u2014 Code analysis', prompt:'Analyze the code, dependencies and technical issues identified in the data'});
2595
+ if (hasWriting) steps.push({icon:'\u{1F58A}',agent:'quill', label:it?'QUILL \u2014 Redazione':'QUILL \u2014 Writing', prompt:'Write a polished, professional document based on all the collected information'});
2596
+ if (hasData) steps.push({icon:'\u{1F4CA}',agent:'DataAnalystAgent',label:it?'Analisi dati':'Data analysis', prompt:'Analyze the data and extract key patterns, trends and insights'});
2597
+ if (hasTranslate) steps.push({icon:'\u{1F310}',agent:'polyglot', label:it?'POLYGLOT \u2014 Traduzione':'POLYGLOT \u2014 Translation', prompt:'Translate the content as requested, maintaining meaning and style'});
2598
+ // If no specialist added but we have data, add HERALD for synthesis
2599
+ const hasSpecialist = hasSecurity || hasFinance || hasStrategy || hasReputation || hasCode || hasWriting || hasData || hasTranslate;
2600
+ if (!hasSpecialist && (hasBriefing || steps.length > 0)) {
2601
+ steps.push({icon:'\u{1F4F0}',agent:'HERALD',label:it?'HERALD \u2014 Briefing esecutivo':'HERALD \u2014 Executive briefing',prompt:'Based on ALL the data collected by the previous steps, write a complete executive briefing with priorities, findings, and strategic recommendations. Do NOT invent data — only use what was provided.'});
2590
2602
  }
2591
- if (hasCanvas) steps.push({icon:'\u{1F4CA}',agent:'CanvasAgent',label:plannerLang==='Italian'?'Dashboard HTML':'HTML Dashboard',prompt:'Create a professional HTML dashboard report with the briefing data'});
2603
+ if (hasCanvas) steps.push({icon:'\u{1F4CA}',agent:'CanvasAgent',label:it?'Dashboard HTML':'HTML Dashboard',prompt:'Create a professional HTML dashboard report with the briefing data'});
2592
2604
  return steps;
2593
2605
  };
2594
2606
 
2595
- // Use keyword plan directly if it covers the task well enough
2596
- // Only fall back to LLM planning for genuinely ambiguous tasks
2607
+ // Use keyword plan directly only fall back to LLM for genuinely ambiguous tasks
2597
2608
  const keywordSteps = buildKeywordPlan();
2598
- const taskIsComplex = !hasEmail && !hasCalendar && !hasSearch && !hasGitHub && !hasSlack && !hasBriefing && keywordSteps.length <= 1;
2609
+ const taskIsComplex = !hasEmail && !hasCalendar && !hasSearch && !hasGitHub && !hasSlack && !hasBriefing && !hasStrategy && !hasReputation && !hasCode && !hasWriting && !hasData && keywordSteps.length <= 1;
2599
2610
 
2600
2611
  try {
2601
2612
  let steps;
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.40';
8
+ export const VERSION = '13.2.42';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -521,7 +521,7 @@ export async function callLLMStream(config, systemPrompt, userMessage, onToken,
521
521
  { role: 'system', content: sanitize(systemPrompt) },
522
522
  { role: 'user', content: sanitize(userMessage) },
523
523
  ],
524
- stream: true,
524
+ stream: false,
525
525
  chat_template_kwargs: { enable_thinking: thinkingEnabled },
526
526
  };
527
527
  const nhaRes = await fetch('https://nothumanallowed.com/api/v1/liara/chat', {
@@ -533,10 +533,13 @@ export async function callLLMStream(config, systemPrompt, userMessage, onToken,
533
533
  const err = await nhaRes.text();
534
534
  throw new Error(`NHA Free ${nhaRes.status}: ${err}`);
535
535
  }
536
- // Node.js native fetch ReadableStream closes after first TCP buffer for SSE.
537
- // Use res.text() to get the full response, then parse SSE lines synchronously.
538
- const rawText = await nhaRes.text();
539
- return parseSSEText(rawText, 'openai', onToken);
536
+ // Non-streaming: vLLM returns complete text no BPE subword splitting issues
537
+ const nhaJson = await nhaRes.json();
538
+ let fullNhaText = nhaJson.choices?.[0]?.message?.content || '';
539
+ // Strip <think>...</think> blocks
540
+ fullNhaText = fullNhaText.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
541
+ if (onToken) onToken(fullNhaText);
542
+ return fullNhaText;
540
543
  }
541
544
 
542
545
  const format = provider === 'anthropic' ? 'anthropic' : 'openai';
@@ -647,6 +650,8 @@ function parseSSEText(text, format, onToken) {
647
650
  let fullText = '';
648
651
  let thinkBuf = '';
649
652
  let inThink = false;
653
+ let isHtmlOutput = false;
654
+ let chunkCount = 0;
650
655
 
651
656
  for (const line of text.split('\n')) {
652
657
  if (!line.startsWith('data: ')) continue;
@@ -680,10 +685,18 @@ function parseSSEText(text, format, onToken) {
680
685
  }
681
686
  }
682
687
  if (out) {
683
- const insideTag = fullText.lastIndexOf('<') > fullText.lastIndexOf('>');
684
- if (!insideTag) out = fixQwen3Markdown(out);
685
- if (fullText && out && !insideTag && !/[\s\n]$/.test(fullText) && !/^[\s\n.,;:!?)\]}'">]/.test(out)) {
686
- out = ' ' + out;
688
+ chunkCount++;
689
+ if (chunkCount <= 3) process.stderr.write(`[QWEN3 CHUNK ${chunkCount}] len=${out.length} repr=${JSON.stringify(out.slice(0,60))}\n`);
690
+ // Detect HTML output on first meaningful token
691
+ if (!isHtmlOutput && (out.includes('<div') || out.includes('<!DOCTYPE') || out.includes('<html'))) {
692
+ isHtmlOutput = true;
693
+ }
694
+ if (!isHtmlOutput) {
695
+ out = fixQwen3Markdown(out);
696
+ const insideTag = fullText.lastIndexOf('<') > fullText.lastIndexOf('>');
697
+ if (fullText && out && !insideTag && !/[\s\n]$/.test(fullText) && !/^[\s\n.,;:!?)\]}'">]/.test(out)) {
698
+ out = ' ' + out;
699
+ }
687
700
  }
688
701
  fullText += out;
689
702
  if (onToken) onToken(out);
@@ -691,7 +704,7 @@ function parseSSEText(text, format, onToken) {
691
704
  }
692
705
  } catch {}
693
706
  }
694
-
707
+ process.stderr.write(`[QWEN3 TOTAL CHUNKS] ${chunkCount}, fullText len=${fullText.length}\n`);
695
708
  return fullText;
696
709
  }
697
710
 
@@ -703,6 +716,7 @@ async function streamSSEWithCallback(res, format, onToken) {
703
716
  let fullText = '';
704
717
  let thinkBuf = ''; // accumulates <think>...</think> content to suppress
705
718
  let inThink = false;
719
+ let isHtmlOutput = false;
706
720
 
707
721
  while (true) {
708
722
  const { done, value } = await reader.read();
@@ -748,10 +762,15 @@ async function streamSSEWithCallback(res, format, onToken) {
748
762
  }
749
763
  }
750
764
  if (out) {
751
- const insideTag2 = fullText.lastIndexOf('<') > fullText.lastIndexOf('>');
752
- if (!insideTag2) out = fixQwen3Markdown(out);
753
- if (fullText && out && !insideTag2 && !/[\s\n]$/.test(fullText) && !/^[\s\n.,;:!?)\]}'">]/.test(out)) {
754
- out = ' ' + out;
765
+ if (!isHtmlOutput && (out.includes('<div') || out.includes('<!DOCTYPE') || out.includes('<html'))) {
766
+ isHtmlOutput = true;
767
+ }
768
+ if (!isHtmlOutput) {
769
+ out = fixQwen3Markdown(out);
770
+ const insideTag2 = fullText.lastIndexOf('<') > fullText.lastIndexOf('>');
771
+ if (fullText && out && !insideTag2 && !/[\s\n]$/.test(fullText) && !/^[\s\n.,;:!?)\]}'">]/.test(out)) {
772
+ out = ' ' + out;
773
+ }
755
774
  }
756
775
  fullText += out;
757
776
  if (onToken) onToken(out);
@@ -2852,6 +2852,7 @@ var studioState = {
2852
2852
  nodes: [], // [{icon,agent,label,status:'waiting'|'running'|'done'|'error'}]
2853
2853
  log: [], // [{agent,icon,text,time,type:'agent'|'system'|'error'}]
2854
2854
  result: '',
2855
+ canvas: null, // HTML canvas content if generated
2855
2856
  running: false,
2856
2857
  planned: false
2857
2858
  };
@@ -2971,14 +2972,14 @@ function renderStudioResult() {
2971
2972
  if (!el) return;
2972
2973
  if (!studioState.result) { el.style.display = 'none'; return; }
2973
2974
  el.style.display = 'block';
2974
- var isHtml = studioState.result.trimStart().startsWith('<');
2975
- var body = isHtml
2975
+ var hasCanvas = !!(studioState.canvas);
2976
+ var body = hasCanvas
2976
2977
  ? '<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>'
2977
2978
  : '<div class="md-body">' + renderMd(studioState.result) + '</div>';
2978
2979
  el.innerHTML = '<div class="studio-result__title">&#10003; ' + t('workflow_complete') + '</div>' + body;
2979
- // Show/hide persistent canvas button in toolbar
2980
+ // Show canvas button in toolbar whenever canvas data exists
2980
2981
  var canvasBtn = document.getElementById('studioCanvasBtn');
2981
- if (canvasBtn) canvasBtn.style.display = isHtml ? '' : 'none';
2982
+ if (canvasBtn) canvasBtn.style.display = hasCanvas ? '' : 'none';
2982
2983
  }
2983
2984
 
2984
2985
  function studioSetNodeStatus(idx, status) {
@@ -2998,6 +2999,7 @@ async function runStudio() {
2998
2999
  studioState.nodes = [];
2999
3000
  studioState.log = [];
3000
3001
  studioState.result = '';
3002
+ studioState.canvas = null;
3001
3003
  studioState.running = true;
3002
3004
  studioState.planned = false;
3003
3005
  renderStudioNodes();
@@ -3233,6 +3235,7 @@ function runStudioStep(idx, node, task, context, stepDef, signal) {
3233
3235
  }
3234
3236
  if (ev.canvas) {
3235
3237
  canvasHtml = ev.canvas;
3238
+ studioState.canvas = ev.canvas;
3236
3239
  // Render canvas immediately when received — don't wait for step resolution
3237
3240
  var cf2 = document.getElementById('canvasFrame');
3238
3241
  var cp2 = document.getElementById('canvasPanel');
@@ -3240,10 +3243,10 @@ function runStudioStep(idx, node, task, context, stepDef, signal) {
3240
3243
  cf2.srcdoc = canvasHtml;
3241
3244
  cp2.classList.add('open');
3242
3245
  var ct2 = document.getElementById('canvasTitle');
3243
- if (ct2) ct2.textContent = 'Studio Report';
3246
+ if (ct2) ct2.textContent = \x27Studio Report\x27;
3244
3247
  }
3245
3248
  var scb = document.getElementById('studioCanvasBtn');
3246
- if (scb) scb.style.display = '';
3249
+ if (scb) scb.style.display = \x27\x27;
3247
3250
  }
3248
3251
  if (ev.usage) { studioAddTokens(ev.usage.input||0, ev.usage.output||0); }
3249
3252
  else if (ev.token && !isStatus) { studioTokens.out += Math.ceil(ev.token.length/4); studioUpdateTokenBar(); }