nothumanallowed 13.2.12 → 13.2.14

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.12",
3
+ "version": "13.2.14",
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": {
@@ -2545,19 +2545,71 @@ export async function cmdUI(args) {
2545
2545
 
2546
2546
  const planPrompt = `You are a workflow planner for NHA Studio. The user wants to accomplish this task: "${task}"
2547
2547
 
2548
- Design a sequential workflow of 2-4 steps. IMPORTANT RULES:
2549
- - Use WebSearchAgent ONLY as step 1 when real web data is needed — it fetches real search results
2550
- - Use WriterAgent, SummaryAgent, DataAnalystAgent for analysis/synthesis steps — they work on text, NO tool calls
2551
- - Use CanvasAgent as the LAST step ONLY when a visual HTML report is explicitly requested
2552
- - The "prompt" field must be a plain language instruction — NEVER include JSON, tool calls, or code
2548
+ Design a sequential workflow of 2-5 steps. RULES:
2549
+ - Use tool-agents (WebSearchAgent, EmailAgent, CalendarAgent, GitHubAgent, NotionAgent, SlackAgent) FIRST when real live data is needed
2550
+ - Use specialist agents (SABER, ATLAS, JARVIS, etc.) for deep domain analysis — they have rich expert system prompts
2551
+ - Use CanvasAgent as the LAST step ONLY when a visual HTML report is requested
2552
+ - The "prompt" field must be a plain language instruction — never JSON or code
2553
2553
  - Each agent receives the previous step's output as context automatically
2554
-
2555
- Available agents: WebSearchAgent, ResearchAgent, EmailAgent, CalendarAgent, GitHubAgent, NotionAgent, SlackAgent, WriterAgent, SummaryAgent, DataAnalystAgent, SecurityAgent, DevOpsAgent, CanvasAgent
2556
-
2557
- Icon values must be actual emoji characters (e.g. the magnifying glass emoji, pencil emoji, chart emoji, paintbrush emoji) — never HTML entities.
2554
+ - Pick the most relevant agents for the task — don't use all of them
2555
+
2556
+ TOOL AGENTS (fetch real live data):
2557
+ - WebSearchAgent: search the web for current information
2558
+ - EmailAgent: read user's real unread emails
2559
+ - CalendarAgent: read user's real calendar events for today
2560
+ - GitHubAgent: read user's GitHub notifications and issues
2561
+ - NotionAgent: search user's Notion workspace
2562
+ - SlackAgent: read user's Slack messages
2563
+ - WriterAgent: write, summarize, synthesize text (no live data)
2564
+ - SummaryAgent: condense and summarize content
2565
+ - DataAnalystAgent: analyze data, find patterns, generate insights
2566
+ - SecurityAgent: security audit, threat analysis
2567
+ - DevOpsAgent: infrastructure, deployment, CI/CD analysis
2568
+ - CanvasAgent: generate a beautiful HTML visual dashboard (LAST step only)
2569
+
2570
+ SPECIALIST AGENTS (deep domain experts with rich system prompts):
2571
+ - SABER: security audits, OWASP, penetration testing, vulnerability analysis
2572
+ - ATLAS: infrastructure-as-code, Terraform, Kubernetes, cloud architecture
2573
+ - JARVIS: full-stack architecture, API design, system design, ADRs
2574
+ - VERITAS: fact-checking, evidence verification, claim validation
2575
+ - CASSANDRA: risk analysis, failure modes, worst-case scenarios
2576
+ - MERCURY: financial analysis, ROI, unit economics, market modeling
2577
+ - HERALD: news analysis, trend detection, executive briefings
2578
+ - ATHENA: tech evaluation, framework comparison, benchmarks
2579
+ - ORACLE: business intelligence, KPIs, OKRs, dashboards
2580
+ - NAVI: data exploration, statistical analysis, pattern detection
2581
+ - MUSE: creative brainstorming, ideation, naming, taglines
2582
+ - QUILL: short-form content, summaries, press releases
2583
+ - SCHEHERAZADE: long-form technical writing, documentation, tutorials
2584
+ - ECHO: content adaptation, cross-platform distribution
2585
+ - POLYGLOT: translation, localization, multilingual content
2586
+ - FORGE: CI/CD pipelines, GitHub Actions, Docker builds
2587
+ - FLUX: deployment strategies, blue/green, canary releases
2588
+ - SHOGUN: Kubernetes, Helm, container orchestration
2589
+ - PIPE: data pipelines, Airflow, dbt, ETL
2590
+ - MACRO: bulk operations, data migration, batch processing
2591
+ - SHELL: shell scripting, CLI tools, automation scripts
2592
+ - CONDUCTOR: workflow orchestration, task decomposition
2593
+ - CRON: scheduling, cron jobs, time-based automation
2594
+ - HERMES: webhooks, event-driven architecture, integrations
2595
+ - BABEL: API design, microservices, OpenAPI specs
2596
+ - CARTOGRAPHER: data mapping, schema inference, knowledge graphs
2597
+ - LOGOS: logical analysis, argument mapping, decision theory
2598
+ - EDI: A/B testing, statistical modeling, hypothesis testing
2599
+ - EPICURE: nutrition, recipes, meal planning
2600
+ - MURASAKI: creative writing, storytelling, narrative craft
2601
+ - LINK: community management, reputation systems
2602
+ - GLITCH: chaos engineering, resilience testing
2603
+ - TEMPEST: performance engineering, load testing
2604
+ - SAURON: observability, monitoring, alerting
2605
+ - PROMETHEUS: capability routing, task decomposition
2606
+ - ADE: agent design, system prompt engineering
2607
+ - ZERO: vulnerability scanning, dependency audit, secret detection
2608
+
2609
+ Icon values must be actual emoji characters — never HTML entities.
2558
2610
 
2559
2611
  Respond with ONLY valid JSON, no markdown:
2560
- {"steps":[{"icon":"🔍","agent":"WebSearchAgent","label":"Search AI news","prompt":"Search for the latest artificial intelligence news today and return the top 5 results with title, source and description"},{"icon":"✍️","agent":"WriterAgent","label":"Select and analyze","prompt":"From the search results provided, select the 3 most important AI news items and write a detailed analysis of each"}]}`;
2612
+ {"steps":[{"icon":"🔍","agent":"WebSearchAgent","label":"Search AI news","prompt":"Search for the latest AI agent news"},{"icon":"🧠","agent":"ATHENA","label":"Tech analysis","prompt":"Analyze the search results and compare the key technologies mentioned"}]}`;
2561
2613
 
2562
2614
  try {
2563
2615
  const planRaw = await callLLM(config, 'You are a JSON workflow planner. Respond only with valid JSON.', planPrompt, { max_tokens: 800 });
@@ -2693,78 +2745,89 @@ Respond with ONLY valid JSON, no markdown:
2693
2745
 
2694
2746
  // ── Build system prompt with real tool data ───────────────────
2695
2747
  const isCanvasAgent = agent === 'CanvasAgent';
2696
- const isPureAnalysis = ['WriterAgent','SummaryAgent','DataAnalystAgent','SecurityAgent','DevOpsAgent'].includes(agent);
2748
+ // Tool-data agents: fetch real live data and use buildSystemPrompt (tool calls allowed)
2749
+ const isLiveDataAgent = ['CalendarAgent','EmailAgent','GitHubAgent','NotionAgent','SlackAgent','DriveAgent','BrowserAgent','WebSearchAgent','ResearchAgent'].includes(agent);
2697
2750
 
2698
- const canvasSystemPrompt = `You are an HTML report generator. Your ONLY job is to output a single complete HTML document.
2699
- CRITICAL RULES:
2700
- - Start your response with exactly: <!DOCTYPE html>
2701
- - Do NOT wrap output in JSON, markdown code blocks, or any other format
2702
- - Do NOT output {"action":...} or any JSON
2703
- - Output raw HTML only, starting with <!DOCTYPE html> on the very first line
2704
- - Use a modern, clean design: white background, Inter/system font, subtle shadows, no cyberpunk
2705
- - Structure: header with title + subtitle, then content cards/sections
2706
- - Make it visually rich with proper spacing, colors (#6366f1 accent), and typography`;
2707
-
2708
- const agentInstruction = isCanvasAgent
2709
- ? `${canvasSystemPrompt}\n\nContent to render as HTML report:\n${context.slice(0, 4000)}`
2710
- : `You are ${agent}, a specialist AI agent inside NHA Studio.\nYour task: ${stepPrompt}\n` +
2751
+ const canvasSystemPrompt = `You are an HTML report generator. Output a single complete HTML document. No preamble, no explanation.
2752
+ RULES:
2753
+ - First character of your response must be < (start of <!DOCTYPE html>)
2754
+ - Do NOT use markdown code blocks, JSON, or any wrapper
2755
+ - Use clean design: white background, Inter/system-ui font, #6366f1 accent color
2756
+ - Structure: gradient header, then card sections with the content
2757
+ - Make it complete and self-contained`;
2758
+
2759
+ let sysPrompt, userMsg;
2760
+
2761
+ if (isCanvasAgent) {
2762
+ sysPrompt = canvasSystemPrompt;
2763
+ userMsg = `Generate a beautiful HTML dashboard report for this content. Start immediately with <!DOCTYPE html>:\n\n${context.slice(0, 8000)}`;
2764
+ } else if (isLiveDataAgent) {
2765
+ // These agents fetched real data — use buildSystemPrompt so they can call tools too
2766
+ const agentInstruction = `You are ${agent}, a specialist AI agent inside NHA Studio.\nYour task: ${stepPrompt}\n` +
2711
2767
  (toolData ? `\n## DATA FROM TOOLS:\n${toolData.slice(0, 4000)}\n` : '') +
2712
2768
  (context ? `\n## OUTPUT FROM PREVIOUS AGENTS:\n${context.slice(0, 3000)}\n` : '') +
2713
- (isPureAnalysis
2714
- ? '\nWrite your analysis in plain text. Do NOT output JSON, tool calls, or code blocks. Use the context above.'
2715
- : '\nOutput your result directly. No preamble.');
2716
-
2717
- const sysPrompt = isCanvasAgent
2718
- ? canvasSystemPrompt
2719
- : buildSystemPrompt(agent, agentInstruction, config);
2720
-
2721
- const userMsg = isCanvasAgent
2722
- ? `Create a beautiful HTML report for this content:\n\n${context.slice(0, 4000)}`
2723
- : toolData
2724
- ? `Use the data above to complete: ${stepPrompt}`
2725
- : context
2726
- ? `Based on the previous output, complete: ${stepPrompt}`
2727
- : stepPrompt;
2728
-
2729
- // ── Stream LLM response (or non-stream for CanvasAgent) ──────
2730
- let fullOutput = '';
2731
- if (isCanvasAgent) {
2732
- // CanvasAgent: non-streaming call — more reliable for large HTML output
2733
- sendToken('Generating visual report...');
2734
- try {
2735
- fullOutput = await withTimeout(
2736
- callLLM(config, sysPrompt, userMsg, { max_tokens: 3000 }),
2737
- 'CanvasAgent'
2738
- );
2739
- } catch (e) {
2740
- fullOutput = '';
2741
- }
2769
+ '\nWrite your analysis in plain text. Do NOT output JSON, tool calls, or code blocks. Summarize the data clearly.';
2770
+ sysPrompt = buildSystemPrompt(agent, agentInstruction, config);
2771
+ userMsg = toolData
2772
+ ? `Summarize the data above for: ${stepPrompt}`
2773
+ : context
2774
+ ? `Based on the previous output, complete: ${stepPrompt}`
2775
+ : stepPrompt;
2742
2776
  } else {
2743
- await callLLMStream(config, sysPrompt, userMsg,
2744
- (token) => { fullOutput += token; sendToken(token); },
2745
- { max_tokens: 2500 }
2777
+ // All other agents (WriterAgent, DataAnalystAgent, specialist agents, etc.)
2778
+ // Use a focused prompt with NO TOOL_DEFINITIONS to prevent JSON/tool-call output
2779
+ const today = new Date().toISOString().split('T')[0];
2780
+ const language = config?.language || 'Italian';
2781
+ sysPrompt = `You are ${agent}, a specialist AI agent inside NHA Studio. Today is ${today}. Respond in ${language}.
2782
+
2783
+ CRITICAL RULES:
2784
+ - Do NOT output JSON, tool calls, function calls, or code blocks
2785
+ - Do NOT ask for more information — use only the data provided below
2786
+ - Write in plain prose, structured with headers and bullet points where appropriate
2787
+ - Be thorough and specific — this is for an executive briefing
2788
+
2789
+ ${toolData ? `## LIVE DATA:\n${toolData.slice(0, 4000)}\n` : ''}
2790
+ ${context ? `## CONTEXT FROM PREVIOUS AGENTS:\n${context.slice(0, 5000)}\n` : ''}`;
2791
+ userMsg = toolData
2792
+ ? `Use the live data and context above to complete this task: ${stepPrompt}`
2793
+ : context
2794
+ ? `Using the context from previous steps, complete this task: ${stepPrompt}`
2795
+ : stepPrompt;
2796
+ }
2797
+
2798
+ // ── Stream LLM response ───────────────────────────────────────
2799
+ let fullOutput = '';
2800
+ sendToken(isCanvasAgent ? 'Generating visual report...' : '');
2801
+ try {
2802
+ await withTimeout(
2803
+ callLLMStream(config, sysPrompt, userMsg,
2804
+ (token) => { fullOutput += token; if (!isCanvasAgent) sendToken(token); },
2805
+ ),
2806
+ isCanvasAgent ? 60000 : 35000
2746
2807
  );
2808
+ } catch (e) {
2809
+ if (!isCanvasAgent) sendToken(`[Error: ${e.message}]`);
2747
2810
  }
2748
2811
 
2749
2812
  if (isCanvasAgent) {
2750
- let html = fullOutput;
2813
+ let html = fullOutput.trim();
2814
+ // Strip thinking tags if not already filtered
2815
+ html = html.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
2751
2816
  // Extract from markdown code block
2752
- const mdMatch = html.match(/```html?\s*([\s\S]*?)```/);
2817
+ const mdMatch = html.match(/```html?\s*([\s\S]*?)```/i);
2753
2818
  if (mdMatch) html = mdMatch[1].trim();
2754
- // Extract from JSON {"html": "..."}
2755
- if (!html.includes('<!DOCTYPE') && html.includes('"html"')) {
2756
- try {
2757
- const jm = html.match(/"html"\s*:\s*"([\s\S]+?)(?<!\\)"/);
2758
- if (jm) html = JSON.parse('"' + jm[1] + '"');
2759
- } catch {}
2760
- }
2819
+ // Find <!DOCTYPE or <html start if there's preamble text
2820
+ const doctypeIdx = html.indexOf('<!DOCTYPE');
2821
+ const htmlTagIdx = html.indexOf('<html');
2822
+ const startIdx = doctypeIdx >= 0 ? doctypeIdx : (htmlTagIdx >= 0 ? htmlTagIdx : -1);
2823
+ if (startIdx > 0) html = html.slice(startIdx);
2761
2824
  // Fallback: build clean HTML from the context directly (no LLM needed)
2762
2825
  if (!html.trim() || !html.includes('<')) {
2763
2826
  // Try splitting on markdown headings first, then numbered items, then double newlines
2764
2827
  let sections = context.split(/\n#{1,3} /).filter(s => s.trim());
2765
2828
  if (sections.length <= 1) sections = context.split(/\n(?=\*\*\d+[\.\)])|(?=^\d+[\.\)])/).filter(s => s.trim());
2766
2829
  if (sections.length <= 1) sections = context.split(/\n{2,}/).filter(s => s.trim());
2767
- const reportTitle = (userMsg.slice(0, 80) || 'NHA Studio Report').replace(/</g,'&lt;').replace(/>/g,'&gt;');
2830
+ const reportTitle = (task.slice(0, 80) || 'NHA Studio Report').replace(/</g,'&lt;').replace(/>/g,'&gt;');
2768
2831
  const cardsHtml = sections.map(s => {
2769
2832
  const clean = s.replace(/\*\*/g, '').replace(/\*/g, '').trim();
2770
2833
  const lines = clean.split('\n').filter(Boolean);
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.12';
8
+ export const VERSION = '13.2.14';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -190,6 +190,7 @@ function deleteConv(id){return fetch(API+'/api/conversations/'+id,{method:'DELET
190
190
  function clearChatHistory(){createNewConv()}
191
191
  var agentsList = [];
192
192
  var selectedAgent = null;
193
+ var agentChatHistory = []; // [{role:'user'|'agent', text:'...'}]
193
194
 
194
195
  // ---- NAV ----
195
196
  function switchView(v) {
@@ -1188,16 +1189,18 @@ function openDayDetail(dateStr){
1188
1189
  });
1189
1190
  }
1190
1191
 
1191
- // Use the agent modal for day detail
1192
+ // Use the agent modal for day detail (read-only mode)
1193
+ selectedAgent=null;
1194
+ agentChatHistory=[];
1192
1195
  document.getElementById('modalName').textContent=dayLabel;
1193
- document.getElementById('modalPrompt').style.display='none';
1194
- document.getElementById('fileDropZone').style.display='none';
1195
- document.getElementById('fileInfo').style.display='none';
1196
- document.getElementById('modalResponse').style.display='block';
1197
- document.getElementById('modalResponse').innerHTML=h;
1196
+ document.getElementById('modalAgentDesc').textContent='';
1197
+ // Show the day events in the messages area
1198
+ var msgEl=document.getElementById('agentMessages');
1199
+ if(msgEl){msgEl.innerHTML='<div class="agent-chat__bubble agent-chat__bubble--agent md-body" style="width:100%;max-width:100%;box-sizing:border-box">'+h+'</div>';}
1200
+ // Hide input footer in read-only mode
1201
+ var footer=document.querySelector('.agent-chat__footer');
1202
+ if(footer)footer.style.display='none';
1198
1203
  document.getElementById('agentModal').classList.add('modal-overlay--open');
1199
- var sendBtn=document.getElementById('agentModal').querySelector('.btn--primary');
1200
- if(sendBtn)sendBtn.style.display='none';
1201
1204
  }
1202
1205
 
1203
1206
  // ---- GITHUB ----
@@ -2162,8 +2165,39 @@ function confirmDeleteAgent(name){
2162
2165
  });
2163
2166
  }
2164
2167
 
2168
+ function renderAgentMessages(){
2169
+ var el=document.getElementById('agentMessages');
2170
+ if(!el)return;
2171
+ if(agentChatHistory.length===0){
2172
+ var icon=AGENT_ICONS[selectedAgent]||'\uD83E\uDD16';
2173
+ var desc=AGENT_DESCRIPTIONS[selectedAgent]||'';
2174
+ el.innerHTML='<div style="text-align:center;padding:24px 8px;color:var(--dim);font-size:12px">'+
2175
+ '<div style="font-size:28px;margin-bottom:6px">'+icon+'</div>'+
2176
+ '<div style="color:var(--fg);font-weight:600;margin-bottom:4px">'+esc(selectedAgent.toUpperCase())+'</div>'+
2177
+ (desc?'<div>'+esc(desc)+'</div>':'')+
2178
+ '</div>';
2179
+ return;
2180
+ }
2181
+ el.innerHTML=agentChatHistory.map(function(m){
2182
+ var cls='agent-chat__bubble agent-chat__bubble--'+(m.role==='user'?'user':'agent');
2183
+ var content;
2184
+ if(m.role==='user'){
2185
+ content=esc(m.text);
2186
+ } else if(m.waiting||(m.streaming&&!m.text)){
2187
+ content='<span class="thinking-dots"><span></span><span></span><span></span></span>';
2188
+ } else if(m.streaming){
2189
+ content=esc(m.text)+'\u258B';
2190
+ } else {
2191
+ content=renderMd(m.text);
2192
+ }
2193
+ return '<div class="'+cls+' '+(m.role==='agent'?'md-body':'')+'">'+content+'</div>';
2194
+ }).join('');
2195
+ el.scrollTop=el.scrollHeight;
2196
+ }
2197
+
2165
2198
  function openAgent(name,display){
2166
2199
  selectedAgent=name;
2200
+ agentChatHistory=[];
2167
2201
  attachedFileContent=null;attachedFileName=null;
2168
2202
  var icon=AGENT_ICONS[name.toLowerCase()]||'\u{1F916}';
2169
2203
  var desc=AGENT_DESCRIPTIONS[name.toLowerCase()]||'';
@@ -2172,17 +2206,17 @@ function openAgent(name,display){
2172
2206
  var subEl=document.getElementById('modalAgentDesc');
2173
2207
  if(subEl){subEl.textContent=desc;}
2174
2208
  document.getElementById('modalPrompt').value='';
2175
- document.getElementById('modalPrompt').style.display='';
2176
- document.getElementById('modalResponse').style.display='none';
2177
- document.getElementById('modalResponse').textContent='';
2178
- document.getElementById('modalResponse').innerHTML='';
2179
- document.getElementById('fileInfo').style.display='none';
2180
- document.getElementById('fileDropZone').style.display='';
2181
- document.getElementById('fileDropZone').style.borderColor='var(--border2)';
2182
- document.getElementById('fileInput').value='';
2209
+ document.getElementById('agentFileInfo').style.display='none';
2210
+ document.getElementById('agentFileInput').value='';
2211
+ document.getElementById('agentFileDropZone').style.borderColor='';
2183
2212
  var askBtn=document.getElementById('agentAskBtn');
2184
- if(askBtn){askBtn.disabled=false;askBtn.textContent='Ask';}
2213
+ if(askBtn){askBtn.disabled=false;askBtn.textContent='Send';}
2214
+ // Ensure footer is visible (may be hidden from calendar day-detail view)
2215
+ var footer=document.querySelector('.agent-chat__footer');
2216
+ if(footer)footer.style.display='';
2217
+ renderAgentMessages();
2185
2218
  document.getElementById('agentModal').classList.add('modal-overlay--open');
2219
+ setTimeout(function(){var i=document.getElementById('modalPrompt');if(i)i.focus();},100);
2186
2220
  }
2187
2221
  function closeModal(){
2188
2222
  document.getElementById('agentModal').classList.remove('modal-overlay--open');
@@ -2388,21 +2422,20 @@ function handleFileSelect(input) {
2388
2422
  if (file) readFile(file);
2389
2423
  }
2390
2424
  function readFile(file) {
2425
+ var infoId = document.getElementById('agentFileInfo') ? 'agentFileInfo' : 'fileInfo';
2426
+ var dropId = document.getElementById('agentFileDropZone') ? 'agentFileDropZone' : 'fileDropZone';
2427
+ var infoEl = document.getElementById(infoId);
2428
+ var dropEl = document.getElementById(dropId);
2391
2429
  if (file.size > 500000) {
2392
- document.getElementById('fileInfo').style.display = 'block';
2393
- document.getElementById('fileInfo').textContent = 'File too large (max 500KB)';
2394
- document.getElementById('fileInfo').style.color = 'var(--red)';
2430
+ if(infoEl){infoEl.style.display='block';infoEl.style.color='var(--red)';infoEl.textContent='File too large (max 500KB)';}
2395
2431
  return;
2396
2432
  }
2397
2433
  var reader = new FileReader();
2398
2434
  reader.onload = function(e) {
2399
2435
  attachedFileContent = e.target.result;
2400
2436
  attachedFileName = file.name;
2401
- var info = document.getElementById('fileInfo');
2402
- info.style.display = 'block';
2403
- info.style.color = 'var(--cyan)';
2404
- info.textContent = 'Attached: ' + file.name + ' (' + (file.size / 1024).toFixed(1) + ' KB)';
2405
- document.getElementById('fileDropZone').style.borderColor = 'var(--green)';
2437
+ if(infoEl){infoEl.style.display='block';infoEl.style.color='var(--cyan)';infoEl.textContent='Attached: '+file.name+' ('+(file.size/1024).toFixed(1)+' KB)';}
2438
+ if(dropEl){dropEl.style.borderColor='var(--green)';}
2406
2439
  };
2407
2440
  reader.readAsText(file);
2408
2441
  }
@@ -2411,9 +2444,13 @@ var agentAbortController = null;
2411
2444
 
2412
2445
  function askAgent(){
2413
2446
  var p=document.getElementById('modalPrompt').value.trim();if(!p||!selectedAgent)return;
2414
- var resp=document.getElementById('modalResponse');
2415
- resp.style.display='block';
2416
- resp.innerHTML='<span style="color:var(--dim)">Thinking...</span>';
2447
+
2448
+ // Add user message to history
2449
+ agentChatHistory.push({role:'user',text:p});
2450
+ // Add waiting placeholder — shows dots until first token arrives
2451
+ agentChatHistory.push({role:'agent',text:'',waiting:true});
2452
+ renderAgentMessages();
2453
+ document.getElementById('modalPrompt').value='';
2417
2454
 
2418
2455
  // Abort any previous stream
2419
2456
  if(agentAbortController){try{agentAbortController.abort();}catch(e){}}
@@ -2422,11 +2459,16 @@ function askAgent(){
2422
2459
  var payload={agent:selectedAgent,prompt:p};
2423
2460
  if(attachedFileContent){payload.fileContent=attachedFileContent;payload.fileName=attachedFileName;}
2424
2461
 
2425
- // Disable Ask button while streaming
2462
+ // Disable Send button while streaming
2426
2463
  var askBtn=document.getElementById('agentAskBtn');
2427
2464
  if(askBtn){askBtn.disabled=true;askBtn.textContent='...';}
2428
2465
 
2429
- var accumulated='';
2466
+ var agentMsgIdx=agentChatHistory.length-1; // index of the streaming placeholder
2467
+
2468
+ function updateStreamingBubble(text,done){
2469
+ agentChatHistory[agentMsgIdx]={role:'agent',text:text,waiting:false,streaming:!done};
2470
+ renderAgentMessages();
2471
+ }
2430
2472
 
2431
2473
  fetch(API+'/api/ask/stream',{
2432
2474
  method:'POST',
@@ -2437,44 +2479,45 @@ function askAgent(){
2437
2479
  var reader=response.body.getReader();
2438
2480
  var decoder=new TextDecoder();
2439
2481
  var buf='';
2482
+ var accumulated='';
2440
2483
  function pump(){
2441
2484
  return reader.read().then(function(result){
2442
2485
  if(result.done){
2443
- // fallback if done event never arrived
2444
- if(accumulated && resp.textContent!==accumulated){
2445
- try{resp.innerHTML=renderMd(accumulated);}catch(e){resp.textContent=accumulated;}
2446
- }
2447
- if(askBtn){askBtn.disabled=false;askBtn.textContent='Ask';}
2486
+ updateStreamingBubble(accumulated||'(empty response)',true);
2487
+ if(askBtn){askBtn.disabled=false;askBtn.textContent='Send';}
2448
2488
  return;
2449
2489
  }
2450
2490
  buf+=decoder.decode(result.value,{stream:true});
2451
- var NLA=String.fromCharCode(10);
2452
- var lines=buf.split(NLA);
2491
+ var lines=buf.split(String.fromCharCode(10));
2453
2492
  buf=lines.pop();
2454
2493
  lines.forEach(function(line){
2455
- if(line.indexOf('data: ')===0){
2456
- try{
2457
- var ev=JSON.parse(line.slice(6));
2458
- if(ev.token){accumulated+=ev.token;resp.textContent=accumulated+'\u258B';}
2459
- if(ev.done){
2460
- try{resp.innerHTML=renderMd(accumulated||'(empty response)');}catch(e){resp.textContent=accumulated||'(empty response)';}
2461
- if(askBtn){askBtn.disabled=false;askBtn.textContent='Ask';}
2462
- attachedFileContent=null;attachedFileName=null;
2463
- document.getElementById('fileInfo').style.display='none';
2464
- document.getElementById('fileDropZone').style.borderColor='var(--border2)';
2465
- document.getElementById('fileInput').value='';
2466
- }
2467
- if(ev.error){resp.innerHTML='<span style="color:var(--red)">Error: '+esc(ev.error)+'</span>';if(askBtn){askBtn.disabled=false;askBtn.textContent='Ask';}}
2468
- }catch(e){}
2469
- }
2494
+ if(line.indexOf('data: ')!==0)return;
2495
+ try{
2496
+ var ev=JSON.parse(line.slice(6));
2497
+ if(ev.token){accumulated+=ev.token;updateStreamingBubble(accumulated,false);}
2498
+ if(ev.done){
2499
+ updateStreamingBubble(accumulated||'(empty response)',true);
2500
+ if(askBtn){askBtn.disabled=false;askBtn.textContent='Send';}
2501
+ attachedFileContent=null;attachedFileName=null;
2502
+ document.getElementById('agentFileInfo').style.display='none';
2503
+ document.getElementById('agentFileInput').value='';
2504
+ document.getElementById('agentFileDropZone').style.borderColor='';
2505
+ }
2506
+ if(ev.error){
2507
+ updateStreamingBubble('\u26a0\ufe0f Error: '+ev.error,true);
2508
+ if(askBtn){askBtn.disabled=false;askBtn.textContent='Send';}
2509
+ }
2510
+ }catch(e){}
2470
2511
  });
2471
2512
  return pump();
2472
2513
  });
2473
2514
  }
2474
2515
  return pump();
2475
2516
  }).catch(function(err){
2476
- if(err.name!=='AbortError'){resp.innerHTML='<span style="color:var(--red)">Stream error: '+esc(err.message)+'</span>';}
2477
- if(askBtn){askBtn.disabled=false;askBtn.textContent='Ask';}
2517
+ if(err.name!=='AbortError'){
2518
+ updateStreamingBubble('\u26a0\ufe0f Stream error: '+err.message,true);
2519
+ }
2520
+ if(askBtn){askBtn.disabled=false;askBtn.textContent='Send';}
2478
2521
  });
2479
2522
  }
2480
2523
 
@@ -2705,7 +2748,8 @@ function renderStudioNodes() {
2705
2748
  else if (n.status === 'done') cls += ' studio-node--done';
2706
2749
  else if (n.status === 'error') cls += ' studio-node--error';
2707
2750
  var statusLabel = {waiting:'&#9711; wait', running:'&#9654; running', done:'&#10003; done', error:'&#10005; error'}[n.status] || '';
2708
- html += '<div class="' + cls + '">';
2751
+ var delay = (i * 120) + 'ms';
2752
+ html += '<div class="' + cls + '" style="animation-delay:' + delay + '">';
2709
2753
  html += '<div class="studio-node__circle">' + n.icon + '</div>';
2710
2754
  html += '<div class="studio-node__label">' + esc(n.label) + '</div>';
2711
2755
  html += '<div class="studio-node__status studio-node__status--' + n.status + '">' + statusLabel + '</div>';
@@ -2715,7 +2759,8 @@ function renderStudioNodes() {
2715
2759
  var arrowCls = 'studio-arrow';
2716
2760
  if (n.status === 'done' && next.status === 'running') arrowCls += ' studio-arrow--active';
2717
2761
  else if (n.status === 'done') arrowCls += ' studio-arrow--done';
2718
- html += '<div class="' + arrowCls + '">&#8594;</div>';
2762
+ var arrowDelay = (i * 120 + 60) + 'ms';
2763
+ html += '<div class="' + arrowCls + '" style="opacity:0;animation:stNodeIn .3s ease ' + arrowDelay + ' forwards">&#8594;</div>';
2719
2764
  }
2720
2765
  });
2721
2766
  html += '</div>';
@@ -2773,9 +2818,12 @@ async function runStudio() {
2773
2818
  renderStudioResult();
2774
2819
 
2775
2820
  var btn = document.getElementById('studioRunBtn');
2776
- if (btn) btn.disabled = true;
2821
+ if (btn) { btn.disabled = true; btn.textContent = 'Planning...'; }
2777
2822
 
2778
2823
  studioLog('Studio', '&#9881;', 'Planning workflow for: "' + task + '"', 'system');
2824
+ // Show a temporary planning indicator in the nodes area
2825
+ var nodesEl = document.getElementById('studioNodes');
2826
+ if (nodesEl) nodesEl.innerHTML = '<div style="text-align:center;padding:20px;color:var(--dim);font-size:12px;font-family:var(--font)"><span class="thinking-dots"><span></span><span></span><span></span></span><div style="margin-top:8px">Designing workflow...</div></div>';
2779
2827
 
2780
2828
  try {
2781
2829
  // Step 1: plan the workflow
@@ -2815,9 +2863,8 @@ async function runStudio() {
2815
2863
  var ct = document.getElementById('canvasTitle');
2816
2864
  if (cf && cp) {
2817
2865
  cf.srcdoc = stepResult.canvas;
2818
- cp.style.display = 'flex';
2866
+ cp.classList.add('open');
2819
2867
  if (ct) ct.textContent = node.label + ' Report';
2820
- canvasShowCanvas();
2821
2868
  }
2822
2869
  }
2823
2870
  context = stepResult.output || stepResult.canvas || context;
@@ -2837,7 +2884,7 @@ async function runStudio() {
2837
2884
  }
2838
2885
 
2839
2886
  studioState.running = false;
2840
- if (btn) btn.disabled = false;
2887
+ if (btn) { btn.disabled = false; btn.textContent = 'Run'; }
2841
2888
  }
2842
2889
 
2843
2890
  // ---- STUDIO SESSIONS ----
@@ -2982,8 +3029,9 @@ function runStudioStep(idx, node, task, context, stepDef) {
2982
3029
  var cp2 = document.getElementById('canvasPanel');
2983
3030
  if (cf2 && cp2) {
2984
3031
  cf2.srcdoc = canvasHtml;
2985
- cp2.style.display = 'flex';
2986
- canvasShowCanvas();
3032
+ cp2.classList.add('open');
3033
+ var ct2 = document.getElementById('canvasTitle');
3034
+ if (ct2) ct2.textContent = 'Studio Report';
2987
3035
  }
2988
3036
  }
2989
3037
  if (ev.usage) { studioAddTokens(ev.usage.input||0, ev.usage.output||0); }
@@ -3305,7 +3353,7 @@ async function runManualWorkflow() {
3305
3353
  if (stepResult.canvas) {
3306
3354
  var cf = document.getElementById('canvasFrame');
3307
3355
  var cp = document.getElementById('canvasPanel');
3308
- if (cf && cp) { cf.srcdoc = stepResult.canvas; cp.style.display='flex'; canvasShowCanvas(); }
3356
+ if (cf && cp) { cf.srcdoc = stepResult.canvas; cp.classList.add('open'); var ct3=document.getElementById('canvasTitle'); if(ct3) ct3.textContent='Studio Report'; }
3309
3357
  }
3310
3358
  context = stepResult.output || stepResult.canvas || context;
3311
3359
  }
@@ -3562,7 +3610,8 @@ input:focus,textarea:focus{border-color:var(--green3)}
3562
3610
  /* ---- MODAL ---- */
3563
3611
  .modal-overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,0.7);z-index:300;align-items:center;justify-content:center}
3564
3612
  .modal-overlay--open{display:flex}
3565
- .modal{background:var(--bg2);border:1px solid var(--border2);border-radius:8px;width:92%;max-width:540px;max-height:90vh;display:flex;flex-direction:column}
3613
+ .modal{background:var(--bg2);border:1px solid var(--border2);border-radius:8px;width:92%;max-width:560px;max-height:90vh;display:flex;flex-direction:column}
3614
+ .modal--chat{height:80vh}
3566
3615
  .modal__header{display:flex;justify-content:space-between;align-items:center;padding:14px 16px;border-bottom:1px solid var(--border)}
3567
3616
  .modal__header h2{font-size:16px;color:var(--green)}
3568
3617
  .modal__close{background:none;color:var(--dim);font-size:24px;padding:0 4px}
@@ -3570,6 +3619,18 @@ input:focus,textarea:focus{border-color:var(--green3)}
3570
3619
  .modal__body textarea{width:100%;min-height:80px;margin-bottom:10px}
3571
3620
  .modal__response{background:var(--bg3);border:1px solid var(--border);border-radius:var(--r);padding:12px;word-wrap:break-word;max-height:400px;overflow-y:auto;font-size:13px}
3572
3621
  .modal__footer{display:flex;justify-content:flex-end;gap:8px;padding:12px 16px;border-top:1px solid var(--border)}
3622
+ .modal--chat .modal__body{padding:0;display:flex;flex-direction:column;overflow:hidden}
3623
+ .agent-chat{display:flex;flex-direction:column;height:100%;min-height:0;padding:12px 16px;box-sizing:border-box}
3624
+ .agent-chat__messages{flex:1;overflow-y:auto;padding:4px 0 8px;min-height:0;display:flex;flex-direction:column;gap:8px}
3625
+ .agent-chat__bubble{max-width:88%;border-radius:8px;padding:8px 12px;font-size:13px;line-height:1.55;word-wrap:break-word}
3626
+ .agent-chat__bubble--user{align-self:flex-end;background:var(--green3);color:#e8ffe8}
3627
+ .agent-chat__bubble--agent{align-self:flex-start;background:var(--bg3);border:1px solid var(--border)}
3628
+ .agent-chat__footer{display:flex;flex-direction:column;gap:6px;padding-top:8px;border-top:1px solid var(--border)}
3629
+ .agent-chat__drop{border:2px dashed var(--border2);border-radius:6px;padding:8px;text-align:center;color:var(--dim);font-size:11px;cursor:pointer;transition:border-color .2s}
3630
+ .agent-chat__drop:hover{border-color:var(--green)}
3631
+ .agent-chat__input-row{display:flex;gap:6px;align-items:flex-end}
3632
+ .agent-chat__input{flex:1;background:var(--bg2);border:1px solid var(--border);border-radius:var(--r);padding:8px 10px;color:var(--fg);font-size:13px;font-family:var(--font);resize:none;min-height:36px;max-height:120px;overflow-y:auto;outline:none}
3633
+ .agent-chat__input:focus{border-color:var(--green)}
3573
3634
  .btn{padding:8px 16px;border-radius:var(--r);font-size:12px;font-weight:600}
3574
3635
  .btn--primary{background:var(--green3);color:var(--bg)}
3575
3636
  .btn--secondary{background:var(--bg3);color:var(--dim);border:1px solid var(--border)}
@@ -3577,6 +3638,11 @@ input:focus,textarea:focus{border-color:var(--green3)}
3577
3638
  /* ---- SPINNER ---- */
3578
3639
  .spinner{width:24px;height:24px;border:2px solid var(--border);border-top-color:var(--green);border-radius:50%;animation:spin .6s linear infinite;margin:0 auto 12px}
3579
3640
  @keyframes spin{to{transform:rotate(360deg)}}
3641
+ .thinking-dots{display:inline-flex;gap:4px;align-items:center;padding:2px 0}
3642
+ .thinking-dots span{width:6px;height:6px;border-radius:50%;background:var(--dim);animation:tdot 1.2s ease-in-out infinite}
3643
+ .thinking-dots span:nth-child(2){animation-delay:.2s}
3644
+ .thinking-dots span:nth-child(3){animation-delay:.4s}
3645
+ @keyframes tdot{0%,80%,100%{opacity:.2;transform:scale(.8)}40%{opacity:1;transform:scale(1)}}
3580
3646
 
3581
3647
  /* ---- TOASTS (real-time notifications) ---- */
3582
3648
  .toast-container{position:fixed;top:16px;right:16px;z-index:500;display:flex;flex-direction:column;gap:8px;pointer-events:none}
@@ -3614,7 +3680,8 @@ input:focus,textarea:focus{border-color:var(--green3)}
3614
3680
  .studio-canvas__empty{display:flex;align-items:center;justify-content:center;height:180px;color:var(--dim);font-size:11px;flex-direction:column;gap:8px}
3615
3681
  .studio-canvas__empty-icon{font-size:32px;opacity:.3}
3616
3682
  .studio-nodes{display:flex;align-items:center;gap:0;padding:28px 24px;overflow-x:auto;min-height:130px;background:var(--bg2);border-radius:10px;border:1px solid var(--border);margin-bottom:16px}
3617
- .studio-node{position:relative;display:flex;flex-direction:column;align-items:center;gap:7px;min-width:106px;max-width:126px}
3683
+ .studio-node{position:relative;display:flex;flex-direction:column;align-items:center;gap:7px;min-width:106px;max-width:126px;opacity:0;animation:stNodeIn .35s ease forwards}
3684
+ @keyframes stNodeIn{from{opacity:0;transform:translateY(10px) scale(.92)}to{opacity:1;transform:translateY(0) scale(1)}}
3618
3685
  .studio-node__circle{width:56px;height:56px;border-radius:14px;border:1.5px solid var(--border2);background:var(--bg3);display:flex;align-items:center;justify-content:center;font-size:22px;transition:all .35s;flex-shrink:0}
3619
3686
  .studio-node--active .studio-node__circle{border-color:var(--green3);box-shadow:0 0 0 4px rgba(99,102,241,.15);background:var(--greendim);animation:stRing 1.4s ease-out infinite}
3620
3687
  .studio-node--done .studio-node__circle{border-color:#22c55e;background:rgba(34,197,94,.08);box-shadow:0 0 0 3px rgba(34,197,94,.12)}
@@ -3778,23 +3845,26 @@ input:focus,textarea:focus{border-color:var(--green3)}
3778
3845
  </div>
3779
3846
 
3780
3847
  <div class="modal-overlay" id="agentModal">
3781
- <div class="modal">
3848
+ <div class="modal modal--chat">
3782
3849
  <div class="modal__header">
3783
3850
  <div><h2 id="modalName">Agent</h2><div id="modalAgentDesc" style="font-size:10px;color:var(--dim);margin-top:2px"></div></div>
3784
3851
  <button class="modal__close" onclick="closeModal()">&times;</button>
3785
3852
  </div>
3786
3853
  <div class="modal__body">
3787
- <textarea id="modalPrompt" placeholder="Ask this agent something... (Enter to send)" onkeydown="if(event.key==='Enter'&&!event.shiftKey){askAgent();event.preventDefault();}"></textarea>
3788
- <div id="fileDropZone" style="border:2px dashed var(--border2);border-radius:6px;padding:12px;text-align:center;color:var(--dim);font-size:11px;cursor:pointer;margin-bottom:10px;transition:border-color .2s" onclick="document.getElementById('fileInput').click()" ondragover="event.preventDefault();this.style.borderColor='var(--green)'" ondragleave="this.style.borderColor='var(--border2)'" ondrop="event.preventDefault();this.style.borderColor='var(--border2)';handleFileDrop(event)">
3789
- Drop a file here or click to attach
3790
- <input type="file" id="fileInput" style="display:none" onchange="handleFileSelect(this)">
3854
+ <div class="agent-chat">
3855
+ <div class="agent-chat__messages" id="agentMessages"></div>
3856
+ <div class="agent-chat__footer">
3857
+ <div class="agent-chat__drop" id="agentFileDropZone" onclick="document.getElementById('agentFileInput').click()" ondragover="event.preventDefault();this.style.borderColor='var(--green)'" ondragleave="this.style.borderColor=''" ondrop="event.preventDefault();this.style.borderColor='';handleFileDrop(event)">
3858
+ Drop a file or click to attach
3859
+ <input type="file" id="agentFileInput" style="display:none" onchange="handleFileSelect(this)">
3860
+ </div>
3861
+ <div id="agentFileInfo" style="display:none;font-size:10px;color:var(--cyan)"></div>
3862
+ <div class="agent-chat__input-row">
3863
+ <textarea class="agent-chat__input" id="modalPrompt" rows="1" placeholder="Ask this agent... (Enter to send)" onkeydown="if(event.key==='Enter'&&!event.shiftKey){askAgent();event.preventDefault();}"></textarea>
3864
+ <button class="btn btn--primary" id="agentAskBtn" onclick="askAgent()" style="height:36px;padding:0 14px">Send</button>
3865
+ </div>
3866
+ </div>
3791
3867
  </div>
3792
- <div id="fileInfo" style="display:none;font-size:10px;color:var(--cyan);margin-bottom:8px"></div>
3793
- <div class="modal__response md-body" id="modalResponse" style="display:none"></div>
3794
- </div>
3795
- <div class="modal__footer">
3796
- <button class="btn btn--secondary" onclick="closeModal()">Close</button>
3797
- <button class="btn btn--primary" id="agentAskBtn" onclick="askAgent()">Ask</button>
3798
3868
  </div>
3799
3869
  </div>
3800
3870
  </div>