infinicode 2.8.68 → 2.8.69

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.
@@ -29,46 +29,39 @@ export const DASHBOARD_HTML = `<!doctype html>
29
29
  <title>RoboPark Control</title>
30
30
  <style>
31
31
  /* ── premium tech glass theme ── dark is the committed default; light is a frosted toggle ── */
32
- :root{
33
- --bg:#08070F;
34
- --grad: radial-gradient(1100px 680px at 12% -12%, rgba(255,184,64,0.16), transparent 60%),
35
- radial-gradient(900px 620px at 102% -6%, rgba(96,132,255,0.15), transparent 58%),
36
- radial-gradient(700px 700px at 88% 108%, rgba(150,90,255,0.10), transparent 60%),
37
- linear-gradient(162deg,#0A0814 0%,#0B0A17 42%,#070610 100%);
38
- --panel: rgba(28,25,42,0.55); --panel-2: rgba(42,37,60,0.60); --panel-solid:#141020;
39
- --glass-border: rgba(255,255,255,0.10); --glass-hi: rgba(255,255,255,0.06);
40
- --border: rgba(255,255,255,0.10); --border-strong: rgba(255,214,112,0.42);
41
- --ink:#F2ECDC; --muted:#9E93B4;
42
- --gold:#E6B23C; --gold-bright:#FFD86B;
32
+ :root{
33
+ --bg:#050504;
34
+ --grad: radial-gradient(900px 520px at 7% -8%, rgba(255,198,79,0.18), transparent 64%),
35
+ radial-gradient(760px 520px at 98% 4%, rgba(161,105,16,0.16), transparent 62%),
36
+ radial-gradient(650px 520px at 62% 112%, rgba(255,211,123,0.08), transparent 68%),
37
+ linear-gradient(145deg,#030303 0%,#0b0905 46%,#050504 100%);
38
+ --panel: rgba(16,14,10,0.64); --panel-2: rgba(27,23,15,0.68); --panel-solid:#0d0b08;
39
+ --glass-border: rgba(255,224,151,0.14); --glass-hi: rgba(255,241,204,0.09);
40
+ --border: rgba(255,224,151,0.12); --border-strong: rgba(255,205,93,0.52);
41
+ --ink:#F6F0E3; --muted:#A99D87;
42
+ --gold:#C9962E; --gold-bright:#F6D77B; --gold-pale:#FFF0BE; --gold-deep:#78520F;
43
43
  --ok:#6FE0A6; --ok-bg:rgba(28,64,50,0.55); --warn:#F2C14E; --warn-bg:rgba(64,50,20,0.5);
44
44
  --bad:#FF7E6E; --bad-bg:rgba(64,24,20,0.55); --info:#78BBFF; --info-bg:rgba(20,44,66,0.55);
45
- --shadow: 0 10px 34px rgba(0,0,0,0.42);
46
- --mono:ui-monospace,"Cascadia Code",Consolas,monospace;
47
- --sans:system-ui,"Segoe UI",Roboto,sans-serif;
45
+ --shadow: 0 18px 55px rgba(0,0,0,0.56),0 2px 12px rgba(0,0,0,0.36);
46
+ --mono:"Cascadia Code","SFMono-Regular",Consolas,monospace;
47
+ --sans:"Trebuchet MS","Segoe UI",sans-serif;
48
+ --display:"Palatino Linotype","Book Antiqua",Palatino,serif;
48
49
  }
49
50
  @media (prefers-color-scheme:dark){:root{}}
50
51
  :root[data-theme="dark"]{}
51
- :root[data-theme="light"]{
52
- --bg:#EDEAF6;
53
- --grad: radial-gradient(1000px 640px at 10% -10%, rgba(255,200,90,0.28), transparent 60%),
54
- radial-gradient(900px 600px at 104% -6%, rgba(120,150,255,0.22), transparent 58%),
55
- linear-gradient(160deg,#F3EFFB 0%,#ECEAF7 50%,#F5F0E4 100%);
56
- --panel: rgba(255,255,255,0.62); --panel-2: rgba(255,255,255,0.78); --panel-solid:#FBF7EE;
57
- --glass-border: rgba(90,70,20,0.16); --glass-hi: rgba(255,255,255,0.7);
58
- --border: rgba(90,70,20,0.16); --border-strong: rgba(185,120,22,0.55);
59
- --ink:#241A08; --muted:#6E5A3E;
60
- --gold:#8A5A0C; --gold-bright:#B9781A;
61
- --ok:#1F7A4C; --ok-bg:rgba(180,220,190,0.6); --warn:#8A6410; --warn-bg:rgba(240,225,180,0.7);
62
- --bad:#B5382E; --bad-bg:rgba(240,205,200,0.7); --info:#1F6F9E; --info-bg:rgba(200,225,240,0.7);
63
- --shadow: 0 10px 30px rgba(60,40,10,0.14);
52
+ :root[data-theme="light"]{
53
+ --bg:#090806;--panel:rgba(24,20,13,.72);--panel-2:rgba(38,31,18,.76);--panel-solid:#100e09;
54
+ --ink:#FFF8E8;--muted:#B5A88F;--glass-border:rgba(255,224,151,.17);--glass-hi:rgba(255,244,214,.1);
55
+ --border:rgba(255,224,151,.14);--border-strong:rgba(255,205,93,.58);--gold:#D3A43D;--gold-bright:#FFE094;
64
56
  }
65
57
  *{box-sizing:border-box;margin:0;padding:0}
66
58
  html,body{height:100%}
67
- body{background:var(--bg);background-image:var(--grad);background-attachment:fixed;background-size:cover;
68
- color:var(--ink);font-family:var(--sans);line-height:1.45;-webkit-font-smoothing:antialiased;}
59
+ body{background:var(--bg);background-image:var(--grad);background-attachment:fixed;background-size:cover;
60
+ color:var(--ink);font-family:var(--sans);line-height:1.45;-webkit-font-smoothing:antialiased;}
61
+ body:before{content:"";position:fixed;inset:0;pointer-events:none;z-index:-1;opacity:.22;background-image:linear-gradient(rgba(255,221,140,.025) 1px,transparent 1px),linear-gradient(90deg,rgba(255,221,140,.025) 1px,transparent 1px);background-size:42px 42px;mask-image:linear-gradient(to bottom,black,transparent 82%);}
69
62
  .mono{font-family:var(--mono);font-variant-numeric:tabular-nums;}
70
- .glass{background:var(--panel);-webkit-backdrop-filter:blur(16px) saturate(1.3);backdrop-filter:blur(16px) saturate(1.3);
71
- border:1px solid var(--glass-border);box-shadow:var(--shadow),inset 0 1px 0 var(--glass-hi);}
63
+ .glass{background:var(--panel);-webkit-backdrop-filter:blur(16px) saturate(1.3);backdrop-filter:blur(16px) saturate(1.3);
64
+ border:1px solid var(--glass-border);box-shadow:var(--shadow),inset 0 1px 0 var(--glass-hi),inset 0 -1px 0 rgba(0,0,0,.5);}
72
65
 
73
66
  /* top bar */
74
67
  header.bar{display:flex;align-items:center;justify-content:space-between;gap:1rem;flex-wrap:wrap;
@@ -80,7 +73,7 @@ header.bar{display:flex;align-items:center;justify-content:space-between;gap:1re
80
73
  .livedot.stale{background:var(--muted);box-shadow:none;animation:none;}
81
74
  @keyframes pulse{0%{box-shadow:0 0 0 0 color-mix(in srgb,var(--ok) 55%,transparent),0 0 12px var(--ok)}70%{box-shadow:0 0 0 7px transparent,0 0 12px var(--ok)}100%{box-shadow:0 0 0 0 transparent,0 0 12px var(--ok)}}
82
75
  @media (prefers-reduced-motion:reduce){.livedot{animation:none}}
83
- .brand b{font-size:0.98rem;letter-spacing:-0.01em;}
76
+ .brand b{font-family:var(--display);font-size:1.08rem;letter-spacing:.015em;color:var(--gold-pale);text-shadow:0 0 24px rgba(246,215,123,.2);}
84
77
  .brand .who{font-family:var(--mono);font-size:0.72rem;color:var(--muted);}
85
78
  .chips{display:flex;gap:0.45rem;flex-wrap:wrap;align-items:center;}
86
79
  .chip{font-family:var(--mono);font-size:0.68rem;padding:0.28em 0.62em;border-radius:100px;border:1px solid var(--glass-border);
@@ -532,21 +525,49 @@ button.act:disabled{opacity:0.5;cursor:default;}
532
525
  .rp-modal-body pre{background:var(--panel-solid);border:1px solid var(--glass-border);border-radius:8px;padding:0.5rem 0.7rem;
533
526
  font-size:0.66rem;max-height:240px;overflow:auto;white-space:pre-wrap;word-break:break-word;color:var(--ink);}
534
527
 
535
- .rp-call-shell{display:grid;grid-template-columns:minmax(260px,0.8fr) minmax(320px,1.2fr);gap:0.8rem;min-height:500px;}
536
- .rp-call-panel{border:1px solid var(--glass-border);border-radius:14px;background:var(--panel);box-shadow:var(--shadow),inset 0 1px 0 var(--glass-hi);padding:0.85rem;}
537
- .rp-call-character{display:flex;align-items:center;gap:0.8rem;padding:0.7rem;border:1px solid var(--glass-border);border-radius:12px;background:var(--panel-2);margin-bottom:0.75rem;}
538
- .rp-call-character img{width:78px;height:78px;object-fit:contain;filter:drop-shadow(0 8px 16px rgba(0,0,0,.35));}
539
- .rp-call-character .name{font-size:1.05rem;color:var(--gold-bright);font-weight:700;}
540
- .rp-call-state{display:flex;align-items:center;gap:0.55rem;font-family:var(--mono);font-size:0.72rem;padding:0.65rem 0;}
541
- .rp-call-orb{width:10px;height:10px;border-radius:50%;background:var(--muted);box-shadow:0 0 0 transparent;}
542
- .rp-call-orb.live{background:var(--ok);box-shadow:0 0 12px var(--ok);animation:pulse 1.6s infinite;}
543
- .rp-call-orb.busy{background:var(--gold-bright);box-shadow:0 0 12px var(--gold-bright);animation:pulse 1s infinite;}
544
- .rp-call-actions{display:flex;gap:0.45rem;flex-wrap:wrap;margin-top:0.7rem;}
545
- .rp-call-transcript{height:430px;overflow-y:auto;display:flex;flex-direction:column;gap:0.55rem;padding:0.3rem;}
546
- .rp-turn{max-width:86%;border:1px solid var(--glass-border);border-radius:12px;padding:0.55rem 0.7rem;font-family:var(--mono);font-size:0.72rem;line-height:1.45;background:var(--panel-2);}
547
- .rp-turn.user{align-self:flex-end;border-color:color-mix(in srgb,var(--info) 45%,transparent);background:color-mix(in srgb,var(--info) 9%,var(--panel-2));}
548
- .rp-turn.assistant{align-self:flex-start;border-color:color-mix(in srgb,var(--gold-bright) 42%,transparent);}
549
- .rp-turn .who{font-size:0.57rem;text-transform:uppercase;letter-spacing:.08em;color:var(--muted);margin-bottom:.18rem;}
528
+ .rp-call-shell{position:relative;min-height:720px;overflow:hidden;border:1px solid var(--glass-border);border-radius:24px;background:linear-gradient(145deg,rgba(18,15,9,.84),rgba(5,5,4,.9));box-shadow:0 30px 90px rgba(0,0,0,.7),inset 0 1px 0 rgba(255,238,194,.11);}
529
+ .rp-call-shell:before{content:"";position:absolute;width:520px;height:520px;border-radius:50%;left:18%;top:-340px;background:rgba(246,215,123,.15);filter:blur(75px);pointer-events:none;}
530
+ .rp-call-top{height:58px;padding:0 1.1rem;display:flex;align-items:center;gap:.75rem;border-bottom:1px solid var(--glass-border);background:rgba(8,7,5,.56);backdrop-filter:blur(24px);position:relative;z-index:3;}
531
+ .rp-call-top .eyebrow{font-family:var(--mono);font-size:.57rem;letter-spacing:.16em;color:var(--gold);text-transform:uppercase;}
532
+ .rp-call-top .title{font-family:var(--display);font-size:1.02rem;color:var(--gold-pale);letter-spacing:.03em;}
533
+ .rp-call-top .session{margin-left:auto;font-family:var(--mono);font-size:.58rem;color:var(--muted);max-width:32ch;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
534
+ .rp-call-grid{display:grid;grid-template-columns:280px minmax(380px,1fr) 330px;gap:1px;background:var(--glass-border);min-height:662px;position:relative;z-index:2;}
535
+ .rp-call-rail,.rp-call-stage,.rp-call-chat{background:rgba(8,7,5,.82);padding:1rem;min-width:0;}
536
+ .rp-call-rail{display:flex;flex-direction:column;gap:.8rem;}
537
+ .rp-call-card{border:1px solid var(--glass-border);border-radius:16px;background:linear-gradient(145deg,rgba(38,31,18,.58),rgba(13,11,8,.66));box-shadow:inset 0 1px 0 var(--glass-hi),0 12px 35px rgba(0,0,0,.25);padding:.8rem;}
538
+ .rp-call-character{position:relative;display:flex;align-items:center;gap:.75rem;padding:.75rem;border:1px solid rgba(246,215,123,.2);border-radius:15px;background:radial-gradient(circle at 12% 10%,rgba(246,215,123,.15),transparent 52%),rgba(16,14,10,.65);overflow:hidden;}
539
+ .rp-call-character img{width:76px;height:76px;object-fit:contain;filter:drop-shadow(0 10px 20px rgba(0,0,0,.48));}
540
+ .rp-call-character .name{font-family:var(--display);font-size:1.18rem;color:var(--gold-pale);font-weight:700;}
541
+ .rp-call-character .meta{font-size:.64rem;line-height:1.4;}
542
+ .rp-call-service-grid{display:grid;gap:.4rem;}
543
+ .rp-call-service{display:grid;grid-template-columns:29px 1fr auto;align-items:center;gap:.45rem;padding:.48rem;border:1px solid var(--glass-border);border-radius:10px;background:rgba(255,255,255,.018);}
544
+ .rp-call-service .ico{width:29px;height:29px;display:grid;place-items:center;border-radius:8px;background:rgba(246,215,123,.09);color:var(--gold-bright);font-size:.75rem;}
545
+ .rp-call-service b{display:block;font-size:.64rem;color:var(--ink);}.rp-call-service small{display:block;font-family:var(--mono);font-size:.52rem;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:130px;}
546
+ .rp-call-service .ready{font-family:var(--mono);font-size:.48rem;letter-spacing:.08em;text-transform:uppercase;color:var(--ok);}
547
+ .rp-call-device{display:grid;gap:.22rem;margin-top:.45rem}.rp-call-device:first-child{margin-top:0}.rp-call-device label{font-family:var(--mono);font-size:.51rem;text-transform:uppercase;letter-spacing:.1em;color:var(--muted)}
548
+ .rp-call-device select{width:100%;min-width:0;font-family:var(--mono);font-size:.62rem;background:#0c0a07;border:1px solid var(--glass-border);color:var(--ink);border-radius:9px;padding:.42rem .5rem;}
549
+ .rp-call-stage{display:flex;flex-direction:column;align-items:center;justify-content:center;position:relative;overflow:hidden;}
550
+ .rp-call-stage:before{content:"";position:absolute;width:420px;height:420px;border:1px solid rgba(246,215,123,.08);border-radius:50%;box-shadow:0 0 0 42px rgba(246,215,123,.018),0 0 0 92px rgba(246,215,123,.012);}
551
+ .rp-call-visual{position:relative;width:min(330px,80%);aspect-ratio:1;border-radius:50%;display:grid;place-items:center;background:radial-gradient(circle,rgba(246,215,123,.14),rgba(10,8,5,.58) 52%,rgba(0,0,0,.82) 72%);border:1px solid rgba(246,215,123,.24);box-shadow:0 0 70px rgba(201,150,46,.13),inset 0 0 48px rgba(246,215,123,.06);z-index:1;transition:box-shadow .3s,border-color .3s;}
552
+ .rp-call-visual.speaking{box-shadow:0 0 95px rgba(246,215,123,.28),inset 0 0 55px rgba(246,215,123,.11);border-color:rgba(255,229,157,.55)}
553
+ .rp-call-avatar{position:absolute;width:58%;height:58%;object-fit:contain;filter:drop-shadow(0 18px 30px rgba(0,0,0,.65));opacity:.96;}
554
+ .rp-call-avatar:not([src]){display:none}
555
+ .rp-call-avatar-fallback{position:absolute;width:128px;height:128px;border-radius:50%;display:grid;place-items:center;font-family:var(--display);font-size:2.8rem;letter-spacing:.08em;color:var(--gold-pale);background:radial-gradient(circle at 32% 24%,rgba(255,234,174,.18),rgba(201,150,46,.07) 48%,rgba(0,0,0,.2));border:1px solid rgba(246,215,123,.2);box-shadow:inset 0 0 34px rgba(246,215,123,.06),0 18px 45px rgba(0,0,0,.42);}
556
+ .rp-call-avatar[src]{z-index:1}.rp-call-avatar[src]+.rp-call-avatar-fallback{display:none}
557
+ .rp-call-bars{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;gap:7px;opacity:.82}.rp-call-bars i{width:5px;height:16px;border-radius:8px;background:linear-gradient(var(--gold-pale),var(--gold));box-shadow:0 0 12px rgba(246,215,123,.35);transition:height .09s ease;}
558
+ .rp-call-ring{position:absolute;inset:-10px;border:1px solid rgba(246,215,123,.13);border-radius:50%;animation:rp-call-spin 18s linear infinite}.rp-call-ring:before,.rp-call-ring:after{content:"";position:absolute;width:7px;height:7px;border-radius:50%;background:var(--gold-bright);box-shadow:0 0 15px var(--gold-bright)}.rp-call-ring:before{left:15%;top:12%}.rp-call-ring:after{right:10%;bottom:18%}
559
+ @keyframes rp-call-spin{to{transform:rotate(360deg)}}
560
+ .rp-call-agent-state{position:relative;z-index:2;margin-top:1.15rem;text-align:center}.rp-call-agent-state strong{display:block;font-family:var(--display);font-size:1.35rem;color:var(--gold-pale);font-weight:500}.rp-call-agent-state span{font-family:var(--mono);font-size:.55rem;letter-spacing:.13em;color:var(--muted);text-transform:uppercase}
561
+ .rp-call-state{display:flex;align-items:center;justify-content:center;gap:.5rem;font-family:var(--mono);font-size:.62rem;padding:.55rem 0;position:relative;z-index:2;}
562
+ .rp-call-orb{width:8px;height:8px;border-radius:50%;background:var(--muted)}.rp-call-orb.live{background:var(--ok);box-shadow:0 0 12px var(--ok);animation:pulse 1.6s infinite}.rp-call-orb.busy{background:var(--gold-bright);box-shadow:0 0 12px var(--gold-bright);animation:pulse 1s infinite}
563
+ .rp-call-latency{position:relative;z-index:2;display:grid;grid-template-columns:repeat(3,1fr);gap:.4rem;width:min(420px,92%);margin-top:.65rem}.rp-call-latency div{padding:.48rem;border:1px solid var(--glass-border);border-radius:10px;background:rgba(0,0,0,.28);text-align:center}.rp-call-latency b{display:block;font-family:var(--mono);font-size:.72rem;color:var(--gold-bright)}.rp-call-latency span{font-family:var(--mono);font-size:.46rem;text-transform:uppercase;letter-spacing:.08em;color:var(--muted)}
564
+ .rp-call-media{position:absolute;right:1rem;top:1rem;width:128px;aspect-ratio:4/3;border:1px solid var(--glass-border);border-radius:12px;background:#050504;overflow:hidden;display:none;z-index:4;box-shadow:var(--shadow)}.rp-call-media.on{display:block}.rp-call-media video{width:100%;height:100%;object-fit:cover}.rp-call-media .tag{position:absolute;left:.35rem;bottom:.3rem;font-family:var(--mono);font-size:.46rem;color:var(--gold-pale);background:rgba(0,0,0,.7);padding:.12rem .3rem;border-radius:4px}
565
+ .rp-call-controls{position:relative;z-index:3;display:flex;align-items:center;justify-content:center;gap:.52rem;margin-top:1rem;flex-wrap:wrap}.rp-call-ctl{width:44px;height:44px;border-radius:50%;display:grid;place-items:center;border:1px solid var(--glass-border);background:rgba(255,255,255,.035);color:var(--ink);cursor:pointer;font-size:1rem;transition:.18s}.rp-call-ctl:hover:not(:disabled){transform:translateY(-2px);border-color:var(--gold-bright);color:var(--gold-bright);box-shadow:0 7px 24px rgba(0,0,0,.38)}.rp-call-ctl.on{background:rgba(246,215,123,.12);border-color:rgba(246,215,123,.46);color:var(--gold-bright)}.rp-call-ctl.danger{background:rgba(255,78,63,.13);border-color:rgba(255,126,110,.35);color:var(--bad)}.rp-call-ctl:disabled{opacity:.32;cursor:not-allowed}.rp-call-start{width:auto;border-radius:100px;padding:0 1.15rem;font-family:var(--mono);font-size:.61rem;text-transform:uppercase;letter-spacing:.08em;background:linear-gradient(135deg,#ffe59a,#b77b16);color:#201401;border-color:#ffdc7d;font-weight:700}
566
+ .rp-call-chat{display:flex;flex-direction:column;min-height:0}.rp-call-chat-head{display:flex;align-items:center;gap:.45rem;padding-bottom:.65rem;border-bottom:1px solid var(--glass-border)}.rp-call-chat-head b{font-family:var(--display);font-size:.94rem;color:var(--gold-pale)}.rp-call-chat-head button{margin-left:auto;background:none;border:0;color:var(--muted);font-family:var(--mono);font-size:.55rem;cursor:pointer}.rp-call-chat-head button:hover{color:var(--gold-bright)}
567
+ .rp-call-transcript{height:500px;overflow-y:auto;display:flex;flex-direction:column;gap:.55rem;padding:.7rem .15rem;scrollbar-color:var(--gold-deep) transparent}.rp-call-transcript.collapsed{display:none}
568
+ .rp-turn{max-width:90%;border:1px solid var(--glass-border);border-radius:14px;padding:.6rem .7rem;font-size:.71rem;line-height:1.5;background:rgba(255,255,255,.025);box-shadow:0 8px 22px rgba(0,0,0,.2)}.rp-turn.user{align-self:flex-end;border-color:rgba(246,215,123,.22);background:linear-gradient(145deg,rgba(201,150,46,.13),rgba(255,255,255,.025));border-bottom-right-radius:4px}.rp-turn.assistant{align-self:flex-start;border-color:rgba(246,215,123,.38);border-bottom-left-radius:4px}.rp-turn .who{font-family:var(--mono);font-size:.5rem;text-transform:uppercase;letter-spacing:.1em;color:var(--gold);margin-bottom:.2rem}
569
+ .rp-call-compose{display:flex;gap:.4rem;margin-top:auto;padding-top:.65rem;border-top:1px solid var(--glass-border)}.rp-call-compose input{flex:1;min-width:0;background:#0c0a07;border:1px solid var(--glass-border);color:var(--ink);border-radius:11px;padding:.58rem .65rem;font-size:.7rem}.rp-call-compose input:focus{outline:none;border-color:var(--gold-bright);box-shadow:0 0 0 3px rgba(246,215,123,.08)}.rp-call-compose button{width:38px;border-radius:11px;border:1px solid rgba(246,215,123,.38);background:rgba(246,215,123,.1);color:var(--gold-bright);cursor:pointer}
570
+ .rp-call-event{min-height:42px;margin-top:.65rem;padding:.5rem .6rem;border:1px solid var(--glass-border);border-radius:10px;background:rgba(0,0,0,.22);font-family:var(--mono);font-size:.54rem;color:var(--muted);line-height:1.45}.rp-call-event b{color:var(--gold-bright)}
550
571
  .rp-detail-hero{display:flex;align-items:center;gap:.8rem;padding:.6rem;border:1px solid var(--glass-border);border-radius:12px;background:var(--panel-2);margin-bottom:.8rem;}
551
572
  .rp-detail-hero img{width:72px;height:72px;object-fit:contain;}
552
573
  .rp-pipeline{display:flex;flex-direction:column;gap:.3rem;margin-top:.5rem;}
@@ -556,7 +577,8 @@ button.act:disabled{opacity:0.5;cursor:default;}
556
577
  .node .node-avatar{width:68px;height:68px;object-fit:contain;flex:none;filter:drop-shadow(0 7px 12px rgba(0,0,0,.3));}
557
578
  .node .node-health{font-family:var(--mono);font-size:.61rem;line-height:1.45;color:var(--muted);}
558
579
  .node .node-health b{color:var(--ink);}
559
- @media(max-width:820px){.rp-call-shell{grid-template-columns:1fr}.rp-call-transcript{height:320px}.rp-pipeline-row{grid-template-columns:62px 105px 58px 1fr}}
580
+ @media(max-width:1150px){.rp-call-grid{grid-template-columns:250px minmax(360px,1fr)}.rp-call-chat{grid-column:1/-1;min-height:390px}.rp-call-transcript{height:280px}}
581
+ @media(max-width:760px){.rp-call-shell{border-radius:15px}.rp-call-grid{display:block}.rp-call-rail,.rp-call-stage,.rp-call-chat{border-bottom:1px solid var(--glass-border)}.rp-call-stage{min-height:590px}.rp-call-top .session{display:none}.rp-call-visual{width:min(285px,78vw)}.rp-call-transcript{height:320px}.rp-pipeline-row{grid-template-columns:62px 105px 58px 1fr}}
560
582
 
561
583
  /* ── TTS voice picker (used inside the generic modal) ── */
562
584
  .rp-voice-picker{display:flex;flex-direction:column;gap:0.6rem;min-height:320px;}
@@ -729,23 +751,64 @@ button.act:disabled{opacity:0.5;cursor:default;}
729
751
  <!-- ═══ History ═══ -->
730
752
  <div class="rp-sub" id="rp-sub-voice">
731
753
  <div class="rp-call-shell">
732
- <div class="rp-call-panel">
733
- <div class="rp-section-h">Operator voice call <span class="meta">RoboVoice pipeline</span></div>
734
- <div class="rp-call-character" id="rp-call-character"><div><div class="name">Choose a character</div><div class="meta">The selected preset and stack are frozen into session history.</div></div></div>
735
- <div class="rp-field"><label>Character preset</label><select id="rp-call-character-select"><option value="">select character...</option></select></div>
736
- <div class="rp-field" style="margin-top:.55rem"><label>Voice stack</label><select id="rp-call-stack-select"><option value="">character default</option></select></div>
737
- <div class="rp-call-state"><span class="rp-call-orb" id="rp-call-orb"></span><span id="rp-call-status">ready to start</span></div>
738
- <div class="rp-call-actions">
739
- <button class="rp-btn primary" id="rp-call-start">Start voice call</button>
740
- <button class="rp-btn" id="rp-call-mute" disabled>Mute microphone</button>
741
- <button class="rp-btn danger" id="rp-call-end" disabled>End call</button>
742
- </div>
743
- <div class="pk-note" style="margin-top:.7rem">Uses your browser microphone. Agent audio plays here and every final transcript turn is saved to session history.</div>
754
+ <div class="rp-call-top">
755
+ <div><div class="eyebrow">RoboVoice operator channel</div><div class="title">Royal Voice Studio</div></div>
756
+ <div class="session" id="rp-call-session">No active session</div>
744
757
  </div>
745
- <div class="rp-call-panel">
746
- <div class="rp-section-h">Live transcript <span class="meta" id="rp-call-session">not connected</span></div>
747
- <div class="rp-call-transcript" id="rp-call-transcript"><div class="rp-tbl-empty">Transcript appears when the call starts.</div></div>
748
- <div id="rp-call-audio" hidden></div>
758
+ <div class="rp-call-grid">
759
+ <aside class="rp-call-rail">
760
+ <div class="rp-call-character" id="rp-call-character"><div><div class="name">Choose a character</div><div class="meta">Preset and voice stack are frozen into session history.</div></div></div>
761
+ <div class="rp-call-card">
762
+ <div class="rp-call-device"><label>Character preset</label><select id="rp-call-character-select"><option value="">select character...</option></select></div>
763
+ <div class="rp-call-device"><label>Voice stack</label><select id="rp-call-stack-select"><option value="">character default</option></select></div>
764
+ </div>
765
+ <div class="rp-call-card">
766
+ <div class="rp-section-h" style="margin:0 0 .5rem">Voice pipeline <span class="meta">selected stack</span></div>
767
+ <div class="rp-call-service-grid" id="rp-call-services">
768
+ <div class="rp-call-service"><span class="ico">STT</span><div><b>Speech recognition</b><small>choose a stack</small></div><span class="ready">standby</span></div>
769
+ <div class="rp-call-service"><span class="ico">LLM</span><div><b>Language model</b><small>choose a stack</small></div><span class="ready">standby</span></div>
770
+ <div class="rp-call-service"><span class="ico">TTS</span><div><b>Voice synthesis</b><small>choose a stack</small></div><span class="ready">standby</span></div>
771
+ </div>
772
+ </div>
773
+ <div class="rp-call-card">
774
+ <div class="rp-section-h" style="margin:0 0 .5rem">Browser devices <span class="meta" id="rp-call-device-status">permission on start</span></div>
775
+ <div class="rp-call-device"><label>Microphone</label><select id="rp-call-mic-device"><option value="">system default</option></select></div>
776
+ <div class="rp-call-device"><label>Camera</label><select id="rp-call-cam-device"><option value="">system default</option></select></div>
777
+ <div class="rp-call-device"><label>Speaker output</label><select id="rp-call-speaker-device"><option value="">system default</option></select></div>
778
+ <button class="rp-btn" id="rp-call-refresh-devices" style="width:100%;margin-top:.55rem">Refresh browser devices</button>
779
+ </div>
780
+ </aside>
781
+ <main class="rp-call-stage">
782
+ <div class="rp-call-media" id="rp-call-media"><video id="rp-call-video" autoplay muted playsinline></video><span class="tag" id="rp-call-media-tag">CAMERA</span></div>
783
+ <div class="rp-call-visual" id="rp-call-visual">
784
+ <div class="rp-call-ring"></div>
785
+ <img class="rp-call-avatar" id="rp-call-avatar" alt="" onerror="this.removeAttribute('src')">
786
+ <div class="rp-call-avatar-fallback" id="rp-call-avatar-fallback" aria-hidden="true">RP</div>
787
+ <div class="rp-call-bars" id="rp-call-bars"><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div>
788
+ </div>
789
+ <div class="rp-call-agent-state"><strong id="rp-call-agent-state">Ready when you are</strong><span id="rp-call-agent-detail">select character and begin</span></div>
790
+ <div class="rp-call-state"><span class="rp-call-orb" id="rp-call-orb"></span><span id="rp-call-status">ready to start</span><span class="mono" id="rp-call-timer">00:00</span></div>
791
+ <div class="rp-call-latency">
792
+ <div><b id="rp-call-lat-total">--</b><span>Total response</span></div>
793
+ <div><b id="rp-call-lat-stt">--</b><span>STT to LLM</span></div>
794
+ <div><b id="rp-call-lat-tts">--</b><span>LLM to TTS</span></div>
795
+ </div>
796
+ <div class="rp-call-controls">
797
+ <button class="rp-call-ctl rp-call-start" id="rp-call-start">Start call</button>
798
+ <button class="rp-call-ctl" id="rp-call-mute" title="Microphone" disabled>MIC</button>
799
+ <button class="rp-call-ctl" id="rp-call-camera" title="Camera" disabled>CAM</button>
800
+ <button class="rp-call-ctl" id="rp-call-screen" title="Share screen" disabled>SCR</button>
801
+ <button class="rp-call-ctl" id="rp-call-chat-toggle" title="Toggle transcript">TXT</button>
802
+ <button class="rp-call-ctl danger" id="rp-call-end" title="End call" disabled>END</button>
803
+ </div>
804
+ <div class="rp-call-event" id="rp-call-event"><b>System</b> Voice pipeline is standing by.</div>
805
+ </main>
806
+ <section class="rp-call-chat">
807
+ <div class="rp-call-chat-head"><b>Conversation</b><span class="chip" id="rp-call-message-count">0 messages</span><button id="rp-call-collapse">collapse transcript</button></div>
808
+ <div class="rp-call-transcript" id="rp-call-transcript"><div class="rp-tbl-empty">Your live transcript and typed messages appear here.</div></div>
809
+ <form class="rp-call-compose" id="rp-call-compose"><input id="rp-call-message" autocomplete="off" placeholder="Type a message to the character..." disabled><button type="submit" id="rp-call-send" disabled>SEND</button></form>
810
+ <div id="rp-call-audio" hidden></div>
811
+ </section>
749
812
  </div>
750
813
  </div>
751
814
  </div>
@@ -2597,7 +2660,7 @@ button.act:disabled{opacity:0.5;cursor:default;}
2597
2660
  })();
2598
2661
 
2599
2662
  // ── History sub-tab ──
2600
- var rpVoiceCall={room:null,session:null,eventToken:null,muted:false,ending:false,seen:{}};
2663
+ var rpVoiceCall={room:null,LK:null,session:null,eventToken:null,muted:false,camera:false,screen:false,ending:false,seen:{},messages:0,timer:null,startedAt:0,audioContext:null,analyser:null,audioRaf:0,transcriptCollapsed:false};
2601
2664
  function refreshVoiceCallOptions(){
2602
2665
  var cs=$('rp-call-character-select'), ss=$('rp-call-stack-select');
2603
2666
  if(!cs||!ss) return;
@@ -2605,19 +2668,35 @@ button.act:disabled{opacity:0.5;cursor:default;}
2605
2668
  cs.innerHTML='<option value="">select character...</option>'+(characterPresets||[]).map(function(c){return '<option value="'+esc(c.id)+'">'+esc(c.name||c.id)+'</option>';}).join('');
2606
2669
  ss.innerHTML='<option value="">character default</option>'+(voiceStacks||[]).map(function(s){return '<option value="'+esc(s.id)+'">'+esc(s.name||s.id)+' - '+esc(s.stt_provider)+' / '+esc(s.llm_model)+' / '+esc(s.tts_provider)+'</option>';}).join('');
2607
2670
  if(cv) cs.value=cv; if(sv) ss.value=sv;
2608
- paintVoiceCallCharacter();
2671
+ paintVoiceCallCharacter(); paintVoiceCallStack();
2609
2672
  }
2610
2673
  function paintVoiceCallCharacter(){
2611
2674
  var sel=$('rp-call-character-select'), box=$('rp-call-character'); if(!sel||!box) return;
2612
2675
  var chosen=null; for(var i=0;i<characterPresets.length;i++) if(characterPresets[i].id===sel.value){chosen=characterPresets[i];break;}
2613
- if(!chosen){ box.innerHTML='<div><div class="name">Choose a character</div><div class="meta">The selected preset and stack are frozen into session history.</div></div>'; return; }
2676
+ if(!chosen){ box.innerHTML='<div><div class="name">Choose a character</div><div class="meta">Preset and voice stack are frozen into session history.</div></div>'; $('rp-call-avatar').removeAttribute('src'); $('rp-call-avatar-fallback').textContent='RP'; return; }
2614
2677
  var img=chosen.img||rpAvatarFor(chosen.id||chosen.name);
2615
2678
  box.innerHTML=(img?'<img alt="'+esc(chosen.name)+'" src="'+esc(img+QS)+'">':'')+'<div><div class="name">'+esc(chosen.name||chosen.id)+'</div><div class="meta">'+esc(chosen.description||'Ready for an operator voice call')+'</div></div>';
2679
+ $('rp-call-avatar-fallback').textContent=String(chosen.name||chosen.id||'RP').slice(0,2).toUpperCase();
2680
+ if(img)$('rp-call-avatar').src=img+QS;else $('rp-call-avatar').removeAttribute('src');
2616
2681
  var stack=$('rp-call-stack-select'); if(stack && !stack.value && chosen.voice_stack_id) stack.value=chosen.voice_stack_id;
2682
+ paintVoiceCallStack();
2683
+ }
2684
+ function selectedVoiceStack(){
2685
+ var sid=$('rp-call-stack-select').value, chosen=null;
2686
+ if(!sid){var cid=$('rp-call-character-select').value;for(var i=0;i<characterPresets.length;i++)if(characterPresets[i].id===cid){sid=characterPresets[i].voice_stack_id||'';break;}}
2687
+ for(var j=0;j<voiceStacks.length;j++)if(voiceStacks[j].id===sid){chosen=voiceStacks[j];break;} return chosen;
2688
+ }
2689
+ function paintVoiceCallStack(){
2690
+ var wrap=$('rp-call-services'); if(!wrap)return; var s=selectedVoiceStack();
2691
+ if(!s){wrap.innerHTML='<div class="rp-call-service"><span class="ico">STT</span><div><b>Speech recognition</b><small>choose a stack</small></div><span class="ready">standby</span></div><div class="rp-call-service"><span class="ico">LLM</span><div><b>Language model</b><small>choose a stack</small></div><span class="ready">standby</span></div><div class="rp-call-service"><span class="ico">TTS</span><div><b>Voice synthesis</b><small>choose a stack</small></div><span class="ready">standby</span></div>';return;}
2692
+ function svc(code,name,provider,model){return '<div class="rp-call-service"><span class="ico">'+code+'</span><div><b>'+esc(name)+'</b><small>'+esc((provider||'default')+' / '+(model||'default'))+'</small></div><span class="ready">ready</span></div>';}
2693
+ wrap.innerHTML=svc('STT','Speech recognition',s.stt_provider,s.stt_model)+svc('LLM','Language model',s.llm_provider,s.llm_model)+svc('TTS','Voice synthesis',s.tts_provider,s.tts_voice);
2617
2694
  }
2618
2695
  function setVoiceCallStatus(text,state){
2619
2696
  var s=$('rp-call-status'), o=$('rp-call-orb'); if(s)s.textContent=text; if(o)o.className='rp-call-orb '+(state||'');
2620
2697
  }
2698
+ function setVoiceAgentState(title,detail){if($('rp-call-agent-state'))$('rp-call-agent-state').textContent=title;if($('rp-call-agent-detail'))$('rp-call-agent-detail').textContent=detail||'';}
2699
+ function setVoiceEvent(title,detail){var e=$('rp-call-event');if(e)e.innerHTML='<b>'+esc(title)+'</b> '+esc(detail||'');}
2621
2700
  function ensureVoiceLiveKit(){
2622
2701
  if(window.LivekitClient) return Promise.resolve(window.LivekitClient);
2623
2702
  return new Promise(function(resolve,reject){
@@ -2633,7 +2712,41 @@ button.act:disabled{opacity:0.5;cursor:default;}
2633
2712
  var div=document.createElement('div'); div.className='rp-turn '+role;
2634
2713
  div.innerHTML='<div class="who">'+esc(role==='user'?'operator':'character')+'</div><div>'+esc(text)+'</div>';
2635
2714
  wrap.appendChild(div); wrap.scrollTop=wrap.scrollHeight;
2715
+ rpVoiceCall.messages++; if($('rp-call-message-count'))$('rp-call-message-count').textContent=rpVoiceCall.messages+' message'+(rpVoiceCall.messages===1?'':'s');
2716
+ }
2717
+ function fillDeviceSelect(id,devices,kind){
2718
+ var sel=$(id);if(!sel)return;var current=sel.value,rows=devices.filter(function(d){return d.kind===kind;});
2719
+ sel.innerHTML='<option value="">system default</option>'+rows.map(function(d,i){return '<option value="'+esc(d.deviceId)+'">'+esc(d.label||kind+' '+(i+1))+'</option>';}).join('');if(current)sel.value=current;
2636
2720
  }
2721
+ function refreshBrowserDevices(requestPermission){
2722
+ if(!navigator.mediaDevices||!navigator.mediaDevices.enumerateDevices){$('rp-call-device-status').textContent='unsupported';return Promise.resolve([]);}
2723
+ var permission=Promise.resolve();
2724
+ if(requestPermission)permission=navigator.mediaDevices.getUserMedia({audio:true}).then(function(stream){stream.getTracks().forEach(function(t){t.stop();});}).catch(function(e){setVoiceEvent('Microphone',e.message||'permission denied');});
2725
+ return permission.then(function(){return navigator.mediaDevices.enumerateDevices();}).then(function(devices){fillDeviceSelect('rp-call-mic-device',devices,'audioinput');fillDeviceSelect('rp-call-cam-device',devices,'videoinput');fillDeviceSelect('rp-call-speaker-device',devices,'audiooutput');$('rp-call-device-status').textContent=devices.length+' detected';return devices;});
2726
+ }
2727
+ function switchVoiceDevice(kind,id){if(!rpVoiceCall.room||!id)return Promise.resolve();return rpVoiceCall.room.switchActiveDevice(kind,id).catch(function(e){setVoiceEvent('Device switch failed',e.message);});}
2728
+ function applyVoiceOutputDevice(){var id=$('rp-call-speaker-device').value;if(!id)return;var els=$('rp-call-audio').querySelectorAll('audio');for(var i=0;i<els.length;i++)if(typeof els[i].setSinkId==='function')els[i].setSinkId(id).catch(function(){});}
2729
+ function startVoiceVisualizer(track){
2730
+ stopVoiceVisualizer();try{var C=window.AudioContext||window.webkitAudioContext;if(!C||!track||!track.mediaStreamTrack)return;var ctx=new C(),an=ctx.createAnalyser(),source=ctx.createMediaStreamSource(new MediaStream([track.mediaStreamTrack]));an.fftSize=128;source.connect(an);rpVoiceCall.audioContext=ctx;rpVoiceCall.analyser=an;var data=new Uint8Array(an.frequencyBinCount),bars=$('rp-call-bars').querySelectorAll('i');
2731
+ function draw(){an.getByteFrequencyData(data);var sum=0;for(var x=0;x<data.length;x++)sum+=data[x];var avg=sum/data.length;for(var i=0;i<bars.length;i++){var idx=Math.floor(i*data.length/bars.length);bars[i].style.height=(12+Math.min(70,data[idx]*.28))+'px';}$('rp-call-visual').classList.toggle('speaking',avg>10);rpVoiceCall.audioRaf=requestAnimationFrame(draw);}draw();
2732
+ }catch(_){}
2733
+ }
2734
+ function stopVoiceVisualizer(){if(rpVoiceCall.audioRaf)cancelAnimationFrame(rpVoiceCall.audioRaf);rpVoiceCall.audioRaf=0;if(rpVoiceCall.audioContext)try{rpVoiceCall.audioContext.close();}catch(_){}rpVoiceCall.audioContext=null;rpVoiceCall.analyser=null;var bars=$('rp-call-bars');if(bars)Array.prototype.forEach.call(bars.querySelectorAll('i'),function(b){b.style.height='16px';});}
2735
+ function updateVoiceTimer(){if(!rpVoiceCall.startedAt)return;var sec=Math.floor((Date.now()-rpVoiceCall.startedAt)/1000),m=Math.floor(sec/60),s=sec%60;$('rp-call-timer').textContent=(m<10?'0':'')+m+':'+(s<10?'0':'')+s;}
2736
+ function updateAgentFromParticipant(participant){if(!participant)return;var a=participant.attributes||{},state=a['lk.agent.state']||a['agent.state']||'';if(state==='speaking')setVoiceAgentState('Speaking','character audio is streaming');else if(state==='thinking')setVoiceAgentState('Thinking','language model is composing');else if(state==='listening')setVoiceAgentState('Listening','speak naturally');else if(state==='initializing')setVoiceAgentState('Preparing agent','loading voice pipeline');}
2737
+ function handleVoiceData(payload,participant,kind,topic){
2738
+ try{var msg=JSON.parse(new TextDecoder().decode(payload));
2739
+ if(topic==='response_latency'){var total=Number(msg.total_ms)||0;$('rp-call-lat-total').textContent=total?total+'ms':'--';$('rp-call-lat-stt').textContent=msg.stt_to_llm_ms!=null?Math.round(msg.stt_to_llm_ms)+'ms':'--';$('rp-call-lat-tts').textContent=msg.llm_to_tts_ms!=null?Math.round(msg.llm_to_tts_ms)+'ms':'--';setVoiceEvent('Response complete',total+'ms round trip');return;}
2740
+ if(topic==='wakeword_state'){setVoiceEvent('Wake word',msg.state==='active'?'active - listening now':'waiting for wake word');return;}
2741
+ if(topic==='model_loading_status'){if(msg.is_loading){setVoiceAgentState('Loading model','cold start in progress');setVoiceEvent('Model status','loading model into memory');}return;}
2742
+ if(topic==='tool_status'){var names=msg.tool_names||[];setVoiceEvent('Tool activity',names.length?names.join(', '):'tool cycle complete');return;}
2743
+ if(topic && topic!=='lk-chat-topic')return;var text=String(msg.message||msg.text||'').trim();if(!text)return;var role=msg.role==='user'?'user':'assistant',key=role+'|'+text;if(rpVoiceCall.seen[key])return;rpVoiceCall.seen[key]=1;appendVoiceTurn(role,text);saveVoiceTurn(role,text,Object.keys(rpVoiceCall.seen).length);
2744
+ }catch(_){}
2745
+ }
2746
+ function attachVoiceMedia(track,tag){var video=$('rp-call-video'),box=$('rp-call-media');try{track.attach(video);box.classList.add('on');$('rp-call-media-tag').textContent=tag;}catch(_){} }
2747
+ function toggleVoiceCamera(){if(!rpVoiceCall.room)return;var next=!rpVoiceCall.camera,opts=$('rp-call-cam-device').value?{deviceId:$('rp-call-cam-device').value}:undefined;$('rp-call-camera').disabled=true;rpVoiceCall.room.localParticipant.setCameraEnabled(next,opts).then(function(){rpVoiceCall.camera=next;$('rp-call-camera').classList.toggle('on',next);$('rp-call-camera').textContent=next?'CAM ON':'CAM';if(next){var pub=rpVoiceCall.room.localParticipant.getTrackPublication(rpVoiceCall.LK.Track.Source.Camera);if(pub&&pub.track)attachVoiceMedia(pub.track,'CAMERA');refreshBrowserDevices(false);}else if(!rpVoiceCall.screen){$('rp-call-media').classList.remove('on');$('rp-call-video').srcObject=null;}}).catch(function(e){setVoiceEvent('Camera',e.message);}).finally(function(){$('rp-call-camera').disabled=false;});}
2748
+ function toggleVoiceScreen(){if(!rpVoiceCall.room)return;var next=!rpVoiceCall.screen;$('rp-call-screen').disabled=true;rpVoiceCall.room.localParticipant.setScreenShareEnabled(next).then(function(){rpVoiceCall.screen=next;$('rp-call-screen').classList.toggle('on',next);$('rp-call-screen').textContent=next?'SCR ON':'SCR';if(next){var pub=rpVoiceCall.room.localParticipant.getTrackPublication(rpVoiceCall.LK.Track.Source.ScreenShare);if(pub&&pub.track)attachVoiceMedia(pub.track,'SCREEN');}else if(!rpVoiceCall.camera){$('rp-call-media').classList.remove('on');$('rp-call-video').srcObject=null;}}).catch(function(e){setVoiceEvent('Screen share',e.message);}).finally(function(){$('rp-call-screen').disabled=false;});}
2749
+ function sendVoiceMessage(text){if(!rpVoiceCall.room||!text)return Promise.resolve();var payload=new TextEncoder().encode(JSON.stringify({message:text,role:'user',timestamp:Date.now()}));return rpVoiceCall.room.localParticipant.publishData(payload,{reliable:true,topic:'lk-chat-topic'}).then(function(){var key='user|'+text;if(!rpVoiceCall.seen[key]){rpVoiceCall.seen[key]=1;appendVoiceTurn('user',text);saveVoiceTurn('user',text,Object.keys(rpVoiceCall.seen).length);}});}
2637
2750
  function saveVoiceTurn(role,text,sequence){
2638
2751
  if(!rpVoiceCall.session||!rpVoiceCall.eventToken)return;
2639
2752
  fetch('/robopark/api/sessions/'+encodeURIComponent(rpVoiceCall.session)+'/transcript'+QS,{
@@ -2645,15 +2758,15 @@ button.act:disabled{opacity:0.5;cursor:default;}
2645
2758
  var cid=$('rp-call-character-select').value, sid=$('rp-call-stack-select').value;
2646
2759
  if(!cid){setVoiceCallStatus('select a character first','');return;}
2647
2760
  if(rpVoiceCall.room)return;
2648
- setVoiceCallStatus('creating scheduled session...','busy'); $('rp-call-start').disabled=true;
2649
- rpAction('POST','/robopark/api/sessions/voice-call',{character_preset_id:cid,voice_stack_id:sid||null,client_name:getOperatorName()||'RoboPark Operator'})
2761
+ setVoiceCallStatus('requesting microphone...','busy');setVoiceAgentState('Opening channel','requesting browser media permission'); $('rp-call-start').disabled=true;
2762
+ refreshBrowserDevices(true).then(function(){return rpAction('POST','/robopark/api/sessions/voice-call',{character_preset_id:cid,voice_stack_id:sid||null,client_name:getOperatorName()||'RoboPark Operator'});})
2650
2763
  .then(function(call){
2651
2764
  if(!call)throw new Error('scheduler returned no call');
2652
- rpVoiceCall.session=call.session_id; rpVoiceCall.eventToken=call.event_token; rpVoiceCall.seen={}; rpVoiceCall.ending=false;
2765
+ rpVoiceCall.session=call.session_id; rpVoiceCall.eventToken=call.event_token; rpVoiceCall.seen={};rpVoiceCall.messages=0;rpVoiceCall.ending=false;
2653
2766
  $('rp-call-session').textContent=call.session_id; $('rp-call-transcript').innerHTML='<div class="rp-tbl-empty">Connecting to '+esc(call.character&&call.character.name||cid)+'...</div>';
2654
2767
  return ensureVoiceLiveKit().then(function(LK){
2655
- var room=new LK.Room({adaptiveStream:true,dynacast:true}); rpVoiceCall.room=room;
2656
- room.on(LK.RoomEvent.TrackSubscribed,function(track){ if(track.kind==='audio'){var el=track.attach();el.autoplay=true;$('rp-call-audio').appendChild(el);} });
2768
+ var room=new LK.Room({adaptiveStream:true,dynacast:true}); rpVoiceCall.room=room;rpVoiceCall.LK=LK;
2769
+ room.on(LK.RoomEvent.TrackSubscribed,function(track){ if(track.kind==='audio'){var el=track.attach();el.autoplay=true;$('rp-call-audio').appendChild(el);applyVoiceOutputDevice();startVoiceVisualizer(track);setVoiceAgentState('Agent online','voice channel connected');} });
2657
2770
  room.on(LK.RoomEvent.TranscriptionReceived,function(segments,participant){
2658
2771
  for(var i=0;i<(segments||[]).length;i++){
2659
2772
  var seg=segments[i], final=seg.final===true||seg.isFinal===true; if(!final||!String(seg.text||'').trim())continue;
@@ -2662,43 +2775,43 @@ button.act:disabled{opacity:0.5;cursor:default;}
2662
2775
  appendVoiceTurn(role,String(seg.text)); saveVoiceTurn(role,String(seg.text),Object.keys(rpVoiceCall.seen).length);
2663
2776
  }
2664
2777
  });
2665
- room.on(LK.RoomEvent.DataReceived,function(payload,participant,kind,topic){
2666
- if(topic && topic!=='lk-chat-topic')return;
2667
- try{
2668
- var msg=JSON.parse(new TextDecoder().decode(payload));
2669
- var text=String(msg.message||msg.text||'').trim(); if(!text)return;
2670
- var role=msg.role==='user'?'user':'assistant';
2671
- var key=role+'|'+text; if(rpVoiceCall.seen[key])return; rpVoiceCall.seen[key]=1;
2672
- appendVoiceTurn(role,text); saveVoiceTurn(role,text,Object.keys(rpVoiceCall.seen).length);
2673
- }catch(_){}
2674
- });
2778
+ room.on(LK.RoomEvent.DataReceived,handleVoiceData);
2779
+ if(LK.RoomEvent.ParticipantAttributesChanged)room.on(LK.RoomEvent.ParticipantAttributesChanged,function(changed,p){updateAgentFromParticipant(p);});
2780
+ room.on(LK.RoomEvent.ParticipantConnected,function(p){updateAgentFromParticipant(p);setVoiceEvent('Agent connected',p.name||p.identity||'ROBOVOICE worker');});
2675
2781
  room.on(LK.RoomEvent.Disconnected,function(){ if(!rpVoiceCall.ending) finishVoiceCall('connection_lost'); });
2676
- return room.connect(call.server_url,call.token).then(function(){return room.localParticipant.setMicrophoneEnabled(true);});
2782
+ return room.connect(call.server_url,call.token).then(function(){var mic=$('rp-call-mic-device').value,opts=mic?{deviceId:mic}:undefined;return room.localParticipant.setMicrophoneEnabled(true,opts);});
2677
2783
  }).then(function(){
2678
- setVoiceCallStatus('connected - listening','live'); $('rp-call-mute').disabled=false; $('rp-call-end').disabled=false;
2784
+ setVoiceCallStatus('secure channel connected','live');setVoiceAgentState('Listening','speak naturally');setVoiceEvent('Connected','waiting for ROBOVOICE agent');rpVoiceCall.startedAt=Date.now();rpVoiceCall.timer=setInterval(updateVoiceTimer,1000);updateVoiceTimer();
2785
+ $('rp-call-mute').disabled=false;$('rp-call-camera').disabled=false;$('rp-call-screen').disabled=false;$('rp-call-end').disabled=false;$('rp-call-message').disabled=false;$('rp-call-send').disabled=false;
2679
2786
  return fetch('/robopark/api/sessions/'+encodeURIComponent(rpVoiceCall.session)+'/joined'+QS,{method:'POST',headers:{'X-RoboPark-Session-Token':rpVoiceCall.eventToken}}).catch(function(){});
2680
2787
  });
2681
2788
  }).catch(function(e){
2682
- setVoiceCallStatus('call failed: '+(e&&e.message||e),''); $('rp-call-start').disabled=false;
2789
+ setVoiceCallStatus('call failed','');setVoiceAgentState('Unable to connect',e&&e.message||String(e));setVoiceEvent('Call failed',e&&e.message||String(e)); $('rp-call-start').disabled=false;
2790
+ if(rpVoiceCall.session)rpAction('POST','/robopark/api/sessions/'+encodeURIComponent(rpVoiceCall.session)+'/end?reason=connection_failed');
2683
2791
  if(rpVoiceCall.room){try{rpVoiceCall.room.disconnect();}catch(_){} rpVoiceCall.room=null;}
2684
2792
  });
2685
2793
  }
2686
2794
  function toggleVoiceMute(){
2687
2795
  if(!rpVoiceCall.room)return; rpVoiceCall.muted=!rpVoiceCall.muted;
2688
2796
  rpVoiceCall.room.localParticipant.setMicrophoneEnabled(!rpVoiceCall.muted);
2689
- $('rp-call-mute').textContent=rpVoiceCall.muted?'Unmute microphone':'Mute microphone'; setVoiceCallStatus(rpVoiceCall.muted?'connected - microphone muted':'connected - listening','live');
2797
+ $('rp-call-mute').textContent=rpVoiceCall.muted?'MIC OFF':'MIC';$('rp-call-mute').classList.toggle('on',!rpVoiceCall.muted);setVoiceCallStatus(rpVoiceCall.muted?'microphone muted':'secure channel connected','live');
2690
2798
  }
2691
2799
  function finishVoiceCall(reason){
2692
2800
  if(rpVoiceCall.ending)return; rpVoiceCall.ending=true; var session=rpVoiceCall.session, room=rpVoiceCall.room;
2693
- rpVoiceCall.room=null; if(room){try{room.disconnect();}catch(_){}}
2801
+ rpVoiceCall.room=null;if(rpVoiceCall.timer)clearInterval(rpVoiceCall.timer);rpVoiceCall.timer=null;stopVoiceVisualizer();if(room){try{room.disconnect();}catch(_){}}
2694
2802
  if(session) rpAction('POST','/robopark/api/sessions/'+encodeURIComponent(session)+'/end?reason='+encodeURIComponent(reason||'operator'));
2695
- setVoiceCallStatus('call ended',''); $('rp-call-start').disabled=false; $('rp-call-mute').disabled=true; $('rp-call-end').disabled=true;
2696
- $('rp-call-mute').textContent='Mute microphone'; rpVoiceCall.session=null; rpVoiceCall.eventToken=null; rpVoiceCall.muted=false; setTimeout(function(){rpVoiceCall.ending=false;},300);
2803
+ setVoiceCallStatus('call ended','');setVoiceAgentState('Call complete','session transcript saved');setVoiceEvent('Session closed',reason||'operator');$('rp-call-start').disabled=false;['rp-call-mute','rp-call-camera','rp-call-screen','rp-call-end'].forEach(function(id){$(id).disabled=true;$(id).classList.remove('on');});$('rp-call-message').disabled=true;$('rp-call-send').disabled=true;$('rp-call-media').classList.remove('on');$('rp-call-video').srcObject=null;$('rp-call-mute').textContent='MIC';$('rp-call-camera').textContent='CAM';$('rp-call-screen').textContent='SCR';$('rp-call-session').textContent='No active session';$('rp-call-timer').textContent='00:00';rpVoiceCall.session=null;rpVoiceCall.eventToken=null;rpVoiceCall.muted=false;rpVoiceCall.camera=false;rpVoiceCall.screen=false;rpVoiceCall.startedAt=0;setTimeout(function(){rpVoiceCall.ending=false;},300);
2697
2804
  }
2698
2805
  (function(){
2699
- var cs=$('rp-call-character-select'), start=$('rp-call-start'), mute=$('rp-call-mute'), end=$('rp-call-end');
2806
+ var cs=$('rp-call-character-select'),ss=$('rp-call-stack-select'),start=$('rp-call-start'),mute=$('rp-call-mute'),end=$('rp-call-end');
2700
2807
  if(cs)cs.addEventListener('change',paintVoiceCallCharacter); if(start)start.addEventListener('click',startVoiceCall);
2701
- if(mute)mute.addEventListener('click',toggleVoiceMute); if(end)end.addEventListener('click',function(){finishVoiceCall('operator');});
2808
+ if(ss)ss.addEventListener('change',paintVoiceCallStack);if(mute)mute.addEventListener('click',toggleVoiceMute);if(end)end.addEventListener('click',function(){finishVoiceCall('operator');});
2809
+ $('rp-call-camera').addEventListener('click',toggleVoiceCamera);$('rp-call-screen').addEventListener('click',toggleVoiceScreen);$('rp-call-refresh-devices').addEventListener('click',function(){refreshBrowserDevices(true);});
2810
+ $('rp-call-mic-device').addEventListener('change',function(){switchVoiceDevice('audioinput',this.value);});$('rp-call-cam-device').addEventListener('change',function(){switchVoiceDevice('videoinput',this.value);});$('rp-call-speaker-device').addEventListener('change',applyVoiceOutputDevice);
2811
+ function toggleTranscript(){rpVoiceCall.transcriptCollapsed=!rpVoiceCall.transcriptCollapsed;$('rp-call-transcript').classList.toggle('collapsed',rpVoiceCall.transcriptCollapsed);$('rp-call-collapse').textContent=rpVoiceCall.transcriptCollapsed?'expand transcript':'collapse transcript';}
2812
+ $('rp-call-chat-toggle').addEventListener('click',toggleTranscript);$('rp-call-collapse').addEventListener('click',toggleTranscript);
2813
+ $('rp-call-compose').addEventListener('submit',function(e){e.preventDefault();var input=$('rp-call-message'),text=input.value.trim();if(!text)return;$('rp-call-send').disabled=true;sendVoiceMessage(text).then(function(){input.value='';}).catch(function(err){setVoiceEvent('Message failed',err.message);}).finally(function(){$('rp-call-send').disabled=!rpVoiceCall.room;});});
2814
+ refreshBrowserDevices(false);
2702
2815
  })();
2703
2816
 
2704
2817
  var rpHistoryFilter = { robot: '', endReason: '', range: '7d', search: '' };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "infinicode",
3
- "version": "2.8.68",
3
+ "version": "2.8.69",
4
4
  "description": "OpenKernel — provider-agnostic AI execution kernel. Native coding agent + mission-driven execution runtime.",
5
5
  "type": "module",
6
6
  "main": "./dist/kernel/index.js",