nothumanallowed 13.5.3 → 13.5.5

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.5.3",
3
+ "version": "13.5.5",
4
4
  "description": "NotHumanAllowed — 38 AI agents, 80 tools, Studio (visual agentic workflows). Email, calendar, browser automation, screen capture, canvas, cron/heartbeat, Alexandria E2E messaging, GitHub, Notion, Slack, voice chat, free AI (Liara), 28 languages. Zero-dependency CLI.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/constants.mjs CHANGED
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url';
5
5
  const __filename = fileURLToPath(import.meta.url);
6
6
  const __dirname = path.dirname(__filename);
7
7
 
8
- export const VERSION = '13.5.3';
8
+ export const VERSION = '13.5.5';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -3308,6 +3308,8 @@ var studioState = {
3308
3308
  };
3309
3309
 
3310
3310
  var studioAbortController = null;
3311
+ var parlActiveAgent = null; // active agent label during parliament streaming
3312
+ var parlDoneAgents = {}; // set of completed agent labels during parliament
3311
3313
 
3312
3314
  function stopStudio() {
3313
3315
  if (!studioState.running) return;
@@ -3605,6 +3607,205 @@ function officeRoomDecor() {
3605
3607
  \x27<div class="prl-office-plant2">\x27+plant2Svg+\x27</div>\x27;
3606
3608
  }
3607
3609
 
3610
+ // ── Isometric JRPG-style scene renderer ────────────────────────────────────
3611
+ // Projects grid positions onto an isometric plane.
3612
+ // iso(col, row) → {x, y} pixel coordinates in the scene container.
3613
+ // Characters are positioned with position:absolute, scale by row for depth.
3614
+ var ISO_TILE_W = 72;
3615
+ var ISO_TILE_H = 36;
3616
+ var ISO_ORIGIN_X = 260; // center-left of scene
3617
+ var ISO_ORIGIN_Y = 40; // top of scene
3618
+ function isoProject(col, row) {
3619
+ return {
3620
+ x: ISO_ORIGIN_X + (col - row) * (ISO_TILE_W / 2),
3621
+ y: ISO_ORIGIN_Y + (col + row) * (ISO_TILE_H / 2)
3622
+ };
3623
+ }
3624
+ // Draw isometric floor tiles as SVG
3625
+ function isoFloorSvg(cols, rows) {
3626
+ var w = 600; var h = 240;
3627
+ var out = \x27<svg viewBox="0 0 \x27+w+\x27 \x27+h+\x27" width="\x27+w+\x27" height="\x27+h+\x27" xmlns="http://www.w3.org/2000/svg" style="position:absolute;top:0;left:0;pointer-events:none;z-index:0">\x27;
3628
+ // Back wall — two side walls
3629
+ out += \x27<polygon points="0,80 300,20 300,100 0,160" fill="#1a1535" opacity=".7"/>\x27;
3630
+ out += \x27<polygon points="300,20 600,80 600,160 300,100" fill="#141030" opacity=".7"/>\x27;
3631
+ // Wall top edge
3632
+ out += \x27<line x1="0" y1="80" x2="300" y2="20" stroke="#3a3060" stroke-width="1.5"/>\x27;
3633
+ out += \x27<line x1="300" y1="20" x2="600" y2="80" stroke="#3a3060" stroke-width="1.5"/>\x27;
3634
+ // Wall window left
3635
+ out += \x27<polygon points="60,82 130,62 130,102 60,122" fill="#1a2840" stroke="#2a4060" stroke-width="1"/>\x27;
3636
+ out += \x27<polygon points="65,85 125,66 125,98 65,117" fill="#7ecfff" opacity=".15" stroke="#4a90c0" stroke-width=".5"/>\x27;
3637
+ // Wall window right
3638
+ out += \x27<polygon points="470,82 540,62 540,102 470,122" fill="#1a2840" stroke="#2a4060" stroke-width="1"/>\x27;
3639
+ out += \x27<polygon points="475,85 535,66 535,98 475,117" fill="#7ecfff" opacity=".15" stroke="#4a90c0" stroke-width=".5"/>\x27;
3640
+ // Sunlight shafts
3641
+ out += \x27<polygon points="80,102 120,90 125,160 85,175" fill="rgba(200,230,255,.04)"/>\x27;
3642
+ out += \x27<polygon points="490,90 530,102 535,175 495,160" fill="rgba(200,230,255,.04)"/>\x27;
3643
+ // Ceiling lamp
3644
+ out += \x27<ellipse cx="300" cy="22" rx="20" ry="6" fill="#2a2050" stroke="#4a3080" stroke-width="1"/>\x27;
3645
+ out += \x27<ellipse cx="300" cy="24" rx="18" ry="10" fill="rgba(255,220,100,.12)"/>\x27;
3646
+ out += \x27<line x1="300" y1="0" x2="300" y2="22" stroke="#3a3060" stroke-width="1.5"/>\x27;
3647
+ // Floor tiles — iso grid
3648
+ for (var r = 0; r < rows; r++) {
3649
+ for (var c = 0; c < cols; c++) {
3650
+ var p = isoProject(c, r);
3651
+ var tx = p.x; var ty = p.y + 80;
3652
+ // Diamond tile
3653
+ var pts = (tx)+\x27,\x27+(ty) + \x27 \x27+(tx+ISO_TILE_W/2)+\x27,\x27+(ty+ISO_TILE_H/2) + \x27 \x27+tx+\x27,\x27+(ty+ISO_TILE_H) + \x27 \x27+(tx-ISO_TILE_W/2)+\x27,\x27+(ty+ISO_TILE_H/2);
3654
+ var tileEven = (r+c) % 2 === 0;
3655
+ out += \x27<polygon points="\x27+pts+\x27" fill="\x27+(tileEven?\x27#1e1840\x27:\x27#1a1535\x27)+\x27" stroke="#2a2050" stroke-width=".8"/>\x27;
3656
+ // Tile highlight (top-left edge)
3657
+ out += \x27<line x1="\x27+(tx-ISO_TILE_W/2)+\x27" y1="\x27+(ty+ISO_TILE_H/2)+\x27" x2="\x27+tx+\x27" y2="\x27+ty+\x27" stroke="rgba(255,255,255,.04)" stroke-width=".6"/>\x27;
3658
+ }
3659
+ }
3660
+ out += \x27</svg>\x27;
3661
+ return out;
3662
+ }
3663
+ // Small isometric desk object at iso position
3664
+ function isoDeskSvg(x, y, accentColor) {
3665
+ var ac = accentColor || \x27#3a3060\x27;
3666
+ return \x27<svg viewBox="0 0 64 40" width="64" height="40" xmlns="http://www.w3.org/2000/svg" style="position:absolute;left:\x27+(x-32)+\x27px;top:\x27+(y+10)+\x27px;z-index:\x27+(Math.round(y))+\x27;pointer-events:none">\x27+
3667
+ // Desk top face
3668
+ \x27<polygon points="32,4 56,16 32,28 8,16" fill="#1e1840" stroke="\x27+ac+\x27" stroke-width="1.2"/>\x27+
3669
+ // Desk right face
3670
+ \x27<polygon points="56,16 56,32 32,44 32,28" fill="#120e28" stroke="\x27+ac+\x2788" stroke-width=".8"/>\x27+
3671
+ // Desk left face
3672
+ \x27<polygon points="8,16 32,28 32,44 8,32" fill="#161230" stroke="\x27+ac+\x2766" stroke-width=".8"/>\x27+
3673
+ // Monitor on desk
3674
+ \x27<polygon points="24,8 38,14 38,22 24,16" fill="\x27+(ac===\x27#22c55e\x27?\x27#0a2010\x27:\x27#0d0d1e\x27)+\x27" stroke="\x27+ac+\x27" stroke-width="1"/>\x27+
3675
+ \x27<polygon points="24,10 36,15 36,20 24,15" fill="\x27+(ac===\x27#22c55e\x27?\x27#0a2810\x27:\x27#111128\x27)+\x27"/>\x27+
3676
+ // Screen lines
3677
+ \x27<line x1="26" y1="12" x2="34" y2="15" stroke="\x27+ac+\x27" stroke-width=".8" opacity=".8"/>\x27+
3678
+ \x27<line x1="26" y1="14" x2="33" y2="17" stroke="\x27+ac+\x27" stroke-width=".8" opacity=".5"/>\x27+
3679
+ \x27<line x1="26" y1="16" x2="32" y2="18" stroke="\x27+ac+\x27" stroke-width=".8" opacity=".3"/>\x27+
3680
+ \x27</svg>\x27;
3681
+ }
3682
+ // Isometric JRPG sprite character — top-down 3/4 perspective, Pokemon-style
3683
+ function isoCharSvg(opts) {
3684
+ var skin = opts.skin || \x27#fbbf24\x27;
3685
+ var shirt = opts.shirt || \x27#4f46e5\x27;
3686
+ var hair = opts.hair || \x27#1a0e08\x27;
3687
+ var isActive = opts.isActive;
3688
+ var isDone = opts.isDone;
3689
+ var scale = opts.scale || 1;
3690
+ var glow = isActive ? (\x27filter:drop-shadow(0 0 6px \x27+(opts.accentColor||shirt)+\x27)\x27) : (isDone ? \x27filter:drop-shadow(0 0 4px #22c55e44)\x27 : \x27\x27);
3691
+ var armCls = isActive ? \x27class="prl-arm"\x27 : \x27\x27;
3692
+ var headCls = isActive ? \x27class="prl-head"\x27 : \x27\x27;
3693
+ var w = Math.round(48 * scale); var h = Math.round(64 * scale);
3694
+ return \x27<svg viewBox="0 0 48 64" width="\x27+w+\x27" height="\x27+h+\x27" xmlns="http://www.w3.org/2000/svg" style="\x27+glow+\x27;display:block;overflow:visible">\x27+
3695
+ // Shadow beneath character
3696
+ \x27<ellipse cx="24" cy="62" rx="\x27+(11*scale)+\x27" ry="\x27+(4*scale)+\x27" fill="rgba(0,0,0,.3)"/>\x27+
3697
+ // Legs
3698
+ \x27<path d="M18 42 C17 50 16 56 15 60 C14 62 17 63 19 62 C21 61 21 56 21 50 L21 42 Z" fill="\x27+shirt+\x27cc" class="prl-master-leg-l"/>\x27+
3699
+ \x27<path d="M24 42 C25 50 26 56 27 60 C28 62 25 63 23 62 C21 61 21 56 21 50 L21 42 Z" fill="\x27+shirt+\x27cc" class="prl-master-leg-r"/>\x27+
3700
+ // Shoes
3701
+ \x27<ellipse cx="16" cy="62" rx="4" ry="2" fill="#1a0e04" transform="rotate(-10 16 62)"/>\x27+
3702
+ \x27<ellipse cx="26" cy="62" rx="4" ry="2" fill="#1a0e04" transform="rotate(10 26 62)"/>\x27+
3703
+ // Body
3704
+ \x27<rect x="13" y="26" width="20" height="18" rx="3" fill="\x27+shirt+\x27"/>\x27+
3705
+ \x27<rect x="13" y="26" width="10" height="18" rx="3" fill="rgba(0,0,0,.1)"/>\x27+
3706
+ // Collar
3707
+ \x27<path d="M21 26 L18 30 L21 28 L24 30 Z" fill="\x27+skin+\x27"/>\x27+
3708
+ // Arms
3709
+ \x27<g \x27+armCls+\x27>\x27+
3710
+ \x27<path d="M13 28 C8 30 6 35 6 39 C6 42 9 43 11 42 C13 41 13 37 14 33 Z" fill="\x27+shirt+\x27"/>\x27+
3711
+ \x27<ellipse cx="7" cy="41" rx="4" ry="3" fill="\x27+skin+\x27" transform="rotate(-15 7 41)"/>\x27+
3712
+ \x27<path d="M33 28 C38 30 40 35 40 39 C40 42 37 43 35 42 C33 41 33 37 32 33 Z" fill="\x27+shirt+\x27"/>\x27+
3713
+ \x27<ellipse cx="39" cy="41" rx="4" ry="3" fill="\x27+skin+\x27" transform="rotate(15 39 41)"/>\x27+
3714
+ \x27</g>\x27+
3715
+ // Head
3716
+ \x27<g \x27+headCls+\x27>\x27+
3717
+ \x27<ellipse cx="21" cy="13" rx="11" ry="12" fill="\x27+skin+\x27"/>\x27+
3718
+ \x27<path d="M10 13 C10 6 14 1 21 0 C28 1 32 6 32 13 C31 7 28 4 21 3 C14 4 11 7 10 13" fill="\x27+hair+\x27"/>\x27+
3719
+ \x27<circle cx="16" cy="14" r="1.5" fill="#0a0a14"/>\x27+
3720
+ \x27<circle cx="26" cy="14" r="1.5" fill="#0a0a14"/>\x27+
3721
+ \x27<circle cx="16.7" cy="13" r=".7" fill="rgba(255,255,255,.9)"/>\x27+
3722
+ (isDone ?
3723
+ \x27<path d="M15 20 Q21 24 27 20" stroke="#8b4513" stroke-width="1.5" fill="none" stroke-linecap="round"/>\x27+
3724
+ \x27<path d="M16 20.5 Q21 23 26 20.5 Q21 22 16 20.5" fill="#fff" opacity=".6"/>\x27
3725
+ : isActive ?
3726
+ \x27<path d="M15 19 Q21 21 27 19" stroke="#8b4513" stroke-width="1.4" fill="none" stroke-linecap="round"/>\x27
3727
+ :
3728
+ \x27<path d="M16 20 Q21 22 26 20" stroke="#8b4513" stroke-width="1.2" fill="none" stroke-linecap="round"/>\x27
3729
+ )+
3730
+ \x27</g>\x27+
3731
+ \x27</svg>\x27;
3732
+ }
3733
+ // Orchestrator — bigger, crowned, JRPG boss sprite
3734
+ function isoOrchSvg(hasActive, doneRatio) {
3735
+ var mc = \x27#818cf8\x27;
3736
+ var armCls = hasActive ? \x27class="prl-master-arm-l"\x27 : \x27\x27;
3737
+ var armRCls = hasActive ? \x27class="prl-master-arm-r"\x27 : \x27\x27;
3738
+ return \x27<svg viewBox="0 0 60 80" width="58" height="76" xmlns="http://www.w3.org/2000/svg" style="display:block;overflow:visible;filter:drop-shadow(0 0 10px \x27+mc+\x27aa)">\x27+
3739
+ // Shadow
3740
+ \x27<ellipse cx="30" cy="78" rx="16" ry="5" fill="rgba(0,0,0,.4)"/>\x27+
3741
+ // Legs
3742
+ \x27<path d="M24 52 C23 60 22 67 21 72 C20 75 22 76 24 76 C26 76 27 74 27 71 C28 65 28 58 28 52 Z" fill="#1e1c4a" class="prl-master-leg-l"/>\x27+
3743
+ \x27<path d="M30 52 C31 60 32 67 33 72 C34 75 32 76 30 76 C28 76 27 74 27 71 C26 65 26 58 26 52 Z" fill="#1e1c4a" class="prl-master-leg-r"/>\x27+
3744
+ // Shoes
3745
+ \x27<ellipse cx="20" cy="74" rx="5" ry="2.5" fill="#0a0a14" transform="rotate(-8 20 74)"/>\x27+
3746
+ \x27<ellipse cx="34" cy="74" rx="5" ry="2.5" fill="#0a0a14" transform="rotate(8 34 74)"/>\x27+
3747
+ // Suit body
3748
+ \x27<path d="M14 32 C13 30 16 27 27 25 C38 27 41 30 40 32 L41 52 L13 52 Z" fill="#252464"/>\x27+
3749
+ \x27<path d="M14 32 C13 30 16 27 27 25 L27 52 L13 52 Z" fill="#1e1d52"/>\x27+
3750
+ \x27<path d="M27 25 C38 27 41 30 40 32 L41 52 L27 52 Z" fill="#2a2870"/>\x27+
3751
+ // Lapels
3752
+ \x27<path d="M27 25 L21 33 L24 36 L27 29 Z" fill="#1a1940"/>\x27+
3753
+ \x27<path d="M27 25 L33 33 L30 36 L27 29 Z" fill="#1a1940"/>\x27+
3754
+ \x27<path d="M27 29 L24 36 L27 34 L30 36 Z" fill="#f0f0fa"/>\x27+
3755
+ // Tie
3756
+ \x27<path d="M27 33 L26 44 L27 48 L28 44 Z" fill="\x27+mc+\x27"/>\x27+
3757
+ // Arms
3758
+ \x27<g \x27+armCls+\x27 style="transform-origin:14px 35px">\x27+
3759
+ \x27<path d="M14 35 C8 38 5 44 5 48 C5 51 8 52 10 51 C12 50 13 46 14 42 C15 39 14 36 14 35" fill="#252450"/>\x27+
3760
+ \x27<ellipse cx="6" cy="51" rx="5" ry="3.5" fill="#d4a97a" transform="rotate(-15 6 51)"/>\x27+
3761
+ \x27</g>\x27+
3762
+ \x27<g \x27+armRCls+\x27 style="transform-origin:40px 35px">\x27+
3763
+ \x27<path d="M40 35 C46 38 49 43 49 47 C49 50 46 51 44 50 C42 49 41 45 40 41 C39 38 40 36 40 35" fill="#252450"/>\x27+
3764
+ // Clipboard
3765
+ \x27<rect x="46" y="30" width="12" height="16" rx="2" fill="#1a1a2e" stroke="\x27+mc+\x2799" stroke-width="1.2"/>\x27+
3766
+ \x27<rect x="49" y="28" width="6" height="4" rx="1" fill="\x27+mc+\x27"/>\x27+
3767
+ \x27<line x1="48" y1="34" x2="56" y2="34" stroke="\x27+mc+\x27cc" stroke-width=".9"/>\x27+
3768
+ \x27<line x1="48" y1="37" x2="56" y2="37" stroke="\x27+mc+\x27aa" stroke-width=".9"/>\x27+
3769
+ \x27<line x1="48" y1="40" x2="54" y2="40" stroke="\x27+mc+\x2788" stroke-width=".9"/>\x27+
3770
+ \x27</g>\x27+
3771
+ // Neck
3772
+ \x27<rect x="24" y="25" width="6" height="5" rx="2" fill="#d4a97a"/>\x27+
3773
+ // Head
3774
+ \x27<ellipse cx="27" cy="14" rx="13" ry="13" fill="#d4a97a"/>\x27+
3775
+ \x27<path d="M14 14 C14 20 18 24 27 25 C36 24 40 20 40 14" fill="#d4a97a"/>\x27+
3776
+ // Hair
3777
+ \x27<path d="M14 14 C13 7 17 2 27 1 C37 2 41 7 40 14 C39 7 35 4 27 3 C19 4 15 7 14 14" fill="#1a0e08"/>\x27+
3778
+ // Eyes
3779
+ \x27<ellipse cx="22" cy="15" rx="3" ry="3.5" fill="#fff"/>\x27+
3780
+ \x27<ellipse cx="32" cy="15" rx="3" ry="3.5" fill="#fff"/>\x27+
3781
+ \x27<circle cx="22" cy="15.5" r="2" fill="#1e3a6e"/>\x27+
3782
+ \x27<circle cx="32" cy="15.5" r="2" fill="#1e3a6e"/>\x27+
3783
+ \x27<circle cx="22" cy="15.5" r="1.2" fill="#0a0a18"/>\x27+
3784
+ \x27<circle cx="32" cy="15.5" r="1.2" fill="#0a0a18"/>\x27+
3785
+ \x27<circle cx="23" cy="14" r=".8" fill="rgba(255,255,255,.9)"/>\x27+
3786
+ (hasActive ?
3787
+ \x27<path d="M21 21 Q27 25 33 21" stroke="#8b4513" stroke-width="1.8" fill="none" stroke-linecap="round"/>\x27+
3788
+ \x27<ellipse cx="27" cy="22.5" rx="3.5" ry="2" fill="#5a1a0a" opacity=".6"/>\x27
3789
+ :
3790
+ \x27<path d="M21 22 Q27 25 33 22" stroke="#8b4513" stroke-width="1.6" fill="none" stroke-linecap="round"/>\x27
3791
+ )+
3792
+ // Crown
3793
+ \x27<path d="M17 4 L20 9 L27 6 L34 9 L37 4 L27 1 Z" fill="#f59e0b"/>\x27+
3794
+ \x27<circle cx="20" cy="9" r="2" fill="#fbbf24"/>\x27+
3795
+ \x27<circle cx="27" cy="6" r="2" fill="#fbbf24"/>\x27+
3796
+ \x27<circle cx="34" cy="9" r="2" fill="#fbbf24"/>\x27+
3797
+ \x27</svg>\x27;
3798
+ }
3799
+
3800
+ // Skin/shirt/hair palette per agent label
3801
+ function agentPalette(lbl) {
3802
+ var skins = [\x27#fbbf24\x27,\x27#f97316\x27,\x27#e8c99a\x27,\x27#c8a97a\x27,\x27#d4a97a\x27,\x27#f5c07a\x27];
3803
+ var shirts = [\x27#4f46e5\x27,\x27#0891b2\x27,\x27#7c3aed\x27,\x27#059669\x27,\x27#dc2626\x27,\x27#d97706\x27];
3804
+ var hairs = [\x27#1a0e08\x27,\x27#4a3728\x27,\x27#c4a35a\x27,\x27#8b0000\x27,\x27#2c4a7c\x27,\x27#3d2b1f\x27];
3805
+ var i = Math.abs((lbl.charCodeAt(0)||65) + (lbl.charCodeAt(lbl.length-1)||90)) % 6;
3806
+ return {skin: skins[i], shirt: shirts[i], hair: hairs[i]};
3807
+ }
3808
+
3608
3809
  function renderStudioNodes() {
3609
3810
  var el = document.getElementById('studioNodes');
3610
3811
  if (!el) return;
@@ -3612,138 +3813,94 @@ function renderStudioNodes() {
3612
3813
  el.innerHTML = '<div class="studio-canvas__empty"><div class="studio-canvas__empty-icon">&#9881;</div><div>Describe a task and click Run</div></div>';
3613
3814
  return;
3614
3815
  }
3615
- // ── Parliament-style office scene ──────────────────────────────────────────
3616
- // Renders all pipeline nodes as the same parliament office scene:
3617
- // agents at desks, orchestrator walking + sbraita (speech bubble yelling).
3618
- var hasActive = studioState.nodes.some(function(n){ return n.status === \x27running\x27; });
3619
- var hasDone = studioState.nodes.some(function(n){ return n.status === \x27done\x27; });
3620
- var showMaster = hasActive || hasDone;
3621
-
3622
- // Sbraita phrases — orchestrator yells at agents
3623
- var sbraitaPhrases = [
3624
- \x27PIU VELOCE!!!\x27, \x27FOCUS!!!\x27, \x27DEADLINE!!!\x27, \x27CHE FAI??!\x27,
3625
- \x27MUOVITI!!\x27, \x27NO ERRORI!\x27, \x27ADESSO!!\x27, \x27SBRIGATI!\x27
3626
- ];
3627
- var activeNode = studioState.nodes.find(function(n){ return n.status === \x27running\x27; });
3628
- var sbIdx = activeNode ? Math.abs((activeNode.label||activeNode.agent||\x27x\x27).charCodeAt(0)) % sbraitaPhrases.length : 0;
3629
- var sbraitaText = sbraitaPhrases[sbIdx];
3630
3816
 
3631
- // Build desks row — same card structure as parliament
3632
- var desksHtml2 = \x27\x27;
3633
- studioState.nodes.forEach(function(n) {
3817
+ var nodes = studioState.nodes;
3818
+ var hasActive = nodes.some(function(n){ return n.status === \x27running\x27; });
3819
+ var doneCount = nodes.filter(function(n){ return n.status === \x27done\x27; }).length;
3820
+ var totalCount = nodes.length;
3821
+ var showMaster = hasActive || doneCount > 0;
3822
+
3823
+ // ── ISO scene dimensions ───────────────────────────────────────────────────
3824
+ var ISO_COLS = Math.max(nodes.length, 1);
3825
+ var SCENE_W = 600; var SCENE_H = 260;
3826
+
3827
+ // Distribute agents along row=1 of the iso grid, spaced evenly
3828
+ // Orchestrator walks on row=0 (further back = smaller)
3829
+ var agentsHtml = \x27\x27;
3830
+ nodes.forEach(function(n, idx) {
3634
3831
  var isActive = n.status === \x27running\x27;
3635
3832
  var isDone = n.status === \x27done\x27;
3636
3833
  var isErr = n.status === \x27error\x27;
3637
- desksHtml2 += \x27<div class="prl-desk\x27+(isActive?\x27 prl-desk--active\x27:\x27\x27)+(isDone?\x27 wf-desk--done\x27:\x27\x27)+(isErr?\x27 wf-desk--err\x27:\x27\x27)+\x27" style="--dc:#6366f1;min-width:78px;cursor:pointer" data-agent-label="\x27+esc(n.label||n.agent)+\x27" 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 \x27+esc(n.label||n.agent)+\x27">\x27;
3638
- if (isActive) desksHtml2 += \x27<div class="prl-action-bubble prl-action-bubble--active" style="font-size:8px;padding:2px 6px">\u2026lavora</div>\x27;
3639
- else if (isDone) desksHtml2 += \x27<div class="prl-action-bubble" style="background:#0a2010;border-color:#22c55e;color:#4ade80;font-size:8px;padding:2px 6px">\u2714 fatto</div>\x27;
3640
- else if (isErr) desksHtml2 += \x27<div class="prl-action-bubble" style="background:#200a0a;border-color:#ef4444;color:#f87171;font-size:8px;padding:2px 6px">\u2715 errore</div>\x27;
3641
- else desksHtml2 += \x27<div class="prl-action-bubble" style="font-size:8px;padding:2px 6px;opacity:.5">in attesa</div>\x27;
3642
- desksHtml2 += buildWorkflowChar(n);
3643
- desksHtml2 += \x27<div class="prl-desk-name" style="color:\x27+(isDone?\x27#4ade80\x27:(isActive?\x27#a5b4fc\x27:(isErr?\x27#f87171\x27:\x27#6b7280\x27)))+\x27;max-width:78px;white-space:normal;word-break:break-word;text-align:center" title="\x27+esc(n.label)+\x27">\x27+esc(n.label)+\x27</div>\x27;
3644
- desksHtml2 += \x27</div>\x27;
3645
- n._rendered = true;
3834
+ var lbl = n.label || n.agent;
3835
+ var pal = agentPalette(lbl);
3836
+ var accentColor = isActive ? \x27#6366f1\x27 : (isDone ? \x27#22c55e\x27 : (isErr ? \x27#ef4444\x27 : \x27#3a3060\x27));
3837
+
3838
+ // Iso position: spread across columns, agents sit at row=2 (front row)
3839
+ var col = (idx + 0.5) * (ISO_COLS > 1 ? (ISO_COLS - 1) / ISO_COLS : 1) + 0.5;
3840
+ col = (idx * (ISO_COLS <= 1 ? 2 : (4 / ISO_COLS))) + 1;
3841
+ var row = 2.5;
3842
+ var pos = isoProject(col, row);
3843
+ var px = pos.x; var py = pos.y + 40;
3844
+ // scale: row 2.5 = 1.0, background rows smaller
3845
+ var charScale = 0.9 + row * 0.08;
3846
+ var zIdx = Math.round(py + 100);
3847
+
3848
+ // Speech bubble content
3849
+ var bubbleText = \x27\x27;
3850
+ var bubbleColor = \x27#6366f1\x27;
3851
+ if (isActive) { bubbleText = \x27...lavora\x27; bubbleColor = \x27#6366f1\x27; }
3852
+ else if (isDone) { bubbleText = \x27\u2714 fatto\x27; bubbleColor = \x27#22c55e\x27; }
3853
+ else if (isErr) { bubbleText = \x27\u2715 errore\x27; bubbleColor = \x27#ef4444\x27; }
3854
+ else { bubbleText = \x27in attesa\x27; bubbleColor = \x27#4a4a6a\x27; }
3855
+
3856
+ var charSvg = isoCharSvg({skin: pal.skin, shirt: pal.shirt, hair: pal.hair,
3857
+ isActive: isActive, isDone: isDone, scale: charScale, accentColor: accentColor});
3858
+
3859
+ var charW = Math.round(48 * charScale);
3860
+ var charH = Math.round(64 * charScale);
3861
+
3862
+ agentsHtml += \x27<div class="iso-agent" data-agent-label="\x27+esc(lbl)+\x27" style="position:absolute;left:\x27+(px - charW/2)+\x27px;top:\x27+(py - charH)+\x27px;z-index:\x27+zIdx+\x27;display:flex;flex-direction:column;align-items:center;gap:2px;cursor:pointer" onclick="studioScrollToAgent(this.getAttribute(String.fromCharCode(100,97,116,97,45,97,103,101,110,116,45,108,97,98,101,108)))">\x27;
3863
+ // Thought bubble above head
3864
+ agentsHtml += \x27<div class="iso-bubble\x27+(isActive?\x27 iso-bubble--active\x27:\x27\x27)+\x27" id="isobubble_\x27+idx+\x27" style="border-color:\x27+bubbleColor+\x27;color:\x27+bubbleColor+\x27">\x27+esc(bubbleText)+\x27</div>\x27;
3865
+ agentsHtml += charSvg;
3866
+ // Name tag
3867
+ agentsHtml += \x27<div class="iso-name" style="color:\x27+(isDone?\x27#4ade80\x27:(isActive?\x27#a5b4fc\x27:(isErr?\x27#f87171\x27:\x27#6b7280\x27)))+\x27">\x27+esc(lbl)+\x27</div>\x27;
3868
+ // Iso desk below character
3869
+ agentsHtml += isoDeskSvg(px, py - charH/2, accentColor);
3870
+ agentsHtml += \x27</div>\x27;
3646
3871
  });
3647
3872
 
3648
- // ── Master orchestrator SVG ─────────────────────────────────────────────────
3649
- var masterColor3 = \x27#818cf8\x27;
3650
- var masterSvg2 = \x27<svg viewBox="0 0 60 90" width="52" height="78" xmlns="http://www.w3.org/2000/svg" font-family="system-ui,sans-serif" style="filter:drop-shadow(0 0 10px \x27+masterColor3+\x27aa)">\x27+
3651
- \x27<path d="M22 55 C21 63 19 72 18 77 C17 80 18 82 21 82 C23 82 24 80 24 77 C25 71 25 62 26 55 Z" fill="#1e1c4a" class="prl-master-leg-l"/>\x27+
3652
- \x27<path d="M28 55 C29 63 31 72 32 77 C33 80 32 82 29 82 C27 82 26 80 26 77 C25 71 25 62 24 55 Z" fill="#1e1c4a" class="prl-master-leg-r"/>\x27+
3653
- \x27<path d="M16 79 C14 79 13 81 14 83 C15 85 18 85 21 84 C23 83 24 82 23 80 C22 79 19 79 16 79" fill="#0a0a14"/>\x27+
3654
- \x27<path d="M34 79 C36 79 37 81 36 83 C35 85 32 85 29 84 C27 83 26 82 27 80 C28 79 31 79 34 79" fill="#0a0a14"/>\x27+
3655
- \x27<path d="M13 32 C12 30 15 27 25 25 C35 27 38 30 37 32 L38 55 L12 55 Z" fill="#252450"/>\x27+
3656
- \x27<path d="M13 32 C12 30 15 27 25 25 L25 55 L12 55 Z" fill="#1e1d44"/>\x27+
3657
- \x27<path d="M25 25 C35 27 38 30 37 32 L38 55 L25 55 Z" fill="#272660"/>\x27+
3658
- \x27<path d="M25 25 L19 33 L22 36 L25 29 Z" fill="#1a1940"/>\x27+
3659
- \x27<path d="M25 25 L31 33 L28 36 L25 29 Z" fill="#1a1940"/>\x27+
3660
- \x27<path d="M25 29 L22 36 L25 34 L28 36 Z" fill="#f0f0fa"/>\x27+
3661
- \x27<path d="M25 33 L24 44 L25 48 L26 44 Z" fill="\x27+masterColor3+\x27"/>\x27+
3662
- \x27<path d="M23.5 32 L26.5 32 L25 34 Z" fill="\x27+masterColor3+\x27"/>\x27+
3663
- \x27<path d="M33 35 L36 33 L37 36 L34 37 Z" fill="\x27+masterColor3+\x2799"/>\x27+
3664
- \x27<circle cx="25" cy="42" r="1.2" fill="\x27+masterColor3+\x27aa"/>\x27+
3665
- \x27<circle cx="20" cy="36" r="2.5" fill="#0d0d1e" stroke="\x27+masterColor3+\x2799" stroke-width="1"/>\x27+
3666
- \x27<text x="20" y="39.5" text-anchor="middle" font-size="4" fill="\x27+masterColor3+\x27" font-family="system-ui,sans-serif">N</text>\x27+
3667
- \x27<g class="prl-master-arm-l">\x27+
3668
- \x27<path d="M13 34 C8 37 6 42 6 46 C6 49 9 50 11 49 C13 48 13 45 14 41 C15 38 14 35 13 34" fill="#252450"/>\x27+
3669
- \x27<path d="M6 46 C4 48 4 51 5 53 C6 55 9 55 10 53 C11 51 10 48 10 46 Z" fill="#d4a97a"/>\x27+
3670
- \x27<ellipse cx="7" cy="54" rx="4.5" ry="3.5" fill="#d4a97a" transform="rotate(-15 7 54)"/>\x27+
3671
- \x27</g>\x27+
3672
- \x27<g class="prl-master-arm-r">\x27+
3673
- \x27<path d="M37 34 C42 37 44 41 44 45 C44 48 41 49 39 48 C37 47 37 44 37 40 C37 37 37 35 37 34" fill="#252450"/>\x27+
3674
- \x27<path d="M44 44 C46 46 47 49 46 52 C45 54 42 54 41 52 C40 50 41 47 41 45 Z" fill="#d4a97a"/>\x27+
3675
- \x27<ellipse cx="45" cy="52" rx="4" ry="3" fill="#d4a97a" transform="rotate(15 45 52)"/>\x27+
3676
- \x27<rect x="43" y="32" width="14" height="19" rx="2.5" fill="#1a1a2e" stroke="\x27+masterColor3+\x2799" stroke-width="1.5"/>\x27+
3677
- \x27<rect x="47" y="30" width="6" height="5" rx="1.5" fill="\x27+masterColor3+\x27"/>\x27+
3678
- \x27<rect x="48" y="31" width="4" height="3" rx="1" fill="#0f0f1e"/>\x27+
3679
- \x27<line x1="46" y1="36" x2="54" y2="36" stroke="\x27+masterColor3+\x27cc" stroke-width="1"/>\x27+
3680
- \x27<line x1="46" y1="39" x2="54" y2="39" stroke="\x27+masterColor3+\x27aa" stroke-width="1"/>\x27+
3681
- \x27<line x1="46" y1="42" x2="54" y2="42" stroke="\x27+masterColor3+\x2788" stroke-width="1"/>\x27+
3682
- \x27</g>\x27+
3683
- \x27<path d="M22 25 L28 25 L28 29 Q28 31 25 31 Q22 31 22 29 Z" fill="#d4a97a"/>\x27+
3684
- \x27<ellipse cx="25" cy="15" rx="12" ry="13" fill="#d4a97a"/>\x27+
3685
- \x27<path d="M14 15 C14 22 18 26 25 27 C32 26 36 22 36 15" fill="#d4a97a"/>\x27+
3686
- \x27<path d="M13 13 C11 13 10 15 10 17 C10 19 11 20 13 20 C14 20 14.5 19 14 17 C14.5 15 14 13 13 13" fill="#d4a97a"/>\x27+
3687
- \x27<path d="M37 13 C39 13 40 15 40 17 C40 19 39 20 37 20 C36 20 35.5 19 36 17 C35.5 15 36 13 37 13" fill="#d4a97a"/>\x27+
3688
- \x27<path d="M13 14 C12 7 16 2 25 1 C34 2 38 7 37 14 C36 7 32 4 25 3 C18 4 14 7 13 14" fill="#1a0e08"/>\x27+
3689
- \x27<path d="M22 3 C21 4 21 6 22 8" stroke="rgba(255,255,255,.15)" stroke-width="1.5" fill="none"/>\x27+
3690
- \x27<path d="M16.5 11 Q19 9.5 21.5 11" stroke="#1a0e08" stroke-width="1.8" fill="none" stroke-linecap="round"/>\x27+
3691
- \x27<path d="M28.5 11 Q31 9.5 33.5 11" stroke="#1a0e08" stroke-width="1.8" fill="none" stroke-linecap="round"/>\x27+
3692
- \x27<ellipse cx="19.5" cy="14.5" rx="3.2" ry="3.5" fill="#fff"/>\x27+
3693
- \x27<ellipse cx="30.5" cy="14.5" rx="3.2" ry="3.5" fill="#fff"/>\x27+
3694
- \x27<circle cx="19.5" cy="15" r="2.3" fill="#1e3a6e"/>\x27+
3695
- \x27<circle cx="30.5" cy="15" r="2.3" fill="#1e3a6e"/>\x27+
3696
- \x27<circle cx="19.5" cy="15" r="1.3" fill="#0a0a18"/>\x27+
3697
- \x27<circle cx="30.5" cy="15" r="1.3" fill="#0a0a18"/>\x27+
3698
- \x27<circle cx="20.5" cy="13.7" r=".9" fill="rgba(255,255,255,.95)"/>\x27+
3699
- \x27<circle cx="31.5" cy="13.7" r=".9" fill="rgba(255,255,255,.95)"/>\x27+
3700
- // Angry expression when active (furrowed brows + open mouth)
3701
- (hasActive ?
3702
- \x27<path d="M19.5 23 Q25 26 30.5 23" stroke="#8b4513" stroke-width="1.8" fill="none" stroke-linecap="round"/>\x27+
3703
- \x27<path d="M20 23.5 Q25 26 30 23.5 Q25 25 20 23.5" fill="#fff" opacity=".6"/>\x27+
3704
- // Open mouth / yelling
3705
- \x27<ellipse cx="25" cy="24.5" rx="3" ry="1.5" fill="#5a1a0a" opacity=".7"/>\x27
3706
- :
3707
- \x27<path d="M19.5 24 Q25 27.5 30.5 24" stroke="#8b4513" stroke-width="1.8" fill="none" stroke-linecap="round"/>\x27+
3708
- \x27<path d="M20 24.5 Q25 27 30 24.5 Q25 26.5 20 24.5" fill="#fff" opacity=".7"/>\x27
3709
- )+
3710
- \x27<circle cx="25" cy="-4" r="9" fill="\x27+masterColor3+\x2722" stroke="\x27+masterColor3+\x2766" stroke-width="1.5"/>\x27+
3711
- \x27<path d="M25 -13 L30 -4 L25 5 L20 -4 Z" fill="\x27+masterColor3+\x27" stroke="\x27+masterColor3+\x27cc" stroke-width=".8"/>\x27+
3712
- \x27<path d="M25 -13 L30 -4 L25 -3 L20 -4 Z" fill="\x27+masterColor3+\x27aa"/>\x27+
3713
- \x27</svg>\x27;
3873
+ // ── Orchestrator ──────────────────────────────────────────────────────────
3874
+ var orchHtml = \x27\x27;
3875
+ if (showMaster) {
3876
+ var oPh = isoProject(ISO_COLS / 2, 0.5);
3877
+ var orchPhrase = [\x27Analizzo il progresso...\x27, \x27Verifico gli step...\x27, \x27Coordinamento...\x27, \x27Supervisione in corso\x27][Math.floor(Date.now()/3000)%4];
3878
+ var orchDone = !hasActive && doneCount === totalCount;
3879
+ var orchAnim = hasActive ? \x27prl-master-walk\x27 : (orchDone ? \x27prl-master-done\x27 : \x27\x27);
3880
+ var orchStatus = hasActive
3881
+ ? (\x27Eseguiti \x27+doneCount+\x27/\x27+totalCount)
3882
+ : (orchDone ? \x27Workflow completato!\x27 : \x27In pianificazione\x27);
3883
+ orchHtml = \x27<div class="prl-master \x27+orchAnim+\x27" id="wfOrch" style="position:absolute;left:\x27+(oPh.x+30)+\x27px;top:\x27+(oPh.y-10)+\x27px;z-index:50;display:flex;flex-direction:column;align-items:center;gap:2px">\x27+
3884
+ \x27<div class="iso-bubble iso-bubble--orch" id="wfOrchBubble" style="border-color:#818cf8;color:#a5b4fc;max-width:120px">\x27+esc(orchStatus)+\x27</div>\x27+
3885
+ isoOrchSvg(hasActive, doneCount / Math.max(totalCount,1))+
3886
+ \x27<div class="prl-master-label" style="color:#818cf8;font-size:8px">Orchestratore</div>\x27+
3887
+ \x27</div>\x27;
3888
+ }
3714
3889
 
3715
- // Speech bubble "sbraita" when there is an active agent
3716
- var bubbleHtml = showMaster && hasActive
3717
- ? (\x27<div class="wf-sbraita-bubble">\x27+sbraitaText+\x27</div>\x27)
3718
- : \x27\x27;
3719
-
3720
- // Phase label
3721
- var doneCount = studioState.nodes.filter(function(n){return n.status===\x27done\x27;}).length;
3722
- var totalCount = studioState.nodes.length;
3723
- var wfMasterAnim = hasActive ? \x27prl-master-walk\x27 : (doneCount===totalCount && totalCount>0 ? \x27prl-master-done\x27 : \x27prl-master-walk\x27);
3724
- var masterWfHtml = showMaster ? (
3725
- \x27<div class="prl-master \x27+wfMasterAnim+\x27 wf-master" style="bottom:20px">\x27+
3726
- bubbleHtml+
3727
- masterSvg2+
3728
- \x27<div class="prl-master-label" style="color:\x27+masterColor3+\x27;font-size:8px">Orchestratore</div>\x27+
3729
- \x27</div>\x27
3730
- ) : \x27\x27;
3731
3890
  var phaseLabel2 = hasActive
3732
- ? (\x27Workflow in esecuzione \u2014 \x27+doneCount+\x27/\x27+totalCount+\x27 completati\x27)
3891
+ ? (\x27Workflow in esecuzione \u2014 \x27+doneCount+\x27/\x27+totalCount)
3733
3892
  : (doneCount===totalCount && totalCount>0 ? \x27Workflow completato\x27 : \x27Workflow pianificato\x27);
3734
3893
  var phaseColor2 = hasActive ? \x27#6366f1\x27 : (doneCount===totalCount && totalCount>0 ? \x27#22c55e\x27 : \x27#6b7280\x27);
3735
3894
 
3736
- var html = \x27<div class="prl-wrap" style="border-color:\x27+phaseColor2+\x2744;padding-bottom:16px">\x27+
3895
+ el.innerHTML =
3896
+ \x27<div class="prl-wrap" style="border-color:\x27+phaseColor2+\x2744;padding-bottom:8px">\x27+
3737
3897
  \x27<div class="prl-header"><span class="prl-phase-chip" style="--pc:\x27+phaseColor2+\x27">\x27+phaseLabel2+\x27</span></div>\x27+
3738
- \x27<div class="prl-office wf-office" style="min-height:160px">\x27+
3739
- officeRoomDecor()+
3740
- \x27<div class="prl-office-floor"></div>\x27+
3741
- \x27<div class="prl-desks-row" style="justify-content:center;flex-wrap:wrap;gap:10px;padding-bottom:8px;position:relative;z-index:2">\x27+desksHtml2+\x27</div>\x27+
3742
- masterWfHtml+
3898
+ \x27<div class="iso-scene" style="position:relative;width:\x27+SCENE_W+\x27px;height:\x27+SCENE_H+\x27px;overflow:hidden;border-radius:10px">\x27+
3899
+ isoFloorSvg(ISO_COLS + 2, 4)+
3900
+ agentsHtml+
3901
+ orchHtml+
3743
3902
  \x27</div>\x27+
3744
3903
  \x27</div>\x27;
3745
-
3746
- el.innerHTML = html;
3747
3904
  }
3748
3905
 
3749
3906
  function studioScrollToAgent(agentLabel) {
@@ -4193,6 +4350,8 @@ async function runStudio() {
4193
4350
  studioState.running = true;
4194
4351
  studioState.planned = false;
4195
4352
  // Keep attachmentContext — it was loaded before hitting Run
4353
+ parlActiveAgent = null;
4354
+ parlDoneAgents = {};
4196
4355
  var parlBlockEl = document.getElementById('studioParliamentBlock');
4197
4356
  if (parlBlockEl) parlBlockEl.style.display = 'none';
4198
4357
  renderStudioNodes();
@@ -4257,7 +4416,7 @@ async function runStudio() {
4257
4416
  for (var i = 0; i < studioState.nodes.length; i++) {
4258
4417
  var node = studioState.nodes[i];
4259
4418
  studioSetNodeStatus(i, 'running');
4260
- studioLog(node.label, node.icon, 'Starting...', 'agent');
4419
+ studioLog(node.label, node.icon, t('starting_agent') || 'Elaborazione in corso...', 'agent');
4261
4420
 
4262
4421
  if (!studioState.running) break; // stopped by user
4263
4422
  var stepResult = await runStudioStep(i, node, task, context, planRes.steps[i], studioAbortController ? studioAbortController.signal : null);
@@ -4324,9 +4483,9 @@ async function runStudio() {
4324
4483
  renderStudioNodes();
4325
4484
 
4326
4485
  // ── Parliament visual block ──────────────────────────────────────
4327
- // Track active R2 agent for visual block
4328
- var parlActiveAgent = null;
4329
- var parlDoneAgents = {};
4486
+ // Track active R2 agent for visual block (module-level vars)
4487
+ parlActiveAgent = null;
4488
+ parlDoneAgents = {};
4330
4489
 
4331
4490
  // ── Parliament boardroom: first call builds DOM, subsequent calls only update state ──
4332
4491
  var parlBlockBuilt = false;
@@ -4352,7 +4511,7 @@ async function runStudio() {
4352
4511
  // Subsequent calls ONLY update agent states (no innerHTML overwrite).
4353
4512
  // ─────────────────────────────────────────────────────────────────────
4354
4513
 
4355
- if (!parlBlockBuilt) {
4514
+ if (!parlBlockBuilt || !pb.innerHTML.trim()) {
4356
4515
  parlBlockBuilt = true;
4357
4516
 
4358
4517
  // Build agent seat HTML (box + avatar)
@@ -5361,10 +5520,55 @@ function runStudioStep(idx, node, task, context, stepDef, signal) {
5361
5520
  tb.textContent = st;
5362
5521
  }
5363
5522
  } else {
5364
- // Live token counter shows progress without raw content
5365
- var chars = output.length;
5366
- var toks = Math.ceil(chars / 4);
5367
- tb.innerHTML = \x27<span style="color:var(--green);font-family:var(--mono);font-size:10px">&#9679; Generating\u2026 \x27 + toks + \x27 token</span>\x27;
5523
+ // Word-by-word streaming: APPEND new chars, never overwrite
5524
+ var renderedLen = parseInt(tb.getAttribute(String.fromCharCode(100,97,116,97,45,114,108,101,110)) || \x270\x27, 10);
5525
+ if (renderedLen === 0) {
5526
+ // First token: set up container + cursor
5527
+ tb.innerHTML = \x27\x27;
5528
+ tb.setAttribute(String.fromCharCode(100,97,116,97,45,114,108,101,110), \x270\x27);
5529
+ var streamSpan = document.createElement(\x27span\x27);
5530
+ streamSpan.id = \x27streamText_\x27 + idx;
5531
+ streamSpan.style.cssText = \x27font-size:12px;color:var(--text);line-height:1.6;white-space:pre-wrap;word-break:break-word;font-family:var(--font)\x27;
5532
+ tb.appendChild(streamSpan);
5533
+ var cursorEl = document.createElement(\x27span\x27);
5534
+ cursorEl.id = \x27streamCursor_\x27 + idx;
5535
+ cursorEl.style.cssText = \x27display:inline-block;width:2px;height:13px;background:var(--green);margin-left:1px;vertical-align:text-bottom;animation:streamBlink .7s step-end infinite\x27;
5536
+ tb.appendChild(cursorEl);
5537
+ }
5538
+ // Append only newly arrived chars
5539
+ var newChars = output.slice(renderedLen);
5540
+ if (newChars.length > 0) {
5541
+ var stEl = document.getElementById(\x27streamText_\x27 + idx);
5542
+ if (stEl) { stEl.appendChild(document.createTextNode(newChars)); }
5543
+ tb.setAttribute(String.fromCharCode(100,97,116,97,45,114,108,101,110), String(output.length));
5544
+ }
5545
+ // Update iso thought bubble of the active agent
5546
+ var isoB = document.getElementById(\x27isobubble_\x27+idx);
5547
+ if (isoB) {
5548
+ var wfRaw = output.length > 48 ? output.slice(-48) : output;
5549
+ var wfSafe = wfRaw.replace(/&/g,\x27&amp;\x27).replace(/</g,\x27&lt;\x27).replace(/>/g,\x27&gt;\x27);
5550
+ isoB.className = \x27iso-bubble iso-bubble--active\x27;
5551
+ isoB.innerHTML = wfSafe + \x27<span style="display:inline-block;width:2px;height:8px;background:#6366f1;margin-left:1px;vertical-align:text-bottom;animation:streamBlink .7s step-end infinite">&#8203;</span>\x27;
5552
+ }
5553
+ // Update orchestrator bubble with progress
5554
+ var orchB = document.getElementById(\x27wfOrchBubble\x27);
5555
+ if (orchB) {
5556
+ var doneN = studioState.nodes.filter(function(x){return x.status===\x27done\x27;}).length;
5557
+ var totN = studioState.nodes.length;
5558
+ var orchPhrases2 = [\x27Elaboro step \x27+doneN+\x27/\x27+totN, \x27Verifico output...\x27, \x27Coordinamento...\x27, \x27Aspetto risposta...\x27];
5559
+ orchB.textContent = orchPhrases2[Math.floor(output.length/120) % orchPhrases2.length];
5560
+ }
5561
+ // Update boardroom seat bubble if parliament is active
5562
+ if (parlActiveAgent) {
5563
+ var brSafeLbl = parlActiveAgent.replace(new RegExp(\x27[^a-zA-Z0-9_-]\x27,\x27g\x27),\x27_\x27);
5564
+ var brBubbleEl = document.getElementById(\x27brbubble_\x27+brSafeLbl);
5565
+ if (brBubbleEl) {
5566
+ var brRaw = output.length > 48 ? output.slice(-48) : output;
5567
+ var brSafe = brRaw.replace(/&/g,\x27&amp;\x27).replace(/</g,\x27&lt;\x27).replace(/>/g,\x27&gt;\x27);
5568
+ brBubbleEl.style.display = \x27\x27;
5569
+ brBubbleEl.innerHTML = brSafe + \x27<span style="display:inline-block;width:2px;height:8px;background:var(--green);margin-left:1px;vertical-align:text-bottom;animation:streamBlink .7s step-end infinite">&#8203;</span>\x27;
5570
+ }
5571
+ }
5368
5572
  }
5369
5573
  }
5370
5574
  }
@@ -6169,8 +6373,8 @@ input:focus,textarea:focus{border-color:var(--green3)}
6169
6373
  .br-seat--active .br-seat-box{background:#1e1a45;border-color:var(--sc,#6366f1);box-shadow:0 0 20px rgba(99,102,241,.3),0 0 40px rgba(99,102,241,.08),inset 0 1px 0 rgba(150,130,255,.15)}
6170
6374
  .br-seat--active{transform:scale(1.04)}
6171
6375
  .br-seat--done .br-seat-box{background:#162516;border-color:#2a4a2a}
6172
- /* Action bubble above agent */
6173
- .br-bubble{font-size:9px;font-family:var(--mono);padding:2px 7px;border-radius:7px;border:1px solid #3a3060;background:#0d0a1e;min-height:14px;text-align:center;line-height:1.4;transition:all .3s;word-break:break-word;max-width:90px}
6376
+ /* Action bubble above agent — shows streaming text */
6377
+ .br-bubble{font-size:8px;font-family:var(--mono);padding:3px 8px;border-radius:8px 8px 8px 2px;border:1px solid #3a3060;background:rgba(10,8,20,.88);min-height:14px;text-align:left;line-height:1.45;transition:border-color .3s;word-break:break-word;max-width:100px;white-space:normal;backdrop-filter:blur(4px)}
6174
6378
  /* Agent name */
6175
6379
  .br-seat-name{font-size:9px;font-family:var(--mono);font-weight:700;text-align:center;white-space:normal;word-break:break-word;max-width:90px;line-height:1.3;margin-top:2px;transition:color .3s}
6176
6380
  /* SVG arm animations when agent is active (typing) */
@@ -6229,16 +6433,25 @@ input:focus,textarea:focus{border-color:var(--green3)}
6229
6433
  .prl-office-plant{position:absolute;bottom:14px;left:4px;width:22px;height:42px;z-index:2;pointer-events:none}
6230
6434
  /* Plant right */
6231
6435
  .prl-office-plant2{position:absolute;bottom:14px;right:8px;width:22px;height:42px;z-index:2;pointer-events:none}
6232
- /* Desks row */
6436
+ /* ── ISOMETRIC JRPG SCENE ── */
6437
+ .iso-scene{background:#0d0b1e;box-shadow:inset 0 0 40px rgba(0,0,0,.6);cursor:default;max-width:100%;overflow-x:auto}
6438
+ .iso-agent{transition:filter .3s}
6439
+ .iso-agent:hover{filter:brightness(1.15)}
6440
+ /* Thought bubble / speech bubble above character */
6441
+ .iso-bubble{font-size:8px;font-family:var(--mono);padding:2px 7px;border-radius:10px 10px 10px 2px;border:1px solid #3a3060;background:rgba(10,8,20,.85);color:#6b7280;white-space:nowrap;max-width:110px;overflow:hidden;text-overflow:ellipsis;line-height:1.4;transition:all .25s;pointer-events:none;backdrop-filter:blur(4px)}
6442
+ .iso-bubble--active{background:rgba(30,20,60,.9);border-color:#6366f1;color:#a5b4fc;animation:isoBubblePop .35s ease;white-space:normal;max-width:110px;word-break:break-word;line-height:1.35}
6443
+ .iso-bubble--orch{font-size:9px;padding:3px 9px;border-radius:12px;border-color:#818cf8;color:#a5b4fc;background:rgba(20,15,50,.9)}
6444
+ @keyframes isoBubblePop{0%{transform:scale(.8) translateY(4px);opacity:.4}100%{transform:scale(1) translateY(0);opacity:1}}
6445
+ .iso-name{font-size:8px;font-family:var(--mono);font-weight:700;letter-spacing:.3px;text-align:center;max-width:80px;white-space:normal;word-break:break-word;line-height:1.3;text-shadow:0 1px 4px rgba(0,0,0,.8);pointer-events:none}
6446
+ /* Desks row — kept for boardroom compat */
6233
6447
  .prl-desks-row{display:flex;gap:8px;align-items:flex-end;flex-wrap:wrap;position:relative;z-index:2;padding-bottom:8px}
6234
- /* Individual desk card — illuminated */
6235
6448
  .prl-desk{display:flex;flex-direction:column;align-items:center;gap:2px;padding:6px 6px 4px;border-radius:12px;background:#1a1535;border:1.5px solid #3a3060;transition:border-color .4s,background .4s,box-shadow .4s;position:relative;min-width:80px;box-shadow:0 2px 8px rgba(0,0,0,.3),inset 0 1px 0 rgba(255,255,255,.06)}
6236
6449
  .prl-desk--active{background:#1e1a45;border-color:var(--dc,#6366f1);box-shadow:0 0 20px rgba(99,102,241,.3),0 0 40px rgba(99,102,241,.1),inset 0 1px 0 rgba(150,130,255,.15)}
6237
6450
  .prl-desk--done{border-color:#2a4a2a;background:#162516}
6238
- /* Action bubble above character */
6239
6451
  .prl-action-bubble{font-size:9px;color:#6b7280;font-family:var(--mono);padding:2px 6px;border-radius:8px;background:#111;border:1px solid #2a2a38;min-height:16px;text-align:center;white-space:normal;word-break:break-word;max-width:88px;line-height:1.3;transition:all .3s}
6240
6452
  .prl-action-bubble--active{color:var(--dc,#6366f1);border-color:var(--dc,#6366f1);background:rgba(99,102,241,.08);animation:parlBubblePop .4s ease}
6241
6453
  @keyframes parlBubblePop{0%{transform:scale(.85);opacity:.5}100%{transform:scale(1);opacity:1}}
6454
+ @keyframes streamBlink{0%,100%{opacity:1}50%{opacity:0}}
6242
6455
  /* Character SVG animations */
6243
6456
  @keyframes parlArmType{0%,100%{transform:rotate(-8deg) translateY(0)}50%{transform:rotate(8deg) translateY(2px)}}
6244
6457
  @keyframes parlHeadNod{0%,100%{transform:translateY(0) rotate(0deg)}50%{transform:translateY(2px) rotate(4deg)}}