nothumanallowed 13.2.99 → 13.3.1

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.99",
3
+ "version": "13.3.1",
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": {
@@ -2730,39 +2730,41 @@ export async function cmdUI(args) {
2730
2730
  // PDF attachment: always read document first to extract specs/data before any web search
2731
2731
  if (hasPdf) {
2732
2732
  const pdfName = body.pdfName || 'documento allegato';
2733
- steps.push({icon:'\u{1F4C4}',agent:'DocumentReaderAgent',label:it?'Leggi documento':'Read document',prompt:`Extract all technical specifications, model numbers, part codes, product names, manufacturer, dimensions, ratings, and any other key data from the attached document "${pdfName}". List every technical detail precisely.`});
2733
+ steps.push({icon:'\u{1F4C4}',agent:'DocumentReaderAgent',label:it?'Leggi documento':'Read document',reason:it?'Allegato PDF rilevato \u2014 estraggo dati tecnici prima di ogni altra operazione':'PDF attachment detected \u2014 extracting technical data first',prompt:`Extract all technical specifications, model numbers, part codes, product names, manufacturer, dimensions, ratings, and any other key data from the attached document "${pdfName}". List every technical detail precisely.`});
2734
2734
  }
2735
- 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'});
2736
- 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'});
2737
- if (hasGitHub) steps.push({icon:'\u{1F4BB}',agent:'GitHubAgent', label:'GitHub', prompt:'Read open issues and pull requests, identify what needs attention'});
2738
- if (hasSlack) steps.push({icon:'\u{1F4AC}',agent:'SlackAgent', label:'Slack', prompt:'Check recent Slack messages and identify important conversations'});
2739
- if (hasNotion) steps.push({icon:'\u{1F4DD}',agent:'NotionAgent', label:'Notion', prompt:'Search Notion for relevant pages and notes'});
2735
+ if (hasEmail) steps.push({icon:'\u{1F4E7}',agent:'EmailAgent', label:it?'Controlla email':'Check emails', reason:it?'Parola chiave email/mail/posta rilevata nel task':'Keyword email/mail detected in task', prompt:'Read the latest unread emails and identify urgent items, deadlines, and required actions'});
2736
+ if (hasCalendar) steps.push({icon:'\u{1F4C5}',agent:'CalendarAgent', label:it?'Rivedi calendario':'Review calendar', reason:it?'Parola chiave calendario/agenda/eventi rilevata nel task':'Keyword calendar/agenda/events detected in task', prompt:'Check today\'s events and identify any scheduling conflicts or important meetings'});
2737
+ if (hasGitHub) steps.push({icon:'\u{1F4BB}',agent:'GitHubAgent', label:'GitHub', reason:it?'Parola chiave GitHub/git/issue/PR rilevata nel task':'Keyword GitHub/git/issue/PR detected in task', prompt:'Read open issues and pull requests, identify what needs attention'});
2738
+ if (hasSlack) steps.push({icon:'\u{1F4AC}',agent:'SlackAgent', label:'Slack', reason:it?'Parola chiave Slack/canale/messaggio rilevata nel task':'Keyword Slack/channel/message detected in task', prompt:'Check recent Slack messages and identify important conversations'});
2739
+ if (hasNotion) steps.push({icon:'\u{1F4DD}',agent:'NotionAgent', label:'Notion', reason:it?'Parola chiave Notion/note rilevata nel task':'Keyword Notion/note detected in task', prompt:'Search Notion for relevant pages and notes'});
2740
2740
  // When PDF is present: always search web (to find where to buy, similar products etc.)
2741
2741
  // The search query will be refined at runtime using the extracted PDF specs as context
2742
2742
  if (hasPdf || hasSearch || hasReputation || (!hasEmail && !hasCalendar && !hasGitHub && !hasSlack)) {
2743
2743
  const searchPrompt = hasPdf
2744
2744
  ? (it ? 'Usando le specifiche tecniche estratte dal documento (codice prodotto, modello, costruttore, caratteristiche), cerca online dove acquistare il prodotto o articoli equivalenti. Usa i codici esatti dal documento come query di ricerca.' : 'Using the technical specifications extracted from the document (product code, model, manufacturer, specs), search online for where to buy this product or equivalent alternatives. Use exact codes from the document as search queries.')
2745
2745
  : searchQuery;
2746
- steps.push({icon:'\u{1F50D}',agent:'WebSearchAgent',label:it?'Ricerca web':'Web search',prompt:searchPrompt});
2746
+ const searchReason = hasPdf ? (it?'Complemento PDF: cerco informazioni online con i dati estratti':'PDF complement: searching online with extracted specs') : hasReputation ? (it?'Parola chiave reputazione/brand: raccolgo dati web':'Reputation/brand keyword: collecting web data') : hasSearch ? (it?'Parola chiave cerca/search/notizie rilevata':'Keyword search/news detected') : (it?'Nessuna fonte dati specifica \u2014 web come fonte primaria':'No specific data source \u2014 web as primary source');
2747
+ steps.push({icon:'\u{1F50D}',agent:'WebSearchAgent',label:it?'Ricerca web':'Web search',reason:searchReason,prompt:searchPrompt});
2747
2748
  }
2748
2749
  // Specialist agents — can stack multiple
2749
- 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'});
2750
- 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'});
2751
- 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'});
2752
- 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'});
2753
- 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'});
2754
- 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'});
2755
- 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'});
2756
- 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'});
2750
+ if (hasSecurity) steps.push({icon:'\u{1F6E1}',agent:'cassandra', label:it?'CASSANDRA \u2014 Rischi sicurezza':'CASSANDRA \u2014 Security risks', reason:it?'Parola chiave sicurezza/vulnerabilit\u00e0/audit rilevata':'Keyword security/vulnerability/audit detected', prompt:'Analyze the collected data and identify security risks, vulnerabilities and concrete recommendations'});
2751
+ if (hasFinance) steps.push({icon:'\u{1F4B0}',agent:'mercury', label:it?'MERCURY \u2014 Analisi mercato':'MERCURY \u2014 Market analysis', reason:it?'Parola chiave finanza/mercato/investimento rilevata':'Keyword finance/market/investment detected', prompt:'Analyze the financial data and market trends from the collected information'});
2752
+ if (hasStrategy) steps.push({icon:'\u{265F}', agent:'athena', label:it?'ATHENA \u2014 Strategia':'ATHENA \u2014 Strategy', reason:it?'Parola chiave strategia/competitivo/posizionamento rilevata':'Keyword strategy/competitive/positioning detected',prompt:'Based on the collected data, produce strategic analysis with competitive positioning and concrete recommendations'});
2753
+ if (hasReputation) steps.push({icon:'\u{1F52D}',agent:'oracle', label:it?'ORACLE \u2014 Reputazione':'ORACLE \u2014 Reputation', reason:it?'Parola chiave reputazione/brand/recensioni rilevata':'Keyword reputation/brand/reviews detected', prompt:'Analyze the online reputation data, sentiment and brand positioning from the collected information'});
2754
+ if (hasCode) steps.push({icon:'\u{1F527}',agent:'forge', label:it?'FORGE \u2014 Analisi codice':'FORGE \u2014 Code analysis', reason:it?'Parola chiave codice/refactor/bug/npm rilevata':'Keyword code/refactor/bug/npm detected', prompt:'Analyze the code, dependencies and technical issues identified in the data'});
2755
+ if (hasWriting) steps.push({icon:'\u{1F58A}',agent:'quill', label:it?'QUILL \u2014 Redazione':'QUILL \u2014 Writing', reason:it?'Parola chiave scrivi/articolo/documento rilevata':'Keyword write/article/document detected', prompt:'Write a polished, professional document based on all the collected information'});
2756
+ if (hasData) steps.push({icon:'\u{1F4CA}',agent:'DataAnalystAgent',label:it?'Analisi dati':'Data analysis', reason:it?'Parola chiave dati/dataset/statistiche rilevata':'Keyword data/dataset/statistics detected', prompt:'Analyze the data and extract key patterns, trends and insights'});
2757
+ if (hasTranslate) steps.push({icon:'\u{1F310}',agent:'polyglot', label:it?'POLYGLOT \u2014 Traduzione':'POLYGLOT \u2014 Translation', reason:it?'Parola chiave traduci/traduzione rilevata':'Keyword translate/translation detected', prompt:'Translate the content as requested, maintaining meaning and style'});
2757
2758
  // If no specialist added but we have data, add HERALD for synthesis
2758
2759
  const hasSpecialist = hasSecurity || hasFinance || hasStrategy || hasReputation || hasCode || hasWriting || hasData || hasTranslate;
2759
2760
  if (!hasSpecialist && (hasBriefing || steps.length > 0)) {
2760
- 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.'});
2761
+ steps.push({icon:'\u{1F4F0}',agent:'HERALD',label:it?'HERALD \u2014 Briefing esecutivo':'HERALD \u2014 Executive briefing',reason:it?'Nessun agente specialista \u2014 HERALD sintetizza tutti i dati raccolti':'No specialist agent \u2014 HERALD synthesizes all collected data',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.'});
2761
2762
  }
2762
2763
  // Add CanvasAgent: always when explicitly requested OR when 2+ specialist agents ran (complex analysis deserves a visual report)
2763
2764
  const specialistCount = [hasSecurity,hasFinance,hasStrategy,hasReputation,hasCode,hasWriting,hasData].filter(Boolean).length;
2764
2765
  if (hasCanvas || specialistCount >= 2 || (hasSpecialist && hasBriefing)) {
2765
- steps.push({icon:'\u{1F4CA}',agent:'CanvasAgent',label:it?'Dashboard HTML':'HTML Dashboard',prompt:'Create a professional HTML dashboard report summarizing all findings from the previous agents'});
2766
+ const canvasReason = hasCanvas ? (it?'Parola chiave html/dashboard/report visuale rilevata':'Keyword html/dashboard/visual report detected') : (it?specialistCount+' agenti specialisti \u2014 analisi complessa merita una dashboard':specialistCount+' specialist agents \u2014 complex analysis needs a visual dashboard');
2767
+ steps.push({icon:'\u{1F4CA}',agent:'CanvasAgent',label:it?'Dashboard HTML':'HTML Dashboard',reason:canvasReason,prompt:'Create a professional HTML dashboard report summarizing all findings from the previous agents'});
2766
2768
  }
2767
2769
  return steps;
2768
2770
  };
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.99';
8
+ export const VERSION = '13.3.1';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -342,8 +342,12 @@ export async function webSearch(query, maxResults = MAX_RESULTS) {
342
342
  function parseDuckDuckGoResults(html, maxResults) {
343
343
  const results = [];
344
344
 
345
- // Split on individual result divs. Each web result starts with this class sequence.
346
- const resultBlocks = html.split('class="result results_links');
345
+ // Split on result__body stable across DDG HTML layout changes.
346
+ // Fallback to legacy class="result results_links" if result__body not present.
347
+ const primarySplit = 'result__body">';
348
+ const fallbackSplit = 'class="result results_links';
349
+ const splitOn = html.includes(primarySplit) ? primarySplit : fallbackSplit;
350
+ const resultBlocks = html.split(splitOn);
347
351
 
348
352
  for (let i = 1; i < resultBlocks.length && results.length < maxResults; i++) {
349
353
  const block = resultBlocks[i];
@@ -192,9 +192,13 @@ function updateBrowserFrame(data){
192
192
  var f=document.getElementById('bvFrame');if(f)f.innerHTML='<img src="'+imgSrc+'" alt="Browser view">';
193
193
  // Save to per-conversation browser history for canvas Browser tab
194
194
  addBrowserPage(data.file||null,data.base64||null,data.url);
195
- // Update canvas browser tab live if open
195
+ // Update canvas browser tab live if open — show detail view of new page
196
196
  var p=document.getElementById('canvasPanel');
197
- if(p&&p.classList.contains('open')&&canvasMode==='browser'){renderCanvasPanel();}
197
+ if(p&&p.classList.contains('open')&&canvasMode==='browser'){
198
+ var dBr=getConvCanvasData();
199
+ browserViewIdx=dBr.browsers.length-1;
200
+ renderCanvasPanel();
201
+ }
198
202
  }
199
203
  function updateBrowserStatus(status){
200
204
  var s=document.getElementById('bvStatus');if(s)s.textContent=status;
@@ -524,6 +528,7 @@ var allCanvasData={}; // {convId: {canvases:[{html,title,ts}], browsers:[{base
524
528
  var canvasIdx=-1;
525
529
  var browserIdx=-1;
526
530
  var canvasMode='canvas';
531
+ var browserViewIdx=-1; // -1=gallery, >=0=detail page view
527
532
 
528
533
  function getConvCanvasData(){
529
534
  var id=activeConvId||'_default';
@@ -599,7 +604,11 @@ function renderCanvasPanel(){
599
604
  // Header title
600
605
  var t=document.getElementById('canvasTitle');
601
606
  if(t){
602
- if(canvasMode==='browser'){t.textContent=d.browsers.length>0?d.browsers.length+' pages visited':'No pages visited';}
607
+ if(canvasMode==='browser'&&browserViewIdx>=0&&d.browsers[browserViewIdx]){
608
+ var bvTitle=d.browsers[browserViewIdx].url||'Page';
609
+ try{var u=new URL(bvTitle);bvTitle=u.hostname+(u.pathname!=='/'?u.pathname:'');}catch(e){}
610
+ t.textContent=bvTitle;
611
+ } else if(canvasMode==='browser'){t.textContent=d.browsers.length>0?d.browsers.length+' pages visited':'No pages visited';}
603
612
  else if(!item){t.textContent='Empty canvas';}
604
613
  else{t.textContent=(item.title||'Canvas')+(d.canvases.length>1?' ('+(canvasIdx+1)+'/'+d.canvases.length+')':'');}
605
614
  }
@@ -617,7 +626,25 @@ function renderCanvasPanel(){
617
626
  var d=getConvCanvasData();
618
627
  if(d.browsers.length===0){
619
628
  f.srcdoc='<html><body style="margin:0;background:#111;display:flex;align-items:center;justify-content:center;height:100vh;font-family:monospace;color:#555"><div style="text-align:center"><div style="font-size:48px;margin-bottom:12px">&#x1F310;</div><div>No pages visited yet</div><div style="font-size:11px;margin-top:8px;color:#333">in this conversation</div><div style="margin-top:16px;font-size:11px;color:#888">Ask me to search or open a page</div></div></body></html>';
629
+ } else if(browserViewIdx>=0&&d.browsers[browserViewIdx]){
630
+ // Detail view: URL bar + screenshot + prev/next + back button
631
+ var bv=d.browsers[browserViewIdx];
632
+ var apiBase=window.API||'';
633
+ var imgSrc=bv.file?apiBase+'/api/screenshots/'+bv.file:(bv.base64?'data:image/jpeg;base64,'+bv.base64:'');
634
+ var total=d.browsers.length;
635
+ var prevBtn=browserViewIdx>0?'<button onclick="window.parent.postMessage({type:\\x27browserNav\\x27,dir:-1},\\x27*\\x27)" style="background:none;border:1px solid #444;color:#aaa;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:12px">&larr;</button>':'<button disabled style="background:none;border:1px solid #222;color:#333;padding:4px 10px;border-radius:4px;font-size:12px">&larr;</button>';
636
+ var nextBtn=browserViewIdx<total-1?'<button onclick="window.parent.postMessage({type:\\x27browserNav\\x27,dir:1},\\x27*\\x27)" style="background:none;border:1px solid #444;color:#aaa;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:12px">&rarr;</button>':'<button disabled style="background:none;border:1px solid #222;color:#333;padding:4px 10px;border-radius:4px;font-size:12px">&rarr;</button>';
637
+ var detail='<html><head><style>*{margin:0;padding:0;box-sizing:border-box}body{background:#111;display:flex;flex-direction:column;height:100vh;font-family:monospace}.toolbar{display:flex;align-items:center;gap:8px;padding:8px 10px;background:#1a1a1a;border-bottom:1px solid #2a2a2a;flex-shrink:0}.back-btn{background:none;border:1px solid #444;color:#00ff41;padding:4px 10px;border-radius:4px;cursor:pointer;font-size:12px;white-space:nowrap}.url-bar{flex:1;background:#0d0d0d;border:1px solid #333;color:#8ab4f8;padding:4px 8px;border-radius:4px;font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.counter{color:#555;font-size:11px;white-space:nowrap}.content{flex:1;overflow-y:auto;display:flex;align-items:flex-start;justify-content:center;padding:8px}.content img{max-width:100%;height:auto;display:block;border:1px solid #222;border-radius:4px}.no-img{color:#555;font-size:12px;margin:auto}</style></head><body>';
638
+ detail+='<div class="toolbar"><button class="back-btn" onclick="window.parent.postMessage({type:\\x27browserBack\\x27},\\x27*\\x27)">&#x25C4; All</button>';
639
+ detail+=prevBtn+nextBtn;
640
+ detail+='<div class="url-bar" title="'+bv.url+'">'+bv.url+'</div>';
641
+ detail+='<span class="counter">'+(browserViewIdx+1)+'/'+total+'</span>';
642
+ detail+='</div>';
643
+ detail+='<div class="content">'+(imgSrc?'<img src="'+imgSrc+'" alt="screenshot"/>':'<div class="no-img">No screenshot available</div>')+'</div>';
644
+ detail+='</body></html>';
645
+ f.srcdoc=detail;
620
646
  } else {
647
+ // Gallery view
621
648
  var apiBase=window.API||'';
622
649
  var gallery='<html><head><style>*{margin:0;padding:0;box-sizing:border-box}body{background:#111;padding:12px;font-family:monospace}h3{color:#00ff41;font-size:12px;margin-bottom:12px}.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:10px}.card{background:#1a1a1a;border:1px solid #333;border-radius:8px;overflow:hidden;cursor:pointer;transition:border-color .2s}.card:hover{border-color:#00ff41}.card img{width:100%;height:120px;object-fit:cover;display:block;background:#222}.card .info{padding:6px 8px}.card .url{color:#8ab4f8;font-size:10px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.card .time{color:#555;font-size:9px;margin-top:2px}</style></head><body><h3>Pages visited ('+d.browsers.length+')</h3><div class="grid">';
623
650
  for(var bi=0;bi<d.browsers.length;bi++){
@@ -647,19 +674,19 @@ function canvasNext(){
647
674
  }
648
675
  // Listen for messages from sandboxed canvas iframe
649
676
  window.addEventListener('message',function(e){
650
- if(e.data&&e.data.type==='selectBrowser'&&typeof e.data.index==='number'){selectBrowserPage(e.data.index);}
677
+ if(!e.data)return;
678
+ if(e.data.type==='selectBrowser'&&typeof e.data.index==='number'){selectBrowserPage(e.data.index);}
679
+ else if(e.data.type==='browserBack'){browserViewIdx=-1;renderCanvasPanel();}
680
+ else if(e.data.type==='browserNav'){
681
+ var d=getConvCanvasData();
682
+ var next=browserViewIdx+e.data.dir;
683
+ if(next>=0&&next<d.browsers.length){browserViewIdx=next;renderCanvasPanel();}
684
+ }
651
685
  });
652
686
  function selectBrowserPage(i){
653
- browserIdx=i;canvasMode='browser';renderCanvasPanel();
654
- // Also show in monitor viewer
655
- var d=getConvCanvasData();var b=d.browsers[i];
656
- if(b){
657
- showBrowserViewer(b.url,'Viewing saved page');
658
- var f=document.getElementById('bvFrame');
659
- if(f){var src=b.file?API+'/api/screenshots/'+b.file:(b.base64?'data:image/jpeg;base64,'+b.base64:'');if(src)f.innerHTML='<img src="'+src+'" alt="'+b.url+'">';}
660
- }
687
+ browserIdx=i;browserViewIdx=i;canvasMode='browser';renderCanvasPanel();
661
688
  }
662
- function canvasShowBrowser(){var d=getConvCanvasData();browserIdx=d.browsers.length-1;canvasMode='browser';renderCanvasPanel();}
689
+ function canvasShowBrowser(){var d=getConvCanvasData();browserIdx=d.browsers.length-1;browserViewIdx=-1;canvasMode='browser';renderCanvasPanel();}
663
690
  function canvasShowCanvas(){var d=getConvCanvasData();canvasIdx=d.canvases.length-1;canvasMode='canvas';renderCanvasPanel();}
664
691
 
665
692
  function onConversationSwitch(){
@@ -669,6 +696,7 @@ function onConversationSwitch(){
669
696
  var d=getConvCanvasData();
670
697
  canvasIdx=d.canvases.length-1;
671
698
  browserIdx=d.browsers.length-1;
699
+ browserViewIdx=-1;
672
700
  renderCanvasPanel();
673
701
  }
674
702
  }
@@ -3381,6 +3409,9 @@ function renderStudioNodes() {
3381
3409
  html += '<div class="' + cls + '" data-agent-label="' + esc(n.label || n.agent) + '" style="' + style + ';cursor:pointer" onclick="studioScrollToAgent(this.getAttribute(String.fromCharCode(100,97,116,97,45,97,103,101,110,116,45,108,97,98,101,108)))" title="Vai al log di ' + esc(n.label || n.agent) + '">';
3382
3410
  html += '<div class="studio-node__circle">' + n.icon + '</div>';
3383
3411
  html += '<div class="studio-node__label">' + esc(n.label) + '</div>';
3412
+ if (n.reason) {
3413
+ html += '<div class="studio-node__reason" onclick="event.stopPropagation();this.classList.toggle(String.fromCharCode(111,112,101,110))" title="' + esc(n.reason) + '">&#x2139;<span class="studio-node__reason-tip">' + esc(n.reason) + '</span></div>';
3414
+ }
3384
3415
  html += '<div class="studio-node__status studio-node__status--' + n.status + '">' + statusLabel + '</div>';
3385
3416
  if (n.status === 'running') {
3386
3417
  html += '<div class="studio-node__progress"><span></span><span></span><span></span></div>';
@@ -3916,7 +3947,7 @@ async function runStudio() {
3916
3947
  }
3917
3948
 
3918
3949
  studioState.nodes = planRes.steps.map(function(s) {
3919
- return {icon: s.icon, agent: s.agent, label: s.label, status: 'waiting'};
3950
+ return {icon: s.icon, agent: s.agent, label: s.label, reason: s.reason || '', status: 'waiting'};
3920
3951
  });
3921
3952
  renderStudioNodes();
3922
3953
  studioLog('Studio', '&#10003;', 'Workflow planned: ' + planRes.steps.map(function(s){return s.label}).join(' -> '), 'system');
@@ -5097,7 +5128,7 @@ input:focus,textarea:focus{border-color:var(--green3)}
5097
5128
  .studio-node__label{font-size:10px;color:var(--dim);text-align:center;line-height:1.3;max-width:110px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:500}
5098
5129
  .studio-node--active .studio-node__label{color:var(--green);font-weight:700}
5099
5130
  .studio-node--done .studio-node__label{color:#22c55e;font-weight:600}
5100
- .studio-node__status{font-size:8px;padding:2px 8px;border-radius:20px;font-weight:700;text-transform:uppercase;letter-spacing:.6px}
5131
+ .studio-node__reason{position:relative;font-size:11px;color:var(--dim);cursor:pointer;line-height:1;padding:2px 3px;border-radius:4px;transition:color .15s}.studio-node__reason:hover{color:var(--green)}.studio-node__reason-tip{display:none;position:absolute;bottom:calc(100% + 6px);left:50%;transform:translateX(-50%);background:#1a1a2e;border:1px solid #6366f1;border-radius:6px;padding:6px 9px;font-size:10px;color:#c7d2fe;white-space:normal;width:180px;line-height:1.4;z-index:999;pointer-events:none;text-transform:none;font-weight:400;letter-spacing:0;text-align:left;box-shadow:0 4px 20px rgba(0,0,0,.5)}.studio-node__reason.open .studio-node__reason-tip{display:block}.studio-node__status{font-size:8px;padding:2px 8px;border-radius:20px;font-weight:700;text-transform:uppercase;letter-spacing:.6px}
5101
5132
  .studio-node__status--waiting{background:rgba(156,163,175,.1);color:var(--dim)}
5102
5133
  .studio-node__status--running{background:rgba(99,102,241,.15);color:var(--green);animation:stPulse .9s ease-in-out infinite}
5103
5134
  .studio-node__status--done{background:rgba(34,197,94,.12);color:#22c55e}