neohive 6.1.4 → 6.2.0

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/dashboard.html CHANGED
@@ -402,57 +402,46 @@
402
402
  .agent-card {
403
403
  background: var(--surface-2);
404
404
  border: 1px solid var(--border);
405
- border-radius: 8px;
406
- padding: 8px 12px;
407
- margin-bottom: 6px;
408
- transition: all 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
405
+ border-radius: 10px;
406
+ padding: 10px 12px;
407
+ margin-bottom: 4px;
408
+ transition: background 0.15s ease, border-color 0.15s ease;
409
409
  cursor: pointer;
410
410
  position: relative;
411
411
  overflow: hidden;
412
412
  }
413
413
 
414
414
  .agent-card:hover {
415
- transform: translateX(4px);
416
- border-color: var(--nh-accent-50);
417
415
  background: var(--surface-3);
418
- box-shadow: var(--shadow-sm);
416
+ border-color: var(--border-light);
419
417
  }
420
418
 
421
419
  .agent-card.working, .agent-card.active {
422
- border-left: 3px solid var(--green);
423
- box-shadow: 0 0 15px var(--green-dim);
424
- animation: nh-glow-pulse 2s infinite ease-in-out;
420
+ background: var(--surface-3);
425
421
  }
426
422
 
427
- .agent-card.dead, .agent-card.offline {
428
- opacity: 0.5;
429
- }
430
- .agent-card.unknown {
431
- opacity: 0.4;
432
- border-left: 3px solid var(--nh-text-muted);
433
- }
434
- .agent-card.stale {
435
- opacity: 0.65;
436
- border-left: 3px solid var(--orange);
437
- }
423
+ .agent-card.dead, .agent-card.offline { opacity: 0.45; }
424
+ .agent-card.unknown { opacity: 0.35; }
425
+ .agent-card.stale { opacity: 0.6; }
438
426
 
439
427
  .agent-top {
440
428
  display: flex;
441
429
  align-items: center;
442
- gap: 8px;
430
+ gap: 10px;
443
431
  }
444
432
 
445
433
  .agent-avatar {
446
- width: 26px;
447
- height: 26px;
448
- border-radius: 50%;
434
+ width: 32px;
435
+ height: 32px;
436
+ border-radius: 8px;
449
437
  display: flex;
450
438
  align-items: center;
451
439
  justify-content: center;
452
440
  font-weight: 700;
453
- font-size: 11px;
441
+ font-size: 13px;
454
442
  color: #fff;
455
443
  flex-shrink: 0;
444
+ letter-spacing: -0.02em;
456
445
  }
457
446
 
458
447
  .agent-info { flex: 1; min-width: 0; }
@@ -463,18 +452,18 @@
463
452
  border-radius: 50%;
464
453
  flex-shrink: 0;
465
454
  }
466
- .agent-status-dot.active, .agent-status-dot.working { background: var(--green); box-shadow: var(--nh-green-glow); }
467
- .agent-status-dot.listening { background: var(--nh-blue); box-shadow: 0 0 6px var(--nh-blue); animation: pulse 2s infinite; }
468
- .agent-status-dot.sleeping, .agent-status-dot.idle { background: var(--nh-accent); animation: pulse 2s infinite; }
469
- .agent-status-dot.dead, .agent-status-dot.offline { background: var(--nh-text-muted); }
470
- .agent-status-dot.unknown { background: var(--nh-text-muted); opacity: 0.5; }
471
- .agent-status-dot.stale { background: var(--orange); animation: pulse 2s infinite; }
472
- .agent-status-dot.unresponsive { background: var(--orange, #f97316); box-shadow: 0 0 6px var(--orange, #f97316); animation: pulse 2s infinite; }
473
- .agent-status-dot.stuck { background: var(--red, #ef4444); box-shadow: 0 0 8px var(--red, #ef4444); animation: pulse 1.2s infinite; }
455
+ .agent-status-dot.active, .agent-status-dot.working { background: var(--green); box-shadow: 0 0 4px var(--green); }
456
+ .agent-status-dot.listening { background: var(--nh-blue); box-shadow: 0 0 4px var(--nh-blue); animation: pulse 2s infinite; }
457
+ .agent-status-dot.sleeping, .agent-status-dot.idle { background: var(--nh-accent); }
458
+ .agent-status-dot.dead, .agent-status-dot.offline { background: var(--nh-text-muted); opacity: 0.4; }
459
+ .agent-status-dot.unknown { background: var(--nh-text-muted); opacity: 0.3; }
460
+ .agent-status-dot.stale { background: var(--orange); }
461
+ .agent-status-dot.unresponsive { background: var(--orange, #f97316); animation: pulse 2s infinite; }
462
+ .agent-status-dot.stuck { background: var(--red, #ef4444); animation: pulse 1.2s infinite; }
474
463
 
475
464
  .agent-state-pill { display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 99px; font-size: 10px; font-weight: 600; letter-spacing: 0.02em; line-height: 16px; flex-shrink: 0; }
476
- .agent-state-pill.listening { background: rgba(96,165,250,0.15); color: #60a5fa; }
477
- .agent-state-pill.working { background: rgba(251,191,36,0.15); color: #fbbf24; }
465
+ .agent-state-pill.listening { background: rgba(96,165,250,0.12); color: #60a5fa; }
466
+ .agent-state-pill.working { background: rgba(74,222,128,0.12); color: #4ade80; }
478
467
  .agent-current-tool { font-size: 10px; color: var(--nh-text-muted, #6b7280); margin-top: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 150px; }
479
468
 
480
469
  .agent-card.stuck { border-color: rgba(239, 68, 68, 0.3); }
@@ -483,20 +472,22 @@
483
472
  .agent-name {
484
473
  font-weight: 600;
485
474
  font-size: 13px;
475
+ color: var(--text);
486
476
  white-space: nowrap;
487
477
  overflow: hidden;
488
478
  text-overflow: ellipsis;
489
479
  }
490
480
 
491
481
  .agent-subtitle {
492
- font-size: 12px;
482
+ font-size: 11px;
493
483
  color: var(--text-muted);
494
484
  white-space: nowrap;
495
485
  overflow: hidden;
496
486
  text-overflow: ellipsis;
487
+ margin-top: 1px;
497
488
  }
498
489
 
499
- /* ===== AGENT SKILLS ===== */
490
+ /* ===== AGENT SKILLS (visible in popup, hidden in card) ===== */
500
491
  .agent-skills {
501
492
  display: flex;
502
493
  flex-wrap: wrap;
@@ -508,66 +499,45 @@
508
499
  font-size: 9px;
509
500
  padding: 1px 5px;
510
501
  border-radius: 4px;
511
- background: var(--surface-3);
512
- color: var(--text-dim);
513
- border: 1px solid var(--border);
502
+ background: var(--accent-dim);
503
+ color: var(--accent);
504
+ border: none;
505
+ font-weight: 500;
514
506
  }
515
507
 
516
- /* ===== NUDGE & SPARKLINE ===== */
508
+ /* ===== NUDGE ===== */
517
509
  .agent-nudge-btn {
518
510
  position: absolute;
519
511
  top: 8px;
520
512
  right: 8px;
521
- width: 24px;
522
- height: 24px;
513
+ width: 22px;
514
+ height: 22px;
523
515
  border-radius: 6px;
524
- background: var(--nh-red-10);
525
- border: 1px solid var(--nh-red);
526
- color: var(--nh-red);
516
+ background: transparent;
517
+ border: 1px solid transparent;
518
+ color: var(--text-muted);
527
519
  display: flex;
528
520
  align-items: center;
529
521
  justify-content: center;
530
522
  cursor: pointer;
531
523
  opacity: 0;
532
- transition: all 0.2s ease;
524
+ transition: all 0.15s ease;
533
525
  z-index: 10;
534
526
  }
535
- .agent-card:hover .agent-nudge-btn { opacity: 1; }
536
- .agent-nudge-btn:hover { background: var(--nh-red); color: #fff; transform: scale(1.1); box-shadow: 0 0 10px rgba(239, 68, 68, 0.4); }
527
+ .agent-card:hover .agent-nudge-btn { opacity: 0.6; }
528
+ .agent-nudge-btn:hover { opacity: 1 !important; color: var(--red); }
537
529
 
538
- .listen-sparkline {
539
- display: flex;
540
- gap: 3px;
541
- margin-top: 8px;
542
- padding-top: 4px;
543
- border-top: 1px solid var(--nh-divider);
544
- align-items: center;
545
- }
546
- .listen-sparkline-label {
547
- font-size: 9px;
548
- color: var(--text-muted);
549
- text-transform: uppercase;
550
- font-weight: 700;
551
- margin-right: 4px;
552
- letter-spacing: 0.5px;
553
- }
554
- .hb-dot {
555
- width: 6px;
556
- height: 6px;
557
- border-radius: 50%;
558
- background: var(--nh-divider-strong);
559
- transition: all 0.3s ease;
560
- }
561
- .hb-dot.active {
562
- background: var(--nh-green);
563
- box-shadow: 0 0 6px var(--nh-green);
564
- }
530
+ /* Sparkline kept for JS compat but hidden in card */
531
+ .listen-sparkline { display: none; }
532
+ .listen-sparkline-label { display: none; }
533
+ .hb-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--nh-divider-strong); }
534
+ .hb-dot.active { background: var(--nh-green); }
565
535
  .hb-dot.stale { background: var(--nh-orange); opacity: 0.5; }
566
536
  .hb-dot.empty { background: transparent; border: 1px solid var(--nh-divider); }
567
537
 
568
538
  @keyframes stuck-pulse {
569
539
  0% { border-color: var(--nh-divider); }
570
- 50% { border-color: var(--nh-red); box-shadow: inset 0 0 10px var(--nh-red-10); }
540
+ 50% { border-color: var(--nh-red); }
571
541
  100% { border-color: var(--nh-divider); }
572
542
  }
573
543
  .agent-card.stuck {
@@ -1073,7 +1043,7 @@
1073
1043
  .msg-avatar {
1074
1044
  width: 36px;
1075
1045
  height: 36px;
1076
- border-radius: 9999px;
1046
+ border-radius: 8px;
1077
1047
  display: flex;
1078
1048
  align-items: center;
1079
1049
  justify-content: center;
@@ -1845,18 +1815,17 @@
1845
1815
  margin: 1px 6px;
1846
1816
  position: relative;
1847
1817
  }
1848
- .nav-item:hover { background: var(--nh-card); color: var(--text); }
1818
+ .nav-item:hover { background: var(--surface-2); color: var(--text); }
1849
1819
  .nav-item.active {
1850
- background: rgba(245, 158, 11, 0.1);
1820
+ background: var(--surface-2);
1851
1821
  border-color: transparent;
1852
- box-shadow: inset 3px 0 0 var(--nh-accent);
1853
1822
  }
1854
1823
  .nav-item .nav-icon { line-height: 0; flex-shrink: 0; width: 20px; min-width: 20px; display: flex; align-items: center; justify-content: center; color: var(--text-muted); }
1855
1824
  .nav-item .nav-icon svg { flex-shrink: 0; width: 16px; height: 16px; }
1856
- .nav-item.active .nav-icon { color: var(--accent); }
1825
+ .nav-item.active .nav-icon { color: var(--text); }
1857
1826
  .nav-item:hover .nav-icon { color: var(--text); }
1858
1827
  .nav-item .nav-text { font-size: 14px; font-weight: 500; color: var(--text-dim); }
1859
- .nav-item.active .nav-text { color: var(--accent); font-weight: 500; }
1828
+ .nav-item.active .nav-text { color: var(--text); font-weight: 600; }
1860
1829
  .nav-item:hover .nav-text { color: var(--text); }
1861
1830
  .nav-sidebar.collapsed .nav-item { justify-content: center; padding: 10px 4px; margin: 0; }
1862
1831
  .nav-sidebar.collapsed .nav-item .nav-text { display: none; }
@@ -2584,6 +2553,8 @@
2584
2553
  .kanban-title.blocked { background: var(--red-dim); color: var(--red); }
2585
2554
  .kanban-title.blocked_permanent { background: var(--red); color: #fff; }
2586
2555
 
2556
+ .task-card.blocked_permanent { border-color: var(--red); border-left: 3px solid var(--red); background: var(--red-dim); }
2557
+
2587
2558
  .kanban-count {
2588
2559
  font-size: 10px;
2589
2560
  opacity: 0.7;
@@ -2941,7 +2912,6 @@
2941
2912
  display: flex;
2942
2913
  align-items: flex-start;
2943
2914
  gap: 8px;
2944
- border-left: 3px solid var(--nh-border, var(--border));
2945
2915
  }
2946
2916
  .system-event-banner .event-icon {
2947
2917
  flex-shrink: 0;
@@ -2973,22 +2943,16 @@
2973
2943
  flex-shrink: 0;
2974
2944
  margin-top: 2px;
2975
2945
  }
2976
- /* Type colors — left border + icon color */
2977
- .system-event-banner.evt-critical { border-left-color: #ef4444; }
2946
+ /* Type colors — icon + text */
2978
2947
  .system-event-banner.evt-critical .event-icon { color: #ef4444; }
2979
2948
  .system-event-banner.evt-critical .event-text { color: #ef4444; font-weight: 500; }
2980
- .system-event-banner.evt-warning { border-left-color: #f59e0b; }
2981
2949
  .system-event-banner.evt-warning .event-icon { color: #f59e0b; }
2982
2950
  .system-event-banner.evt-warning .event-text { color: #f59e0b; }
2983
- .system-event-banner.evt-event { border-left-color: #22c55e; }
2984
2951
  .system-event-banner.evt-event .event-icon { color: #22c55e; }
2985
- .system-event-banner.evt-review { border-left-color: #3b82f6; }
2986
2952
  .system-event-banner.evt-review .event-icon { color: #3b82f6; }
2987
- .system-event-banner.evt-status { border-left-color: #6b7280; }
2988
2953
  .system-event-banner.evt-status .event-icon { color: #6b7280; }
2989
- .system-event-banner.evt-role { border-left-color: #8b5cf6; }
2990
2954
  .system-event-banner.evt-role .event-icon { color: #8b5cf6; }
2991
- .system-event-banner.evt-violation { border-left-color: #ef4444; background: rgba(239,68,68,0.05); }
2955
+ .system-event-banner.evt-violation { background: rgba(239,68,68,0.05); }
2992
2956
  .system-event-banner.evt-violation .event-icon { color: #ef4444; }
2993
2957
  .system-event-banner.evt-violation .event-text { color: var(--text); font-weight: 500; }
2994
2958
 
@@ -3244,9 +3208,9 @@
3244
3208
 
3245
3209
  /* ===== v3.0: PROFILE AVATAR ===== */
3246
3210
  .agent-avatar-img {
3247
- width: 28px;
3248
- height: 28px;
3249
- border-radius: 50%;
3211
+ width: 32px;
3212
+ height: 32px;
3213
+ border-radius: 8px;
3250
3214
  object-fit: cover;
3251
3215
  flex-shrink: 0;
3252
3216
  }
@@ -3254,7 +3218,7 @@
3254
3218
  .msg-avatar-img {
3255
3219
  width: 36px;
3256
3220
  height: 36px;
3257
- border-radius: 50%;
3221
+ border-radius: 8px;
3258
3222
  object-fit: cover;
3259
3223
  flex-shrink: 0;
3260
3224
  margin-top: 2px;
@@ -3263,33 +3227,27 @@
3263
3227
 
3264
3228
  .role-badge {
3265
3229
  font-size: 9px;
3266
- padding: 1px 5px;
3230
+ padding: 2px 6px;
3267
3231
  border-radius: 6px;
3268
3232
  font-weight: 600;
3269
- background: var(--purple-dim);
3270
- color: var(--purple);
3271
3233
  margin-left: 4px;
3272
3234
  }
3273
3235
 
3274
- /* ===== v3.0: PROFILE POPUP (3-TAB) ===== */
3236
+ /* ===== v4.0: AGENT POPUP (stats-first, compact) ===== */
3275
3237
  .profile-popup {
3276
3238
  display: none;
3277
3239
  position: fixed;
3278
3240
  z-index: 300;
3279
3241
  background: var(--surface);
3280
- background-image: var(--gradient-surface);
3281
3242
  border: 1px solid var(--border-light);
3282
- border-radius: 14px;
3243
+ border-radius: 12px;
3283
3244
  padding: 0;
3284
- width: 310px;
3245
+ width: 280px;
3285
3246
  box-sizing: border-box;
3286
- max-height: 80vh;
3287
- overflow-x: hidden;
3288
- overflow-y: hidden;
3247
+ max-height: 70vh;
3248
+ overflow: hidden;
3289
3249
  word-break: break-word;
3290
- box-shadow: var(--shadow-lg), 0 0 40px rgba(0,0,0,0.2);
3291
- backdrop-filter: blur(12px);
3292
- -webkit-backdrop-filter: blur(12px);
3250
+ box-shadow: 0 8px 30px rgba(0,0,0,0.35);
3293
3251
  }
3294
3252
 
3295
3253
  .profile-popup.open { display: block; }
@@ -3297,40 +3255,40 @@
3297
3255
  .profile-popup-header {
3298
3256
  display: flex;
3299
3257
  align-items: center;
3300
- gap: 12px;
3301
- padding: 16px 18px 12px;
3258
+ gap: 10px;
3259
+ padding: 14px 16px 10px;
3302
3260
  }
3303
3261
 
3304
3262
  .profile-popup-avatar {
3305
- width: 44px;
3306
- height: 44px;
3307
- border-radius: 50%;
3263
+ width: 36px;
3264
+ height: 36px;
3265
+ border-radius: 8px;
3308
3266
  object-fit: cover;
3309
3267
  flex-shrink: 0;
3310
3268
  }
3311
3269
 
3312
3270
  .profile-popup-name {
3313
3271
  font-weight: 700;
3314
- font-size: 15px;
3272
+ font-size: 14px;
3315
3273
  line-height: 1.2;
3316
3274
  }
3317
3275
 
3318
3276
  .profile-popup-id {
3319
- font-size: 11px;
3277
+ font-size: 10px;
3320
3278
  color: var(--text-muted);
3279
+ margin-top: 1px;
3321
3280
  }
3322
3281
 
3323
3282
  .pp-status-badge {
3324
3283
  display: inline-flex;
3325
3284
  align-items: center;
3326
- gap: 4px;
3327
- font-size: 10px;
3285
+ font-size: 9px;
3328
3286
  font-weight: 600;
3329
3287
  text-transform: uppercase;
3330
3288
  letter-spacing: 0.3px;
3331
- padding: 2px 8px;
3332
- border-radius: 10px;
3333
- margin-left: 6px;
3289
+ padding: 1px 6px;
3290
+ border-radius: 6px;
3291
+ margin-left: 4px;
3334
3292
  }
3335
3293
  .pp-status-badge.online { background: rgba(52,211,153,0.15); color: #34d399; }
3336
3294
  .pp-status-badge.offline { background: rgba(156,163,175,0.15); color: #9ca3af; }
@@ -3338,36 +3296,15 @@
3338
3296
  .pp-status-badge.unresponsive { background: rgba(249,115,22,0.15); color: #f97316; }
3339
3297
  .pp-status-badge.stuck { background: rgba(239,68,68,0.15); color: #ef4444; }
3340
3298
 
3341
- /* Tab bar */
3342
- .pp-tabs {
3343
- display: flex;
3344
- border-bottom: 1px solid var(--border);
3345
- padding: 0 18px;
3346
- gap: 0;
3347
- }
3348
- .pp-tab {
3349
- flex: 1;
3350
- padding: 8px 0;
3351
- font-size: 11px;
3352
- font-weight: 600;
3353
- text-transform: uppercase;
3354
- letter-spacing: 0.4px;
3355
- color: var(--text-muted);
3356
- background: none;
3357
- border: none;
3358
- border-bottom: 2px solid transparent;
3359
- cursor: pointer;
3360
- text-align: center;
3361
- transition: color 0.15s, border-color 0.15s;
3362
- }
3363
- .pp-tab:hover { color: var(--text); }
3364
- .pp-tab.active { color: var(--nh-accent); border-bottom-color: var(--nh-accent); }
3299
+ /* Tabs hidden by default — kept for JS compat */
3300
+ .pp-tabs { display: none; }
3301
+ .pp-tab { display: none; }
3365
3302
 
3366
- /* Tab content */
3303
+ /* Tab content — stats always visible */
3367
3304
  .pp-tab-content {
3368
3305
  display: none;
3369
- padding: 14px 18px 16px;
3370
- max-height: calc(80vh - 140px);
3306
+ padding: 0 16px 14px;
3307
+ max-height: calc(70vh - 100px);
3371
3308
  overflow-y: auto;
3372
3309
  overflow-x: hidden;
3373
3310
  }
@@ -3398,12 +3335,11 @@
3398
3335
  display: grid;
3399
3336
  grid-template-columns: 1fr 1fr;
3400
3337
  gap: 6px;
3401
- margin-top: 8px;
3402
3338
  }
3403
3339
 
3404
3340
  .profile-popup-stat {
3405
3341
  font-size: 11px;
3406
- color: var(--text-dim);
3342
+ color: var(--text-muted);
3407
3343
  background: var(--surface-2);
3408
3344
  border-radius: 8px;
3409
3345
  padding: 8px 10px;
@@ -3413,9 +3349,38 @@
3413
3349
  font-weight: 600;
3414
3350
  color: var(--text);
3415
3351
  display: block;
3416
- font-size: 16px;
3417
- margin-bottom: 2px;
3352
+ font-size: 15px;
3353
+ margin-bottom: 1px;
3354
+ }
3355
+
3356
+ .pp-quick-actions {
3357
+ display: flex;
3358
+ gap: 6px;
3359
+ padding: 0 16px 12px;
3360
+ border-top: 1px solid var(--border);
3361
+ padding-top: 10px;
3362
+ margin-top: 2px;
3363
+ }
3364
+ .pp-quick-action {
3365
+ flex: 1;
3366
+ display: flex;
3367
+ align-items: center;
3368
+ justify-content: center;
3369
+ gap: 5px;
3370
+ background: var(--surface-2);
3371
+ border: 1px solid var(--border);
3372
+ border-radius: 6px;
3373
+ padding: 7px 0;
3374
+ font-size: 11px;
3375
+ font-weight: 600;
3376
+ font-family: inherit;
3377
+ color: var(--text-dim);
3378
+ cursor: pointer;
3379
+ text-align: center;
3380
+ transition: all 0.15s;
3418
3381
  }
3382
+ .pp-quick-action:hover { background: var(--accent-dim); color: var(--accent); border-color: var(--accent); }
3383
+ .pp-quick-action svg { width: 13px; height: 13px; stroke: currentColor; fill: none; stroke-width: 1.8; flex-shrink: 0; }
3419
3384
 
3420
3385
  /* Actions tab */
3421
3386
  .pp-actions-grid {
@@ -4250,7 +4215,7 @@
4250
4215
  .avatar-option {
4251
4216
  width: 44px;
4252
4217
  height: 44px;
4253
- border-radius: 50%;
4218
+ border-radius: 10px;
4254
4219
  border: 2px solid var(--border);
4255
4220
  cursor: pointer;
4256
4221
  transition: all 0.15s;
@@ -4265,7 +4230,7 @@
4265
4230
  .char-designer {
4266
4231
  position: fixed;
4267
4232
  top: 0; right: 0; bottom: 0;
4268
- width: 480px;
4233
+ width: 380px;
4269
4234
  max-width: 100vw;
4270
4235
  background: var(--surface);
4271
4236
  border-left: 1px solid var(--border);
@@ -4305,28 +4270,13 @@
4305
4270
  flex: 1; overflow-y: auto; padding: 0;
4306
4271
  }
4307
4272
 
4308
- .cd-preview {
4309
- width: 100%;
4310
- height: 250px;
4311
- background: #12151c;
4312
- position: relative;
4313
- border-bottom: 1px solid var(--border);
4314
- cursor: grab;
4315
- }
4316
- .cd-preview:active { cursor: grabbing; }
4317
- .cd-preview canvas { display: block; width: 100% !important; height: 100% !important; }
4318
-
4319
4273
  .cd-info-section {
4320
4274
  padding: 14px 18px;
4321
- display: flex; flex-direction: column; gap: 8px;
4275
+ display: flex; flex-direction: column; gap: 10px;
4322
4276
  border-bottom: 1px solid var(--border);
4323
4277
  }
4324
- .cd-info-section .cd-avatar-row {
4325
- display: flex; align-items: center; gap: 10px; margin-bottom: 4px;
4326
- }
4327
- .cd-info-section .cd-avatar-row .avatar-option { width: 36px; height: 36px; }
4328
4278
  .cd-avatar-grid {
4329
- display: grid; grid-template-columns: repeat(6, 1fr); gap: 5px; margin-bottom: 6px;
4279
+ display: grid; grid-template-columns: repeat(6, 1fr); gap: 6px; margin-bottom: 2px;
4330
4280
  }
4331
4281
  .cd-avatar-upload-row {
4332
4282
  display: flex; gap: 6px; align-items: center;
@@ -4353,82 +4303,6 @@
4353
4303
  border-color: var(--accent);
4354
4304
  }
4355
4305
 
4356
- .cd-tabs {
4357
- display: flex;
4358
- padding: 0 18px;
4359
- border-bottom: 1px solid var(--border);
4360
- gap: 0;
4361
- flex-shrink: 0;
4362
- overflow-x: auto;
4363
- }
4364
- .cd-tab {
4365
- background: none; border: none; border-bottom: 2px solid transparent;
4366
- color: var(--text-dim); padding: 10px 12px; font-size: 12px; font-weight: 600;
4367
- cursor: pointer; transition: all 0.15s; white-space: nowrap;
4368
- font-family: inherit;
4369
- }
4370
- .cd-tab:hover { color: var(--text); background: var(--surface-2); }
4371
- .cd-tab.active { color: var(--accent); border-bottom-color: var(--accent); }
4372
-
4373
- .cd-tab-content {
4374
- padding: 14px 18px;
4375
- display: none;
4376
- flex-direction: column;
4377
- gap: 14px;
4378
- }
4379
- .cd-tab-content.active { display: flex; }
4380
-
4381
- .cd-subsection-label {
4382
- font-size: 10px; color: var(--text-muted);
4383
- text-transform: uppercase; letter-spacing: 0.5px;
4384
- margin-bottom: 2px;
4385
- }
4386
-
4387
- .cd-item-grid {
4388
- display: grid;
4389
- grid-template-columns: repeat(3, 1fr);
4390
- gap: 6px;
4391
- }
4392
-
4393
- .cd-item-card {
4394
- display: flex; flex-direction: column;
4395
- align-items: center; justify-content: center;
4396
- gap: 4px;
4397
- background: var(--surface-2);
4398
- border: 2px solid var(--border);
4399
- border-radius: 8px;
4400
- padding: 10px 6px;
4401
- cursor: pointer;
4402
- transition: all 0.15s;
4403
- font-size: 11px; color: var(--text-dim);
4404
- text-align: center;
4405
- min-height: 56px;
4406
- }
4407
- .cd-item-card:hover { border-color: var(--border-light); color: var(--text); background: var(--surface-3); }
4408
- .cd-item-card.active {
4409
- border-color: var(--accent);
4410
- background: var(--accent-dim);
4411
- color: var(--text);
4412
- box-shadow: 0 0 0 1px var(--accent-glow);
4413
- }
4414
- .cd-item-card .cd-card-icon { font-size: 18px; line-height: 1; }
4415
- .cd-item-card .cd-card-label { font-size: 10px; font-weight: 500; }
4416
-
4417
- .cd-color-row {
4418
- display: flex; align-items: center; gap: 10px;
4419
- }
4420
- .cd-color-row label {
4421
- font-size: 11px; color: var(--text-dim); min-width: 70px;
4422
- }
4423
- .cd-color-row input[type="color"] {
4424
- width: 40px; height: 28px; border: 1px solid var(--border);
4425
- border-radius: 6px; background: var(--surface-2);
4426
- cursor: pointer; padding: 2px;
4427
- }
4428
- .cd-color-row .cd-color-hex {
4429
- font-size: 11px; color: var(--text-muted); font-family: monospace;
4430
- }
4431
-
4432
4306
  .cd-footer {
4433
4307
  display: flex; gap: 10px; padding: 14px 18px;
4434
4308
  border-top: 1px solid var(--border);
@@ -4533,7 +4407,7 @@
4533
4407
  <span class="nav-icon"><svg viewBox="0 0 16 16" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="2" y="2" width="12" height="12" rx="2"/><path d="M5 8l2 2 4-4"/></svg></span><span class="nav-text">Tasks</span>
4534
4408
  </div>
4535
4409
  <div class="nav-item" data-view="workflows" onclick="switchView('workflows')">
4536
- <span class="nav-icon"><svg viewBox="0 0 16 16" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M2 8a6 6 0 0111 0M14 8a6 6 0 01-11 0"/><path d="M13 5v3h-3M3 11v-3h3"/></svg></span><span class="nav-text">Workflows</span>
4410
+ <span class="nav-icon"><svg viewBox="0 0 16 16" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="3" cy="8" r="1.5"/><circle cx="13" cy="4" r="1.5"/><circle cx="13" cy="12" r="1.5"/><path d="M4.5 7.2L11.5 4.5M4.5 8.8L11.5 11.5"/></svg></span><span class="nav-text">Workflows</span>
4537
4411
  </div>
4538
4412
  <div class="nav-item" data-view="stats" onclick="switchView('stats')">
4539
4413
  <span class="nav-icon"><svg viewBox="0 0 16 16" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M3 14V8M8 14V3M13 14V6"/></svg></span><span class="nav-text">Analytics</span>
@@ -4548,9 +4422,8 @@
4548
4422
  <div class="nav-sidebar-label">AGENTS</div>
4549
4423
  <div id="agents-list"></div>
4550
4424
  </div>
4551
- <div class="nav-sidebar-divider"></div>
4552
- <!-- Project -->
4553
- <div class="nav-sidebar-section nav-sidebar-project">
4425
+ <!-- Project (hidden — single-project mode) -->
4426
+ <div class="nav-sidebar-section nav-sidebar-project" style="display:none">
4554
4427
  <div class="nav-sidebar-label" style="display:flex;align-items:center;justify-content:space-between;gap:6px">
4555
4428
  <span>PROJECT</span>
4556
4429
  <span style="display:flex;align-items:center;gap:2px">
@@ -4620,14 +4493,15 @@
4620
4493
  <div class="branch-tabs" id="branch-tabs"></div>
4621
4494
  <div class="search-bar" id="search-bar">
4622
4495
  <input class="search-input" id="search-input" placeholder="Search messages... ( / )" oninput="onSearch()">
4623
- <button id="search-all-btn" onclick="toggleSearchAll()" title="Search across all projects" style="background:var(--surface-2);border:1px solid var(--border);border-radius:6px;padding:4px 8px;font-size:10px;cursor:pointer;color:var(--text-muted);white-space:nowrap;transition:all 0.2s">All Projects</button>
4624
- <button id="deep-search-btn" onclick="deepSearch()" title="Search full history (server-side, includes compacted messages and channels)" style="background:var(--surface-2);border:1px solid var(--border);border-radius:6px;padding:4px 8px;font-size:10px;cursor:pointer;color:var(--text-muted);white-space:nowrap;transition:all 0.2s">Deep Search</button>
4625
4496
  <span class="search-count" id="search-count"></span>
4626
4497
  <button class="compact-toggle" id="compact-toggle" onclick="toggleCompactMode()" title="Toggle compact view">Compact</button>
4627
4498
  <select id="conversation-select" onchange="loadConversation()" title="Load saved conversation" style="background:var(--surface-2);border:1px solid var(--border);border-radius:6px;padding:4px 8px;font-size:10px;cursor:pointer;color:var(--text-muted)"><option value="">Current</option></select>
4628
4499
  <button onclick="newConversation()" title="Archive current and start fresh" style="background:var(--surface-2);border:1px solid var(--border);border-radius:6px;padding:4px 8px;font-size:10px;cursor:pointer;color:var(--text-muted);white-space:nowrap;transition:all 0.2s">New Conversation</button>
4629
- <button onclick="clearMessages()" title="Clear all messages for this project" style="background:#4a2020;border:1px solid #6a3030;border-radius:6px;padding:4px 8px;font-size:10px;cursor:pointer;color:#ff6b6b;white-space:nowrap;transition:all 0.2s">Clear Messages</button>
4500
+ <button onclick="scrollToLatest()" title="Scroll to latest message" style="background:var(--surface-2);border:1px solid var(--border);border-radius:6px;padding:4px 8px;font-size:10px;cursor:pointer;color:var(--text-muted);white-space:nowrap;transition:all 0.2s;display:flex;align-items:center;gap:4px"><svg viewBox="0 0 16 16" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M8 3v10M4 9l4 4 4-4"/></svg>Latest</button>
4630
4501
  </div>
4502
+ <!-- Hidden but preserved for JS compatibility -->
4503
+ <button id="search-all-btn" style="display:none"></button>
4504
+ <button id="deep-search-btn" style="display:none"></button>
4631
4505
  <div class="channel-filter-bar" id="channel-filter-bar" style="display:none"></div>
4632
4506
  <div class="pinned-section" id="pinned-section">
4633
4507
  <div class="pinned-header" onclick="togglePinnedSection()"><span>Pinned Messages</span><span class="pinned-toggle" id="pinned-toggle">Hide</span></div>
@@ -4682,52 +4556,42 @@
4682
4556
  <div class="profile-popup-id" id="pp-id"></div>
4683
4557
  </div>
4684
4558
  </div>
4685
- <div class="pp-tabs">
4559
+ <!-- Tabs (hidden but kept for JS compat) -->
4560
+ <div class="pp-tabs" style="display:none">
4686
4561
  <button class="pp-tab active" data-tab="stats" onclick="switchPpTab('stats')">Stats</button>
4687
4562
  <button class="pp-tab" data-tab="actions" onclick="switchPpTab('actions')">Actions</button>
4688
4563
  <button class="pp-tab" data-tab="profile" onclick="switchPpTab('profile')">Profile</button>
4689
- <button class="pp-tab" data-tab="terminal" onclick="switchPpTab('terminal', true)" style="display:none">Terminal</button>
4564
+ <button class="pp-tab" data-tab="terminal" onclick="switchPpTab('terminal', true)">Terminal</button>
4690
4565
  </div>
4691
- <!-- Stats Tab -->
4566
+ <!-- Stats (always visible) -->
4692
4567
  <div class="pp-tab-content active" id="pp-tab-stats">
4693
- <div id="pp-badges" style="display:flex;gap:6px;flex-wrap:wrap;margin-bottom:10px"></div>
4568
+ <div id="pp-badges" style="display:flex;gap:4px;flex-wrap:wrap;margin-bottom:8px"></div>
4694
4569
  <div class="profile-popup-stats" id="pp-stats"></div>
4695
- <div id="pp-skills-readonly" style="margin-top:12px"></div>
4696
- </div>
4697
- <!-- Actions Tab -->
4698
- <div class="pp-tab-content" id="pp-tab-actions">
4699
- <div class="pp-actions-grid" id="pp-actions"></div>
4570
+ <div id="pp-skills-readonly" style="margin-top:10px"></div>
4700
4571
  </div>
4701
- <!-- Profile Tab -->
4702
- <div class="pp-tab-content" id="pp-tab-profile">
4572
+ <!-- Quick actions row -->
4573
+ <div class="pp-quick-actions" id="pp-quick-actions"></div>
4574
+ <!-- Hidden tabs for JS compat -->
4575
+ <div class="pp-tab-content" id="pp-tab-actions" style="display:none"><div class="pp-actions-grid" id="pp-actions"></div></div>
4576
+ <div class="pp-tab-content" id="pp-tab-profile" style="display:none">
4703
4577
  <div class="profile-popup-bio" id="pp-bio"></div>
4704
- <div id="pp-skills-container" style="margin-top:10px;padding-top:10px;border-top:1px solid var(--border)"></div>
4705
- <button class="btn btn-primary" style="width:100%;margin-top:12px" id="pp-edit-btn" onclick="openProfileEditor()">Edit Profile</button>
4706
- <button class="btn btn-danger" style="width:100%;margin-top:6px" id="pp-delete-btn" onclick="confirmDeleteAgent()">Delete Agent</button>
4578
+ <div id="pp-skills-container"></div>
4579
+ <button class="btn btn-danger" style="display:none" id="pp-delete-btn" onclick="confirmDeleteAgent()">Delete Agent</button>
4707
4580
  </div>
4708
- <!-- Terminal Tab -->
4709
4581
  <div class="pp-tab-content" id="pp-tab-terminal" style="display:none">
4710
- <div style="font-size:10px;text-transform:uppercase;letter-spacing:0.5px;color:var(--text-muted);margin-bottom:6px">Live Terminal Bridge</div>
4711
- <div class="terminal-wrapper">
4712
- <div id="pp-terminal-container"></div>
4713
- </div>
4714
- <div style="font-size:10px;color:var(--text-muted);margin-top:8px;display:flex;justify-content:space-between">
4715
- <span>Real-time agent stream</span>
4716
- <span id="terminal-status">Ready</span>
4717
- </div>
4582
+ <div class="terminal-wrapper"><div id="pp-terminal-container"></div></div>
4583
+ <span id="terminal-status" style="display:none">Ready</span>
4718
4584
  </div>
4719
4585
  </div>
4720
4586
 
4721
- <!-- Character Designer Panel -->
4587
+ <!-- Profile Editor Panel (replaces Character Designer) -->
4722
4588
  <div class="char-designer-backdrop" id="cd-backdrop" onclick="closeProfileEditor()"></div>
4723
4589
  <div class="char-designer" id="char-designer">
4724
4590
  <div class="cd-header">
4725
- <h3>Character Designer</h3>
4591
+ <h3>Edit Profile</h3>
4726
4592
  <button class="cd-close" onclick="closeProfileEditor()">&times;</button>
4727
4593
  </div>
4728
4594
  <div class="cd-body">
4729
-
4730
- <!-- Info Section: Avatar + Name/Role/Bio -->
4731
4595
  <div class="cd-info-section">
4732
4596
  <div class="cd-field">
4733
4597
  <label>Avatar</label>
@@ -4744,349 +4608,52 @@
4744
4608
  <label>Display Name</label>
4745
4609
  <input type="text" id="pe-display-name" maxlength="30">
4746
4610
  </div>
4747
- <div style="display:flex;gap:8px">
4748
- <div class="cd-field" style="flex:1">
4749
- <label>Role</label>
4750
- <input type="text" id="pe-role" maxlength="30" placeholder="e.g. Architect, Coder">
4751
- </div>
4752
- </div>
4753
4611
  <div class="cd-field">
4754
- <label>Bio</label>
4755
- <textarea id="pe-bio" maxlength="200" rows="2" placeholder="Short description..."></textarea>
4612
+ <label>Role</label>
4613
+ <input type="text" id="pe-role" maxlength="30" placeholder="e.g. Architect, Coder">
4756
4614
  </div>
4615
+ <textarea id="pe-bio" style="display:none"></textarea>
4757
4616
  </div>
4758
4617
 
4759
- <!-- Tabs -->
4760
- <div class="cd-tabs">
4761
- <button class="cd-tab active" onclick="cdSwitchTab('body', this)">Body</button>
4762
- <button class="cd-tab" onclick="cdSwitchTab('hair', this)">Hair</button>
4763
- <button class="cd-tab" onclick="cdSwitchTab('face', this)">Face</button>
4764
- <button class="cd-tab" onclick="cdSwitchTab('outfit', this)">Outfit</button>
4765
- <button class="cd-tab" onclick="cdSwitchTab('accessories', this)">Acc.</button>
4766
- <button class="cd-tab" onclick="cdSwitchTab('stats', this)">Stats</button>
4618
+ <!-- Stats -->
4619
+ <div class="cd-info-section" style="border-bottom:none">
4620
+ <div style="font-size:10px;text-transform:uppercase;letter-spacing:0.5px;color:var(--text-muted)">Stats</div>
4621
+ <div id="pe-stats-container" style="display:grid;grid-template-columns:1fr 1fr;gap:6px;font-size:12px;color:var(--text)"></div>
4767
4622
  </div>
4768
4623
 
4769
- <!-- Tab: Stats -->
4770
- <div class="cd-tab-content" id="cd-tab-stats">
4771
- <div id="pe-stats-container" style="display:grid;grid-template-columns:1fr 1fr;gap:8px;font-size:12px;color:var(--text)"></div>
4772
- </div>
4773
-
4774
- <!-- Tab: Body -->
4775
- <div class="cd-tab-content active" id="cd-tab-body">
4776
- <div class="cd-color-row">
4777
- <label>Skin color</label>
4778
- <input type="color" id="pe-head-color" value="#FFD5B8" onchange="cdOnChange()">
4779
- <span class="cd-color-hex" id="cd-skin-hex">#FFD5B8</span>
4780
- </div>
4781
- <div>
4782
- <div class="cd-subsection-label">Body Type</div>
4783
- <div class="cd-item-grid" id="cd-body-types">
4784
- <div class="cd-item-card" data-value="default" onclick="cdPickBodyType(this)">
4785
- <span class="cd-card-icon">&#x1f9cd;</span>
4786
- <span class="cd-card-label">Default</span>
4787
- </div>
4788
- <div class="cd-item-card" data-value="stocky" onclick="cdPickBodyType(this)">
4789
- <span class="cd-card-icon">&#x1f4aa;</span>
4790
- <span class="cd-card-label">Stocky</span>
4791
- </div>
4792
- <div class="cd-item-card" data-value="slim" onclick="cdPickBodyType(this)">
4793
- <span class="cd-card-icon">&#x1f483;</span>
4794
- <span class="cd-card-label">Slim</span>
4795
- </div>
4796
- </div>
4797
- </div>
4624
+ <!-- Hidden tab elements for JS compat -->
4625
+ <div class="cd-tabs" style="display:none">
4626
+ <button class="cd-tab" onclick="cdSwitchTab('body', this)">Body</button>
4627
+ <button class="cd-tab" onclick="cdSwitchTab('stats', this)">Stats</button>
4798
4628
  </div>
4799
-
4800
- <!-- Tab: Hair -->
4801
- <div class="cd-tab-content" id="cd-tab-hair">
4802
- <div>
4803
- <div class="cd-subsection-label">Style</div>
4804
- <div class="cd-item-grid" id="cd-hair-styles">
4805
- <div class="cd-item-card" data-value="none" onclick="cdPickHairStyle(this)">
4806
- <span class="cd-card-icon">&#x1f6ab;</span>
4807
- <span class="cd-card-label">None</span>
4808
- </div>
4809
- <div class="cd-item-card" data-value="short" onclick="cdPickHairStyle(this)">
4810
- <span class="cd-card-icon">&#x2702;</span>
4811
- <span class="cd-card-label">Short</span>
4812
- </div>
4813
- <div class="cd-item-card" data-value="spiky" onclick="cdPickHairStyle(this)">
4814
- <span class="cd-card-icon">&#x26a1;</span>
4815
- <span class="cd-card-label">Spiky</span>
4816
- </div>
4817
- <div class="cd-item-card" data-value="long" onclick="cdPickHairStyle(this)">
4818
- <span class="cd-card-icon">&#x1f9d1;</span>
4819
- <span class="cd-card-label">Long</span>
4820
- </div>
4821
- <div class="cd-item-card" data-value="ponytail" onclick="cdPickHairStyle(this)">
4822
- <span class="cd-card-icon">&#x1f3c3;</span>
4823
- <span class="cd-card-label">Ponytail</span>
4824
- </div>
4825
- <div class="cd-item-card" data-value="bob" onclick="cdPickHairStyle(this)">
4826
- <span class="cd-card-icon">&#x1f487;</span>
4827
- <span class="cd-card-label">Bob</span>
4828
- </div>
4829
- <div class="cd-item-card" data-value="curly" onclick="cdPickHairStyle(this)">
4830
- <span class="cd-card-icon">&#x27b0;</span>
4831
- <span class="cd-card-label">Curly</span>
4832
- </div>
4833
- <div class="cd-item-card" data-value="afro" onclick="cdPickHairStyle(this)">
4834
- <span class="cd-card-icon">&#x2b55;</span>
4835
- <span class="cd-card-label">Afro</span>
4836
- </div>
4837
- <div class="cd-item-card" data-value="bun" onclick="cdPickHairStyle(this)">
4838
- <span class="cd-card-icon">&#x1f361;</span>
4839
- <span class="cd-card-label">Bun</span>
4840
- </div>
4841
- <div class="cd-item-card" data-value="braids" onclick="cdPickHairStyle(this)">
4842
- <span class="cd-card-icon">&#x1f380;</span>
4843
- <span class="cd-card-label">Braids</span>
4844
- </div>
4845
- <div class="cd-item-card" data-value="mohawk" onclick="cdPickHairStyle(this)">
4846
- <span class="cd-card-icon">&#x1f525;</span>
4847
- <span class="cd-card-label">Mohawk</span>
4848
- </div>
4849
- <div class="cd-item-card" data-value="wavy" onclick="cdPickHairStyle(this)">
4850
- <span class="cd-card-icon">&#x1f30a;</span>
4851
- <span class="cd-card-label">Wavy</span>
4852
- </div>
4853
- </div>
4854
- </div>
4855
- <div class="cd-color-row">
4856
- <label>Hair color</label>
4857
- <input type="color" id="pe-hair-color" value="#4A3728" onchange="cdOnChange()">
4858
- <span class="cd-color-hex" id="cd-hair-hex">#4A3728</span>
4859
- </div>
4629
+ <div class="cd-tab-content" id="cd-tab-stats" style="display:none"></div>
4630
+ <div class="cd-tab-content" id="cd-tab-body" style="display:none">
4631
+ <input type="color" id="pe-head-color" value="#FFD5B8" style="display:none">
4632
+ <span id="cd-skin-hex" style="display:none"></span>
4860
4633
  </div>
4861
-
4862
- <!-- Tab: Face -->
4863
- <div class="cd-tab-content" id="cd-tab-face">
4864
- <div>
4865
- <div class="cd-subsection-label">Eyes</div>
4866
- <div class="cd-item-grid" id="cd-eye-styles">
4867
- <div class="cd-item-card" data-value="dots" onclick="cdPickEyeStyle(this)">
4868
- <span class="cd-card-icon">&#x1f441;</span>
4869
- <span class="cd-card-label">Dots</span>
4870
- </div>
4871
- <div class="cd-item-card" data-value="anime" onclick="cdPickEyeStyle(this)">
4872
- <span class="cd-card-icon">&#x2728;</span>
4873
- <span class="cd-card-label">Anime</span>
4874
- </div>
4875
- <div class="cd-item-card" data-value="glasses" onclick="cdPickEyeStyle(this)">
4876
- <span class="cd-card-icon">&#x1f453;</span>
4877
- <span class="cd-card-label">Glasses</span>
4878
- </div>
4879
- <div class="cd-item-card" data-value="sleepy" onclick="cdPickEyeStyle(this)">
4880
- <span class="cd-card-icon">&#x1f634;</span>
4881
- <span class="cd-card-label">Sleepy</span>
4882
- </div>
4883
- <div class="cd-item-card" data-value="surprised" onclick="cdPickEyeStyle(this)">
4884
- <span class="cd-card-icon">&#x1f632;</span>
4885
- <span class="cd-card-label">Surprised</span>
4886
- </div>
4887
- <div class="cd-item-card" data-value="angry" onclick="cdPickEyeStyle(this)">
4888
- <span class="cd-card-icon">&#x1f620;</span>
4889
- <span class="cd-card-label">Angry</span>
4890
- </div>
4891
- <div class="cd-item-card" data-value="happy" onclick="cdPickEyeStyle(this)">
4892
- <span class="cd-card-icon">&#x1f604;</span>
4893
- <span class="cd-card-label">Happy</span>
4894
- </div>
4895
- <div class="cd-item-card" data-value="wink" onclick="cdPickEyeStyle(this)">
4896
- <span class="cd-card-icon">&#x1f609;</span>
4897
- <span class="cd-card-label">Wink</span>
4898
- </div>
4899
- <div class="cd-item-card" data-value="confident" onclick="cdPickEyeStyle(this)">
4900
- <span class="cd-card-icon">&#x1f60e;</span>
4901
- <span class="cd-card-label">Confident</span>
4902
- </div>
4903
- <div class="cd-item-card" data-value="tired" onclick="cdPickEyeStyle(this)">
4904
- <span class="cd-card-icon">&#x1f62b;</span>
4905
- <span class="cd-card-label">Tired</span>
4906
- </div>
4907
- </div>
4908
- </div>
4909
- <div>
4910
- <div class="cd-subsection-label">Mouth</div>
4911
- <div class="cd-item-grid" id="cd-mouth-styles">
4912
- <div class="cd-item-card" data-value="smile" onclick="cdPickMouthStyle(this)">
4913
- <span class="cd-card-icon">&#x1f642;</span>
4914
- <span class="cd-card-label">Smile</span>
4915
- </div>
4916
- <div class="cd-item-card" data-value="neutral" onclick="cdPickMouthStyle(this)">
4917
- <span class="cd-card-icon">&#x1f610;</span>
4918
- <span class="cd-card-label">Neutral</span>
4919
- </div>
4920
- <div class="cd-item-card" data-value="open" onclick="cdPickMouthStyle(this)">
4921
- <span class="cd-card-icon">&#x1f62e;</span>
4922
- <span class="cd-card-label">Open</span>
4923
- </div>
4924
- <div class="cd-item-card" data-value="grin" onclick="cdPickMouthStyle(this)">
4925
- <span class="cd-card-icon">&#x1f601;</span>
4926
- <span class="cd-card-label">Grin</span>
4927
- </div>
4928
- <div class="cd-item-card" data-value="frown" onclick="cdPickMouthStyle(this)">
4929
- <span class="cd-card-icon">&#x2639;</span>
4930
- <span class="cd-card-label">Frown</span>
4931
- </div>
4932
- <div class="cd-item-card" data-value="smirk" onclick="cdPickMouthStyle(this)">
4933
- <span class="cd-card-icon">&#x1f60f;</span>
4934
- <span class="cd-card-label">Smirk</span>
4935
- </div>
4936
- <div class="cd-item-card" data-value="tongue" onclick="cdPickMouthStyle(this)">
4937
- <span class="cd-card-icon">&#x1f61b;</span>
4938
- <span class="cd-card-label">Tongue</span>
4939
- </div>
4940
- <div class="cd-item-card" data-value="whistle" onclick="cdPickMouthStyle(this)">
4941
- <span class="cd-card-icon">&#x1f617;</span>
4942
- <span class="cd-card-label">Whistle</span>
4943
- </div>
4944
- </div>
4945
- </div>
4634
+ <div class="cd-tab-content" id="cd-tab-hair" style="display:none">
4635
+ <input type="color" id="pe-hair-color" value="#4A3728" style="display:none">
4636
+ <span id="cd-hair-hex" style="display:none"></span>
4946
4637
  </div>
4947
-
4948
- <!-- Tab: Outfit -->
4949
- <div class="cd-tab-content" id="cd-tab-outfit">
4950
- <div>
4951
- <div class="cd-subsection-label">Outfit Style</div>
4952
- <div class="cd-item-grid" id="cd-outfit-styles">
4953
- <div class="cd-item-card" data-value="" onclick="cdPickOutfit(this)">
4954
- <span class="cd-card-icon">&#x1f6ab;</span>
4955
- <span class="cd-card-label">None</span>
4956
- </div>
4957
- <div class="cd-item-card" data-value="hoodie" onclick="cdPickOutfit(this)">
4958
- <span class="cd-card-icon">&#x1f9e5;</span>
4959
- <span class="cd-card-label">Hoodie</span>
4960
- </div>
4961
- <div class="cd-item-card" data-value="suit" onclick="cdPickOutfit(this)">
4962
- <span class="cd-card-icon">&#x1f454;</span>
4963
- <span class="cd-card-label">Suit</span>
4964
- </div>
4965
- <div class="cd-item-card" data-value="dress" onclick="cdPickOutfit(this)">
4966
- <span class="cd-card-icon">&#x1f457;</span>
4967
- <span class="cd-card-label">Dress</span>
4968
- </div>
4969
- <div class="cd-item-card" data-value="labcoat" onclick="cdPickOutfit(this)">
4970
- <span class="cd-card-icon">&#x1f9ea;</span>
4971
- <span class="cd-card-label">Lab Coat</span>
4972
- </div>
4973
- <div class="cd-item-card" data-value="vest" onclick="cdPickOutfit(this)">
4974
- <span class="cd-card-icon">&#x1f3bd;</span>
4975
- <span class="cd-card-label">Vest</span>
4976
- </div>
4977
- <div class="cd-item-card" data-value="jacket" onclick="cdPickOutfit(this)">
4978
- <span class="cd-card-icon">&#x1f9e5;</span>
4979
- <span class="cd-card-label">Jacket</span>
4980
- </div>
4981
- </div>
4982
- </div>
4983
- <div class="cd-color-row">
4984
- <label>Shirt color</label>
4985
- <input type="color" id="pe-shirt-color" value="#f59e0b" onchange="cdOnChange()">
4986
- <span class="cd-color-hex" id="cd-shirt-hex">#f59e0b</span>
4987
- </div>
4988
- <div class="cd-color-row">
4989
- <label>Pants color</label>
4990
- <input type="color" id="pe-pants-color" value="#2d3748" onchange="cdOnChange()">
4991
- <span class="cd-color-hex" id="cd-pants-hex">#2d3748</span>
4992
- </div>
4993
- <div class="cd-color-row">
4994
- <label>Shoe color</label>
4995
- <input type="color" id="pe-shoe-color" value="#1a1a2e" onchange="cdOnChange()">
4996
- <span class="cd-color-hex" id="cd-shoe-hex">#1a1a2e</span>
4997
- </div>
4638
+ <div class="cd-tab-content" id="cd-tab-face" style="display:none"></div>
4639
+ <div class="cd-tab-content" id="cd-tab-outfit" style="display:none">
4640
+ <input type="color" id="pe-shirt-color" value="#f59e0b" style="display:none">
4641
+ <span id="cd-shirt-hex" style="display:none"></span>
4642
+ <input type="color" id="pe-pants-color" value="#2d3748" style="display:none">
4643
+ <span id="cd-pants-hex" style="display:none"></span>
4644
+ <input type="color" id="pe-shoe-color" value="#1a1a2e" style="display:none">
4645
+ <span id="cd-shoe-hex" style="display:none"></span>
4998
4646
  </div>
4999
-
5000
- <!-- Tab: Accessories -->
5001
- <div class="cd-tab-content" id="cd-tab-accessories">
5002
- <div>
5003
- <div class="cd-subsection-label">Glasses</div>
5004
- <div class="cd-item-grid" id="cd-glasses-styles">
5005
- <div class="cd-item-card" data-value="" onclick="cdPickGlasses(this)">
5006
- <span class="cd-card-icon">&#x1f6ab;</span>
5007
- <span class="cd-card-label">None</span>
5008
- </div>
5009
- <div class="cd-item-card" data-value="round" onclick="cdPickGlasses(this)">
5010
- <span class="cd-card-icon">&#x1f4a0;</span>
5011
- <span class="cd-card-label">Round</span>
5012
- </div>
5013
- <div class="cd-item-card" data-value="square" onclick="cdPickGlasses(this)">
5014
- <span class="cd-card-icon">&#x25fb;</span>
5015
- <span class="cd-card-label">Square</span>
5016
- </div>
5017
- <div class="cd-item-card" data-value="sunglasses" onclick="cdPickGlasses(this)">
5018
- <span class="cd-card-icon">&#x1f576;</span>
5019
- <span class="cd-card-label">Sunglasses</span>
5020
- </div>
5021
- </div>
5022
- <div class="cd-color-row" id="cd-glasses-color-row" style="margin-top:8px;display:none">
5023
- <label>Frame color</label>
5024
- <input type="color" id="pe-glasses-color" value="#555555" onchange="cdOnChange()">
5025
- <span class="cd-color-hex" id="cd-glasses-hex">#555555</span>
5026
- </div>
5027
- </div>
5028
- <div>
5029
- <div class="cd-subsection-label">Headwear</div>
5030
- <div class="cd-item-grid" id="cd-headwear-styles">
5031
- <div class="cd-item-card" data-value="" onclick="cdPickHeadwear(this)">
5032
- <span class="cd-card-icon">&#x1f6ab;</span>
5033
- <span class="cd-card-label">None</span>
5034
- </div>
5035
- <div class="cd-item-card" data-value="beanie" onclick="cdPickHeadwear(this)">
5036
- <span class="cd-card-icon">&#x1f9e2;</span>
5037
- <span class="cd-card-label">Beanie</span>
5038
- </div>
5039
- <div class="cd-item-card" data-value="cap" onclick="cdPickHeadwear(this)">
5040
- <span class="cd-card-icon">&#x1f9e2;</span>
5041
- <span class="cd-card-label">Cap</span>
5042
- </div>
5043
- <div class="cd-item-card" data-value="headphones" onclick="cdPickHeadwear(this)">
5044
- <span class="cd-card-icon">&#x1f3a7;</span>
5045
- <span class="cd-card-label">Headphones</span>
5046
- </div>
5047
- <div class="cd-item-card" data-value="headband" onclick="cdPickHeadwear(this)">
5048
- <span class="cd-card-icon">&#x1f3bd;</span>
5049
- <span class="cd-card-label">Headband</span>
5050
- </div>
5051
- </div>
5052
- <div class="cd-color-row" id="cd-headwear-color-row" style="margin-top:8px;display:none">
5053
- <label>Color</label>
5054
- <input type="color" id="pe-headwear-color" value="#333333" onchange="cdOnChange()">
5055
- <span class="cd-color-hex" id="cd-headwear-hex">#333333</span>
5056
- </div>
5057
- </div>
5058
- <div>
5059
- <div class="cd-subsection-label">Neckwear</div>
5060
- <div class="cd-item-grid" id="cd-neckwear-styles">
5061
- <div class="cd-item-card" data-value="" onclick="cdPickNeckwear(this)">
5062
- <span class="cd-card-icon">&#x1f6ab;</span>
5063
- <span class="cd-card-label">None</span>
5064
- </div>
5065
- <div class="cd-item-card" data-value="tie" onclick="cdPickNeckwear(this)">
5066
- <span class="cd-card-icon">&#x1f454;</span>
5067
- <span class="cd-card-label">Tie</span>
5068
- </div>
5069
- <div class="cd-item-card" data-value="bowtie" onclick="cdPickNeckwear(this)">
5070
- <span class="cd-card-icon">&#x1f380;</span>
5071
- <span class="cd-card-label">Bowtie</span>
5072
- </div>
5073
- <div class="cd-item-card" data-value="lanyard" onclick="cdPickNeckwear(this)">
5074
- <span class="cd-card-icon">&#x1f4db;</span>
5075
- <span class="cd-card-label">Lanyard</span>
5076
- </div>
5077
- </div>
5078
- <div class="cd-color-row" id="cd-neckwear-color-row" style="margin-top:8px;display:none">
5079
- <label>Color</label>
5080
- <input type="color" id="pe-neckwear-color" value="#c0392b" onchange="cdOnChange()">
5081
- <span class="cd-color-hex" id="cd-neckwear-hex">#c0392b</span>
5082
- </div>
5083
- </div>
4647
+ <div class="cd-tab-content" id="cd-tab-accessories" style="display:none">
4648
+ <div id="cd-glasses-color-row" style="display:none"><input type="color" id="pe-glasses-color" value="#555555"><span id="cd-glasses-hex"></span></div>
4649
+ <div id="cd-headwear-color-row" style="display:none"><input type="color" id="pe-headwear-color" value="#333333"><span id="cd-headwear-hex"></span></div>
4650
+ <div id="cd-neckwear-color-row" style="display:none"><input type="color" id="pe-neckwear-color" value="#c0392b"><span id="cd-neckwear-hex"></span></div>
5084
4651
  </div>
5085
4652
  </div>
5086
4653
 
5087
4654
  <!-- Footer -->
5088
4655
  <div class="cd-footer">
5089
- <button class="btn" onclick="cdRandomize()">Randomize</button>
4656
+ <button class="btn" onclick="closeProfileEditor()">Cancel</button>
5090
4657
  <button class="btn btn-primary" onclick="saveProfile()">Save</button>
5091
4658
  </div>
5092
4659
  </div>
@@ -5453,6 +5020,14 @@ function renderAgents(agents) {
5453
5020
  var sleepCount = 0;
5454
5021
  var html = '';
5455
5022
 
5023
+ function getAgentAvatar(name, info) {
5024
+ var av = info.avatar;
5025
+ if (av && av.indexOf("rx='32'") === -1) return av;
5026
+ var hash = 0;
5027
+ for (var c = 0; c < name.length; c++) hash = ((hash << 5) - hash) + name.charCodeAt(c);
5028
+ return BUILT_IN_AVATARS[Math.abs(hash) % BUILT_IN_AVATARS.length];
5029
+ }
5030
+
5456
5031
  function renderAgentCards(agentNames) {
5457
5032
  var cardHtml = '';
5458
5033
  for (var i = 0; i < agentNames.length; i++) {
@@ -5486,82 +5061,24 @@ function renderAgents(agents) {
5486
5061
  }
5487
5062
 
5488
5063
  var cardClass = 'agent-card' + (state !== 'working' ? ' ' + state : '');
5489
- var avatarHtml = info.avatar
5490
- ? '<img class="agent-avatar-img" src="' + escapeHtml(info.avatar) + '" alt="' + escapeHtml(name) + '" onerror="this.style.display=\'none\'">'
5491
- : '<div class="agent-avatar" style="background:' + color + '">' + initial(name) + '</div>';
5064
+ var agentAv = getAgentAvatar(name, info);
5065
+ var avatarHtml = '<img class="agent-avatar-img" src="' + escapeHtml(agentAv) + '" alt="' + escapeHtml(name) + '" onerror="this.outerHTML=\'<div class=&quot;agent-avatar&quot; style=&quot;background:' + color + '&quot;>' + initial(name) + '</div>\'">';
5492
5066
  var displayName = info.display_name || name;
5493
5067
 
5494
- // Subtitle: role + activity
5495
5068
  var subtitleParts = [];
5496
5069
  if (info.role) subtitleParts.push(info.role);
5497
5070
  if (activityText) subtitleParts.push(activityText);
5498
5071
  var subtitleText = subtitleParts.join(' \u00B7 ');
5499
5072
 
5500
- // Skills: limited to 3 for sidebar cards
5501
- var skillsHtml = '';
5502
- if (info.skills && info.skills.length > 0) {
5503
- var platformClass = 'platform-' + (info.provider || 'unknown').toLowerCase();
5504
- var platformSkills = info.platform_skills || [];
5505
- skillsHtml = '<div class="agent-skills">';
5506
- for (var s = 0; s < Math.min(info.skills.length, 3); s++) {
5507
- var skill = info.skills[s];
5508
- var isPlatform = platformSkills.indexOf(skill) !== -1;
5509
- skillsHtml += '<span class="skill-tag' + (isPlatform ? ' ' + platformClass : '') + '">' + escapeHtml(skill) + '</span>';
5510
- }
5511
- if (info.skills.length > 3) {
5512
- skillsHtml += '<span class="skill-tag" style="opacity:0.5">+' + (info.skills.length - 3) + '</span>';
5513
- }
5514
- skillsHtml += '</div>';
5515
- }
5516
-
5517
- // Nudge button (Bell icon)
5518
- var nudgeBtnHtml = '';
5519
- if (state !== 'offline' && state !== 'dead' && state !== 'unknown') {
5520
- nudgeBtnHtml = '<div class="agent-nudge-btn" title="Nudge agent to listen()" onclick="sendNudge(\'' + escapeHtml(name) + '\', event)">' +
5521
- '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path><path d="M13.73 21a2 2 0 0 1-3.46 0"></path></svg>' +
5522
- '</div>';
5523
- }
5524
-
5525
- // Liveness Sparkline (Heartbeat dots)
5526
- var sparklineHtml = '';
5527
- if (state !== 'offline' && state !== 'dead' && state !== 'unknown') {
5528
- var dots = '';
5529
- var history = info.listen_history || [];
5530
- // Fallback: synthesize from last_listen_call if listen_history is missing
5531
- if (history.length === 0) {
5532
- var llc = info.last_listen_call || info.last_listened_at;
5533
- if (llc) {
5534
- var llcTs = typeof llc === 'string' ? new Date(llc).getTime() : llc;
5535
- if (llcTs > 0) history = [llcTs];
5536
- }
5537
- }
5538
- for (var d = 0; d < 10; d++) {
5539
- var dotClass = 'hb-dot';
5540
- if (d < history.length) {
5541
- var age = (Date.now() - history[d]) / 1000;
5542
- if (age < 30) dotClass += ' active';
5543
- else if (age < 120) dotClass += ' stale';
5544
- } else {
5545
- dotClass += ' empty';
5546
- }
5547
- dots += '<div class="' + dotClass + '"></div>';
5548
- }
5549
- sparklineHtml = '<div class="listen-sparkline"><span class="listen-sparkline-label">Liveness</span>' + dots + '</div>';
5550
- }
5551
-
5552
5073
  var indicatorHtml = (state === 'listening' || state === 'working')
5553
5074
  ? '<span class="agent-state-pill ' + state + '">' + state + '</span>'
5554
5075
  : '<span class="agent-status-dot ' + state + '" title="' + state + '"></span>';
5555
5076
  cardHtml += '<div class="' + cardClass + '" data-agent="' + escapeHtml(name) + '" onclick="showProfilePopup(\'' + escapeHtml(name) + '\', event)">' +
5556
- nudgeBtnHtml +
5557
5077
  '<div class="agent-top">' +
5558
5078
  avatarHtml +
5559
5079
  '<div class="agent-info">' +
5560
- '<div class="agent-name" style="color:' + color + '">' + escapeHtml(displayName) + '</div>' +
5080
+ '<div class="agent-name">' + escapeHtml(displayName) + '</div>' +
5561
5081
  '<div class="agent-subtitle">' + escapeHtml(subtitleText) + '</div>' +
5562
- currentToolHtml +
5563
- skillsHtml +
5564
- sparklineHtml +
5565
5082
  '</div>' +
5566
5083
  indicatorHtml +
5567
5084
  '</div>' +
@@ -6376,6 +5893,10 @@ function scrollToBottom() {
6376
5893
  document.getElementById('scroll-bottom').classList.remove('visible');
6377
5894
  }
6378
5895
 
5896
+ function scrollToLatest() {
5897
+ scrollToBottom();
5898
+ }
5899
+
6379
5900
  // ==================== COMPACT MODE ====================
6380
5901
 
6381
5902
  var compactMode = localStorage.getItem('compactMode') === 'true';
@@ -7194,7 +6715,7 @@ function renderTasks() {
7194
6715
  }
7195
6716
 
7196
6717
  // Data hash to skip unnecessary DOM rebuilds
7197
- var tasksHash = cachedTasks.map(function(t) { return t.id + ':' + t.status + ':' + (t.assignee || ''); }).join('|');
6718
+ var tasksHash = cachedTasks.map(function(t) { return t.id + ':' + t.status + ':' + (t.assignee || '') + ':' + (t.retry_count || 0); }).join('|');
7198
6719
  if (tasksHash === _lastTasksHash) return;
7199
6720
  _lastTasksHash = tasksHash;
7200
6721
 
@@ -7247,6 +6768,7 @@ function buildTaskCard(t) {
7247
6768
  '<option value="in_review"' + (t.status === 'in_review' ? ' selected' : '') + '>In Review</option>' +
7248
6769
  '<option value="done"' + (t.status === 'done' ? ' selected' : '') + '>Done</option>' +
7249
6770
  '<option value="blocked"' + (t.status === 'blocked' ? ' selected' : '') + '>Blocked</option>' +
6771
+ '<option value="blocked_permanent"' + (t.status === 'blocked_permanent' ? ' selected' : '') + '>Needs Intervention</option>' +
7250
6772
  '</select>';
7251
6773
 
7252
6774
  var badgesHtml = '';
@@ -7264,7 +6786,7 @@ function buildTaskCard(t) {
7264
6786
  ? '<div style="font-size:10px;color:var(--red);margin-top:4px;font-style:italic">' + escapeHtml(t.blocked_reason) + '</div>'
7265
6787
  : '';
7266
6788
 
7267
- return '<div class="task-card" draggable="true" data-task-id="' + t.id + '" ondragstart="onTaskDragStart(event)" ondragend="onTaskDragEnd(event)" onclick="showTaskDetail(\'' + t.id + '\')">' +
6789
+ return '<div class="task-card' + (t.status === 'blocked_permanent' ? ' blocked_permanent' : '') + '" draggable="true" data-task-id="' + t.id + '" ondragstart="onTaskDragStart(event)" ondragend="onTaskDragEnd(event)" onclick="showTaskDetail(\'' + t.id + '\')">' +
7268
6790
  '<div class="task-title">' + escapeHtml(t.title || 'Untitled') + (badgesHtml ? ' ' + badgesHtml : '') + '</div>' +
7269
6791
  (t.description ? '<div class="task-desc">' + escapeHtml(t.description) + '</div>' : '') +
7270
6792
  blockedReasonHtml +
@@ -7299,7 +6821,14 @@ function showTaskDetail(taskId) {
7299
6821
 
7300
6822
  var html = '';
7301
6823
  html += '<div style="margin-bottom:16px"><div style="font-size:18px;font-weight:700;color:var(--text);margin-bottom:6px">' + escapeHtml(task.title || 'Untitled') + '</div>';
7302
- html += '<span style="font-size:11px;padding:3px 8px;border-radius:6px;font-weight:600;background:' + statusColor + '20;color:' + statusColor + '">' + escapeHtml(task.status || 'pending') + '</span></div>';
6824
+ var statusLabel = task.status === 'blocked_permanent' ? 'Needs Intervention' : escapeHtml(task.status || 'pending');
6825
+ html += '<span style="font-size:11px;padding:3px 8px;border-radius:6px;font-weight:600;background:' + statusColor + '20;color:' + statusColor + '">' + statusLabel + '</span>';
6826
+ if (task.retry_count > 0) html += ' <span style="font-size:10px;padding:2px 6px;border-radius:6px;font-weight:600;background:var(--yellow)20;color:var(--yellow)" title="Reclaimed ' + task.retry_count + ' time(s) by watchdog">↺' + task.retry_count + '</span>';
6827
+ html += '</div>';
6828
+
6829
+ if (task.status === 'blocked_permanent' && task.blocked_reason) {
6830
+ html += '<div style="font-size:12px;color:var(--red);padding:8px 12px;background:var(--red-dim);border-radius:8px;margin-bottom:16px;border-left:3px solid var(--red)">' + escapeHtml(task.blocked_reason) + '</div>';
6831
+ }
7303
6832
 
7304
6833
  if (task.description) {
7305
6834
  html += '<div style="font-size:13px;color:var(--text-dim);line-height:1.6;margin-bottom:16px;padding:12px;background:var(--surface-2);border-radius:8px">' + escapeHtml(task.description) + '</div>';
@@ -7909,12 +7438,18 @@ textarea.addEventListener('input', function() {
7909
7438
 
7910
7439
  // ==================== v3.0: MESSAGE AVATAR HELPER ====================
7911
7440
 
7912
- function getMsgAvatar(name, color) {
7441
+ function getMsgAvatarUrl(name) {
7913
7442
  var agent = cachedAgents[name];
7914
- if (agent && agent.avatar) {
7915
- return '<img class="msg-avatar-img" src="' + escapeHtml(agent.avatar) + '" alt="' + escapeHtml(name) + '" onerror="this.outerHTML=\'<div class=&quot;msg-avatar&quot; style=&quot;background:' + color + '&quot;>' + initial(name) + '</div>\'">';
7916
- }
7917
- return '<div class="msg-avatar" style="background:' + color + '">' + initial(name) + '</div>';
7443
+ var av = agent && agent.avatar;
7444
+ if (av && av.indexOf("rx='32'") === -1) return av;
7445
+ var hash = 0;
7446
+ for (var c = 0; c < name.length; c++) hash = ((hash << 5) - hash) + name.charCodeAt(c);
7447
+ return BUILT_IN_AVATARS[Math.abs(hash) % BUILT_IN_AVATARS.length];
7448
+ }
7449
+
7450
+ function getMsgAvatar(name, color) {
7451
+ var src = getMsgAvatarUrl(name);
7452
+ return '<img class="msg-avatar-img" src="' + escapeHtml(src) + '" alt="' + escapeHtml(name) + '" onerror="this.outerHTML=\'<div class=&quot;msg-avatar&quot; style=&quot;background:' + color + '&quot;>' + initial(name) + '</div>\'">';
7918
7453
  }
7919
7454
 
7920
7455
  // ==================== v3.0: PROFILE POPUP ====================
@@ -8059,39 +7594,43 @@ function ppPing(agentName) {
8059
7594
  }
8060
7595
 
8061
7596
  function ppMessage(agentName) {
7597
+ document.getElementById('profile-popup').classList.remove('open');
7598
+ switchView('messages');
8062
7599
  var sel = document.getElementById('inject-target');
8063
7600
  if (sel) sel.value = agentName;
8064
7601
  var ta = document.getElementById('inject-content');
8065
7602
  if (ta) { ta.value = ''; ta.focus(); }
8066
7603
  updateSendBtn();
8067
- document.getElementById('profile-popup').classList.remove('open');
8068
7604
  }
8069
7605
 
8070
7606
  function ppNudge(agentName) {
7607
+ document.getElementById('profile-popup').classList.remove('open');
7608
+ switchView('messages');
8071
7609
  var sel = document.getElementById('inject-target');
8072
7610
  if (sel) sel.value = agentName;
8073
7611
  var ta = document.getElementById('inject-content');
8074
7612
  if (ta) { ta.value = '@' + agentName + ' '; ta.focus(); }
8075
7613
  updateSendBtn();
8076
- document.getElementById('profile-popup').classList.remove('open');
8077
7614
  }
8078
7615
 
8079
7616
  function ppFilterChat(agentName) {
7617
+ document.getElementById('profile-popup').classList.remove('open');
7618
+ switchView('messages');
8080
7619
  searchQuery = agentName.toLowerCase();
8081
7620
  var input = document.getElementById('search-input');
8082
7621
  if (input) input.value = agentName;
8083
7622
  lastMessageCount = 0;
8084
7623
  renderMessages(cachedHistory);
8085
- document.getElementById('profile-popup').classList.remove('open');
8086
7624
  }
8087
7625
 
8088
7626
  function ppViewHistory(agentName) {
7627
+ document.getElementById('profile-popup').classList.remove('open');
7628
+ switchView('messages');
8089
7629
  searchQuery = agentName.toLowerCase();
8090
7630
  var input = document.getElementById('search-input');
8091
7631
  if (input) input.value = agentName;
8092
7632
  lastMessageCount = 0;
8093
7633
  renderMessages(cachedHistory);
8094
- document.getElementById('profile-popup').classList.remove('open');
8095
7634
  }
8096
7635
 
8097
7636
  function showProfilePopup(agentName, event) {
@@ -8106,7 +7645,9 @@ function showProfilePopup(agentName, event) {
8106
7645
 
8107
7646
  // Header: avatar, name, status badge
8108
7647
  var ppAvatar = document.getElementById('pp-avatar');
8109
- if (agent.avatar && (agent.avatar.startsWith('data:image/') || agent.avatar.startsWith('/'))) { ppAvatar.setAttribute('src', agent.avatar); ppAvatar.style.display = ''; } else { ppAvatar.style.display = 'none'; }
7648
+ var ppAvatarUrl = getMsgAvatarUrl(agentName);
7649
+ ppAvatar.setAttribute('src', ppAvatarUrl);
7650
+ ppAvatar.style.display = '';
8110
7651
  document.getElementById('pp-name').textContent = agent.display_name || agentName;
8111
7652
  document.getElementById('pp-name').style.color = color;
8112
7653
  document.getElementById('pp-id').textContent = '@' + agentName;
@@ -8118,21 +7659,38 @@ function showProfilePopup(agentName, event) {
8118
7659
  document.getElementById('pp-status-badge').className = 'pp-status-badge ' + badgeClass;
8119
7660
  document.getElementById('pp-status-badge').textContent = statusLabel;
8120
7661
 
8121
- // Default to Actions tab when opening agent popup
8122
- switchPpTab('actions', false);
8123
-
8124
- // === Stats Tab ===
7662
+ switchPpTab('stats', false);
7663
+
7664
+ // === Stats ===
7665
+ var _badgePalette = [
7666
+ { bg: 'rgba(96,165,250,0.15)', fg: '#60a5fa' },
7667
+ { bg: 'rgba(52,211,153,0.15)', fg: '#34d399' },
7668
+ { bg: 'rgba(251,191,36,0.15)', fg: '#fbbf24' },
7669
+ { bg: 'rgba(167,139,250,0.15)', fg: '#a78bfa' },
7670
+ { bg: 'rgba(251,113,133,0.15)', fg: '#fb7185' },
7671
+ { bg: 'rgba(56,189,248,0.15)', fg: '#38bdf8' },
7672
+ { bg: 'rgba(249,115,22,0.15)', fg: '#f97316' },
7673
+ { bg: 'rgba(232,121,249,0.15)', fg: '#e879f9' },
7674
+ ];
7675
+ function _badgeColor(text) {
7676
+ var h = 0;
7677
+ for (var i = 0; i < text.length; i++) h = ((h << 5) - h) + text.charCodeAt(i);
7678
+ return _badgePalette[Math.abs(h) % _badgePalette.length];
7679
+ }
8125
7680
  var badgesHtml = '';
8126
- if (agent.provider) badgesHtml += '<span class="role-badge" style="font-size:10px">' + escapeHtml(agent.provider) + '</span>';
8127
- if (agent.role) badgesHtml += '<span class="role-badge" style="font-size:10px">' + escapeHtml(agent.role) + '</span>';
7681
+ if (agent.provider) { var bc = _badgeColor(agent.provider); badgesHtml += '<span class="role-badge" style="font-size:10px;background:' + bc.bg + ';color:' + bc.fg + '">' + escapeHtml(agent.provider) + '</span>'; }
7682
+ if (agent.role) { var bc2 = _badgeColor(agent.role); badgesHtml += '<span class="role-badge" style="font-size:10px;background:' + bc2.bg + ';color:' + bc2.fg + '">' + escapeHtml(agent.role) + '</span>'; }
8128
7683
  document.getElementById('pp-badges').innerHTML = badgesHtml;
8129
7684
 
8130
- // Count tasks completed and messages for this agent
8131
7685
  var tasksCompleted = 0;
7686
+ var tasksActive = 0;
8132
7687
  var msgCount = 0;
8133
7688
  if (cachedTasks) {
8134
7689
  for (var t = 0; t < cachedTasks.length; t++) {
8135
- if (cachedTasks[t].assignee === agentName && cachedTasks[t].status === 'done') tasksCompleted++;
7690
+ if (cachedTasks[t].assignee === agentName) {
7691
+ if (cachedTasks[t].status === 'done') tasksCompleted++;
7692
+ else if (cachedTasks[t].status === 'in_progress' || cachedTasks[t].status === 'in_review') tasksActive++;
7693
+ }
8136
7694
  }
8137
7695
  }
8138
7696
  if (cachedHistory) {
@@ -8141,68 +7699,50 @@ function showProfilePopup(agentName, event) {
8141
7699
  }
8142
7700
  }
8143
7701
 
8144
- var statsHtml = '<div class="profile-popup-stat"><span>' + tasksCompleted + '</span>Tasks Done</div>' +
8145
- '<div class="profile-popup-stat"><span>' + msgCount + '</span>Messages</div>';
7702
+ var statsHtml = '<div class="profile-popup-stat"><span>' + tasksCompleted + '</span>Done</div>' +
7703
+ '<div class="profile-popup-stat"><span>' + tasksActive + '</span>Active</div>' +
7704
+ '<div class="profile-popup-stat"><span>' + msgCount + '</span>Sent</div>' +
7705
+ '<div class="profile-popup-stat"><span>' + (agent.branch || 'main') + '</span>Branch</div>';
8146
7706
  document.getElementById('pp-stats').innerHTML = statsHtml;
8147
7707
 
8148
- // Skills (read-only display)
8149
7708
  var skills = agent.skills || [];
8150
7709
  var skillsRoHtml = '';
8151
7710
  if (skills.length > 0) {
8152
- var platformClass = 'platform-' + (agent.provider || 'unknown').toLowerCase();
8153
- var platformSkills = agent.platform_skills || [];
8154
- skillsRoHtml = '<div style="font-size:10px;text-transform:uppercase;letter-spacing:0.5px;color:var(--text-muted);margin-bottom:6px">Skills</div><div class="skill-tags-inline">';
7711
+ skillsRoHtml = '<div style="display:flex;flex-wrap:wrap;gap:4px">';
8155
7712
  for (var s = 0; s < skills.length; s++) {
8156
- var isPlatform = platformSkills.indexOf(skills[s]) !== -1;
8157
- skillsRoHtml += '<span class="skill-tag' + (isPlatform ? ' ' + platformClass : '') + '">' + escapeHtml(skills[s]) + '</span>';
7713
+ skillsRoHtml += '<span class="skill-tag">' + escapeHtml(skills[s]) + '</span>';
8158
7714
  }
8159
7715
  skillsRoHtml += '</div>';
8160
7716
  }
8161
7717
  document.getElementById('pp-skills-readonly').innerHTML = skillsRoHtml;
8162
7718
 
8163
- // === Actions Tab ===
7719
+ // === Quick actions ===
8164
7720
  var eName = escapeHtml(agentName);
8165
- var actionsHtml = '<button class="pp-action-btn" onclick="ppMessage(\'' + eName + '\')">' +
8166
- '<svg viewBox="0 0 24 24"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>' +
8167
- 'Message</button>' +
8168
- '<button class="pp-action-btn pp-ping-btn" onclick="ppPing(\'' + eName + '\')">' +
8169
- '<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M12 1v4M12 19v4M4.22 4.22l2.83 2.83M16.95 16.95l2.83 2.83M1 12h4M19 12h4M4.22 19.78l2.83-2.83M16.95 7.05l2.83-2.83"/></svg>' +
8170
- 'Ping</button>' +
8171
- '<button class="pp-action-btn" onclick="ppNudge(\'' + eName + '\')">' +
8172
- '<svg viewBox="0 0 24 24"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>' +
8173
- 'Nudge</button>' +
8174
- '<button class="pp-action-btn" onclick="ppFilterChat(\'' + eName + '\')">' +
8175
- '<svg viewBox="0 0 24 24"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/></svg>' +
8176
- 'Messages</button>' +
8177
- '<button class="pp-action-btn" onclick="ppViewHistory(\'' + eName + '\')">' +
8178
- '<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>' +
8179
- 'History</button>';
8180
- document.getElementById('pp-actions').innerHTML = actionsHtml;
8181
-
8182
- // === Profile Tab ===
8183
- document.getElementById('pp-bio').textContent = agent.bio || 'No bio set.';
8184
- renderSkillEditor(agentName);
8185
-
8186
- // Position near click — find closest clickable ancestor
8187
- var anchor = event.target.closest('.agent-top') || event.target.closest('.agent-card') || event.target;
7721
+ document.getElementById('pp-quick-actions').innerHTML =
7722
+ '<button class="pp-quick-action" onclick="ppMessage(\'' + eName + '\')"><svg viewBox="0 0 16 16"><path d="M2 3h12v8H6l-3 2.5V11H2z" stroke-linejoin="round"/></svg>Message</button>' +
7723
+ '<button class="pp-quick-action" onclick="ppFilterChat(\'' + eName + '\')"><svg viewBox="0 0 16 16"><path d="M8 14A6 6 0 108 2a6 6 0 000 12z"/><path d="M8 5v3l2 1"/></svg>History</button>' +
7724
+ '<button class="pp-quick-action" onclick="ppNudge(\'' + eName + '\')"><svg viewBox="0 0 16 16"><path d="M13 3L3 8l4 1.5L8.5 14z" stroke-linejoin="round"/></svg>Nudge</button>';
7725
+
7726
+ document.getElementById('pp-bio').textContent = agent.bio || '';
7727
+
7728
+ var anchor = event.target.closest('.agent-card') || event.target.closest('.agent-pill') || event.target;
8188
7729
  var rect = anchor.getBoundingClientRect();
8189
7730
  popup.style.visibility = 'hidden';
8190
7731
  popup.classList.add('open');
8191
- var popupHeight = popup.offsetHeight;
8192
-
8193
- var topPos = rect.bottom + 4;
8194
- if (topPos + popupHeight > window.innerHeight - 10) {
8195
- var topSpace = rect.top;
8196
- var bottomSpace = window.innerHeight - rect.bottom;
8197
- if (topSpace > bottomSpace && topSpace > popupHeight) {
8198
- topPos = rect.top - popupHeight - 4;
8199
- } else {
8200
- topPos = window.innerHeight - popupHeight - 10;
8201
- }
7732
+ var popupW = popup.offsetWidth;
7733
+ var popupH = popup.offsetHeight;
7734
+
7735
+ var topPos = rect.bottom + 6;
7736
+ if (topPos + popupH > window.innerHeight - 10) {
7737
+ topPos = rect.top - popupH - 6;
8202
7738
  }
7739
+ topPos = Math.max(10, Math.min(topPos, window.innerHeight - popupH - 10));
8203
7740
 
8204
- popup.style.top = Math.max(10, topPos) + 'px';
8205
- popup.style.left = Math.min(rect.left, window.innerWidth - 310) + 'px';
7741
+ var leftPos = rect.left;
7742
+ leftPos = Math.max(10, Math.min(leftPos, window.innerWidth - popupW - 10));
7743
+
7744
+ popup.style.top = topPos + 'px';
7745
+ popup.style.left = leftPos + 'px';
8206
7746
  popup.style.visibility = 'visible';
8207
7747
  }
8208
7748
 
@@ -8288,15 +7828,6 @@ function showAgentPopup(agentName) {
8288
7828
  if (!agent) return;
8289
7829
  editingAgent = agentName;
8290
7830
  openProfileEditor();
8291
-
8292
- // Auto-switch to stats tab
8293
- var tabs = document.querySelectorAll('.cd-tab');
8294
- for (var i = 0; i < tabs.length; i++) {
8295
- if (tabs[i].textContent === 'Stats') {
8296
- cdSwitchTab('stats', tabs[i]);
8297
- break;
8298
- }
8299
- }
8300
7831
  }
8301
7832
 
8302
7833
  document.addEventListener('click', function() {
@@ -8357,18 +7888,18 @@ function confirmDeleteAgent() {
8357
7888
  // ==================== v3.0: PROFILE EDITOR ====================
8358
7889
 
8359
7890
  var BUILT_IN_AVATARS = [
8360
- "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='32' fill='%23f59e0b'/%3E%3Ccircle cx='22' cy='26' r='4' fill='%23fff'/%3E%3Ccircle cx='42' cy='26' r='4' fill='%23fff'/%3E%3Crect x='20' y='38' width='24' height='4' rx='2' fill='%23fff'/%3E%3Crect x='14' y='12' width='6' height='10' rx='3' fill='%23f59e0b' stroke='%23fff' stroke-width='1.5'/%3E%3Crect x='44' y='12' width='6' height='10' rx='3' fill='%23f59e0b' stroke='%23fff' stroke-width='1.5'/%3E%3C/svg%3E",
8361
- "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='32' fill='%233fb950'/%3E%3Ccircle cx='22' cy='26' r='5' fill='%23fff'/%3E%3Ccircle cx='42' cy='26' r='5' fill='%23fff'/%3E%3Ccircle cx='22' cy='26' r='2' fill='%23333'/%3E%3Ccircle cx='42' cy='26' r='2' fill='%23333'/%3E%3Cpath d='M20 38 Q32 46 44 38' stroke='%23fff' fill='none' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E",
8362
- "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='32' fill='%23d29922'/%3E%3Crect x='16' y='22' width='12' height='8' rx='2' fill='%23fff'/%3E%3Crect x='36' y='22' width='12' height='8' rx='2' fill='%23fff'/%3E%3Ccircle cx='22' cy='26' r='2' fill='%23333'/%3E%3Ccircle cx='42' cy='26' r='2' fill='%23333'/%3E%3Cpath d='M24 40 H40' stroke='%23fff' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E",
8363
- "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='32' fill='%23f85149'/%3E%3Ccircle cx='22' cy='26' r='4' fill='%23fff'/%3E%3Ccircle cx='42' cy='26' r='4' fill='%23fff'/%3E%3Ccircle cx='22' cy='26' r='2' fill='%23333'/%3E%3Ccircle cx='42' cy='26' r='2' fill='%23333'/%3E%3Cpath d='M22 40 Q32 34 42 40' stroke='%23fff' fill='none' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E",
8364
- "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='32' fill='%23fb923c'/%3E%3Ccircle cx='22' cy='28' r='4' fill='%23fff'/%3E%3Ccircle cx='42' cy='28' r='4' fill='%23fff'/%3E%3Cpath d='M16 18 L22 24' stroke='%23fff' stroke-width='2' stroke-linecap='round'/%3E%3Cpath d='M48 18 L42 24' stroke='%23fff' stroke-width='2' stroke-linecap='round'/%3E%3Cellipse cx='32' cy='42' rx='8' ry='4' fill='%23fff'/%3E%3C/svg%3E",
8365
- "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='32' fill='%23f778ba'/%3E%3Ccircle cx='24' cy='26' r='6' fill='%23fff'/%3E%3Ccircle cx='40' cy='26' r='6' fill='%23fff'/%3E%3Ccircle cx='24' cy='26' r='3' fill='%23333'/%3E%3Ccircle cx='40' cy='26' r='3' fill='%23333'/%3E%3Cpath d='M26 40 Q32 46 38 40' stroke='%23fff' fill='none' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E",
8366
- "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='32' fill='%23fbbf24'/%3E%3Crect x='17' y='23' width='10' height='6' rx='3' fill='%23fff'/%3E%3Crect x='37' y='23' width='10' height='6' rx='3' fill='%23fff'/%3E%3Cpath d='M22 38 L32 44 L42 38' stroke='%23fff' fill='none' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E",
8367
- "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='32' fill='%237ee787'/%3E%3Ccircle cx='22' cy='26' r='4' fill='%23fff'/%3E%3Ccircle cx='42' cy='26' r='4' fill='%23fff'/%3E%3Ccircle cx='23' cy='25' r='2' fill='%23333'/%3E%3Ccircle cx='43' cy='25' r='2' fill='%23333'/%3E%3Cpath d='M20 38 Q32 48 44 38' stroke='%23fff' fill='none' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E",
8368
- "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='32' fill='%23e3b341'/%3E%3Cpath d='M18 22 L26 30 L18 30Z' fill='%23fff'/%3E%3Cpath d='M46 22 L38 30 L46 30Z' fill='%23fff'/%3E%3Crect x='24' y='38' width='16' height='6' rx='3' fill='%23fff'/%3E%3C/svg%3E",
8369
- "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='32' fill='%23ffa198'/%3E%3Ccircle cx='22' cy='26' r='5' fill='%23fff'/%3E%3Ccircle cx='42' cy='26' r='5' fill='%23fff'/%3E%3Ccircle cx='22' cy='27' r='2.5' fill='%23333'/%3E%3Ccircle cx='42' cy='27' r='2.5' fill='%23333'/%3E%3Cellipse cx='32' cy='42' rx='6' ry='3' fill='%23fff'/%3E%3C/svg%3E",
8370
- "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='32' fill='%23d97706'/%3E%3Crect x='16' y='20' width='14' height='10' rx='2' fill='%23fff'/%3E%3Crect x='34' y='20' width='14' height='10' rx='2' fill='%23fff'/%3E%3Ccircle cx='23' cy='25' r='2' fill='%23d97706'/%3E%3Ccircle cx='41' cy='25' r='2' fill='%23d97706'/%3E%3Crect x='26' y='38' width='12' height='4' rx='2' fill='%23fff'/%3E%3C/svg%3E",
8371
- "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='32' fill='%23b45309'/%3E%3Ccircle cx='24' cy='24' r='5' fill='%23fff'/%3E%3Ccircle cx='40' cy='24' r='5' fill='%23fff'/%3E%3Ccircle cx='24' cy='24' r='2' fill='%23b45309'/%3E%3Ccircle cx='40' cy='24' r='2' fill='%23b45309'/%3E%3Cpath d='M20 38 Q32 50 44 38' stroke='%23fff' fill='none' stroke-width='3' stroke-linecap='round'/%3E%3Ccircle cx='32' cy='10' r='4' fill='%23fff'/%3E%3C/svg%3E",
7891
+ "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='12' fill='%231e293b'/%3E%3Ccircle cx='32' cy='28' r='12' fill='none' stroke='%2360a5fa' stroke-width='2'/%3E%3Ccircle cx='32' cy='28' r='4' fill='%2360a5fa'/%3E%3Cpath d='M20 48h24' stroke='%2360a5fa' stroke-width='2' stroke-linecap='round'/%3E%3Cpath d='M24 52h16' stroke='%2360a5fa' stroke-width='1.5' stroke-linecap='round' opacity='0.5'/%3E%3C/svg%3E",
7892
+ "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='12' fill='%231e293b'/%3E%3Cpolygon points='32,12 52,42 12,42' fill='none' stroke='%2334d399' stroke-width='2'/%3E%3Ccircle cx='32' cy='36' r='3' fill='%2334d399'/%3E%3C/svg%3E",
7893
+ "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='12' fill='%231e293b'/%3E%3Crect x='16' y='16' width='32' height='32' rx='4' fill='none' stroke='%23f59e0b' stroke-width='2' transform='rotate(45 32 32)'/%3E%3Ccircle cx='32' cy='32' r='5' fill='%23f59e0b'/%3E%3C/svg%3E",
7894
+ "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='12' fill='%231e293b'/%3E%3Ccircle cx='32' cy='32' r='18' fill='none' stroke='%23a78bfa' stroke-width='1.5'/%3E%3Ccircle cx='32' cy='32' r='10' fill='none' stroke='%23a78bfa' stroke-width='1.5'/%3E%3Ccircle cx='32' cy='32' r='3' fill='%23a78bfa'/%3E%3C/svg%3E",
7895
+ "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='12' fill='%231e293b'/%3E%3Cpath d='M16 32h32' stroke='%23fb7185' stroke-width='2'/%3E%3Cpath d='M32 16v32' stroke='%23fb7185' stroke-width='2'/%3E%3Ccircle cx='32' cy='32' r='8' fill='none' stroke='%23fb7185' stroke-width='1.5'/%3E%3Ccircle cx='32' cy='32' r='2' fill='%23fb7185'/%3E%3C/svg%3E",
7896
+ "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='12' fill='%231e293b'/%3E%3Cpath d='M14 42L32 18L50 42Z' fill='none' stroke='%2338bdf8' stroke-width='2' stroke-linejoin='round'/%3E%3Cpath d='M22 42L32 28L42 42' fill='none' stroke='%2338bdf8' stroke-width='1.5' stroke-linejoin='round' opacity='0.5'/%3E%3C/svg%3E",
7897
+ "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='12' fill='%231e293b'/%3E%3Cpath d='M18 18h28v28H18z' fill='none' stroke='%234ade80' stroke-width='2'/%3E%3Cpath d='M26 18v28M38 18v28M18 26h28M18 38h28' stroke='%234ade80' stroke-width='1' opacity='0.4'/%3E%3Crect x='27' y='27' width='10' height='10' fill='%234ade80' opacity='0.3'/%3E%3C/svg%3E",
7898
+ "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='12' fill='%231e293b'/%3E%3Cpath d='M32 14L48 24v16L32 50L16 40V24z' fill='none' stroke='%23fbbf24' stroke-width='2' stroke-linejoin='round'/%3E%3Ccircle cx='32' cy='32' r='4' fill='%23fbbf24'/%3E%3C/svg%3E",
7899
+ "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='12' fill='%231e293b'/%3E%3Ccircle cx='22' cy='22' r='8' fill='none' stroke='%23f97316' stroke-width='1.5'/%3E%3Ccircle cx='42' cy='22' r='8' fill='none' stroke='%23f97316' stroke-width='1.5'/%3E%3Ccircle cx='32' cy='42' r='8' fill='none' stroke='%23f97316' stroke-width='1.5'/%3E%3Ccircle cx='22' cy='22' r='2' fill='%23f97316'/%3E%3Ccircle cx='42' cy='22' r='2' fill='%23f97316'/%3E%3Ccircle cx='32' cy='42' r='2' fill='%23f97316'/%3E%3C/svg%3E",
7900
+ "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='12' fill='%231e293b'/%3E%3Cpath d='M12 50L32 14L52 50Z' fill='none' stroke='%23e879f9' stroke-width='2' stroke-linejoin='round'/%3E%3Cpath d='M32 50L22 32L42 32Z' fill='%23e879f9' opacity='0.2'/%3E%3Ccircle cx='32' cy='38' r='3' fill='%23e879f9'/%3E%3C/svg%3E",
7901
+ "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='12' fill='%231e293b'/%3E%3Cpath d='M16 32C16 20 32 14 32 14S48 20 48 32S32 50 32 50S16 44 16 32Z' fill='none' stroke='%23f43f5e' stroke-width='2'/%3E%3Ccircle cx='32' cy='32' r='4' fill='%23f43f5e' opacity='0.6'/%3E%3C/svg%3E",
7902
+ "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='12' fill='%231e293b'/%3E%3Ccircle cx='32' cy='32' r='16' fill='none' stroke='%2394a3b8' stroke-width='1.5' stroke-dasharray='4 3'/%3E%3Cpath d='M32 16v32M16 32h32' stroke='%2394a3b8' stroke-width='1' opacity='0.3'/%3E%3Ccircle cx='32' cy='32' r='5' fill='%2394a3b8' opacity='0.4'/%3E%3Ccircle cx='32' cy='32' r='2' fill='%2394a3b8'/%3E%3C/svg%3E",
8372
7903
  ];
8373
7904
 
8374
7905
  var editingAgent = '';
@@ -8422,11 +7953,6 @@ function cdOnChange() {
8422
7953
 
8423
7954
  function cdSwitchTab(name, btn) {
8424
7955
  cdCurrentTab = name;
8425
- document.querySelectorAll('.cd-tab-content').forEach(function(el) { el.classList.remove('active'); });
8426
- document.querySelectorAll('.cd-tab').forEach(function(el) { el.classList.remove('active'); });
8427
- var target = document.getElementById('cd-tab-' + name);
8428
- if (target) target.classList.add('active');
8429
- if (btn) btn.classList.add('active');
8430
7956
  }
8431
7957
 
8432
7958
  function cdActivateCard(container, value) {
@@ -8541,52 +8067,12 @@ function openProfileEditor() {
8541
8067
 
8542
8068
  document.getElementById('profile-popup').classList.remove('open');
8543
8069
 
8544
- // Populate basic fields
8545
8070
  document.getElementById('pe-display-name').value = agent.display_name || editingAgent;
8546
8071
  document.getElementById('pe-role').value = agent.role || '';
8547
8072
  document.getElementById('pe-bio').value = agent.bio || '';
8548
8073
  document.getElementById('pe-avatar-url').value = '';
8549
8074
  selectedAvatar = agent.avatar || '';
8550
8075
 
8551
- var app = agent.appearance || {};
8552
- var defApp = app;
8553
-
8554
- // Set color pickers
8555
- document.getElementById('pe-head-color').value = defApp.head_color || '#FFD5B8';
8556
- document.getElementById('pe-shirt-color').value = defApp.shirt_color || '#f59e0b';
8557
- document.getElementById('pe-hair-color').value = defApp.hair_color || '#4A3728';
8558
- document.getElementById('pe-pants-color').value = defApp.pants_color || '#2d3748';
8559
- document.getElementById('pe-shoe-color').value = defApp.shoe_color || '#1a1a2e';
8560
- document.getElementById('pe-glasses-color').value = defApp.glasses_color || '#555555';
8561
- document.getElementById('pe-headwear-color').value = defApp.headwear_color || '#333333';
8562
- document.getElementById('pe-neckwear-color').value = defApp.neckwear_color || '#c0392b';
8563
-
8564
- // Set appearance state
8565
- cdAppearance.hair_style = defApp.hair_style || 'short';
8566
- cdAppearance.eye_style = defApp.eye_style || 'dots';
8567
- cdAppearance.mouth_style = defApp.mouth_style || 'smile';
8568
- cdAppearance.glasses = defApp.glasses || null;
8569
- cdAppearance.headwear = defApp.headwear || null;
8570
- cdAppearance.neckwear = defApp.neckwear || null;
8571
- cdAppearance.outfit = defApp.outfit || null;
8572
- cdAppearance.body_type = defApp.body_type || 'default';
8573
-
8574
- // Activate correct cards
8575
- cdActivateCard('cd-hair-styles', cdAppearance.hair_style);
8576
- cdActivateCard('cd-eye-styles', cdAppearance.eye_style);
8577
- cdActivateCard('cd-mouth-styles', cdAppearance.mouth_style);
8578
- cdActivateCard('cd-glasses-styles', cdAppearance.glasses || '');
8579
- cdActivateCard('cd-headwear-styles', cdAppearance.headwear || '');
8580
- cdActivateCard('cd-neckwear-styles', cdAppearance.neckwear || '');
8581
- cdActivateCard('cd-outfit-styles', cdAppearance.outfit || '');
8582
- cdActivateCard('cd-body-types', cdAppearance.body_type);
8583
-
8584
- // Show/hide accessory color rows
8585
- document.getElementById('cd-glasses-color-row').style.display = cdAppearance.glasses ? 'flex' : 'none';
8586
- document.getElementById('cd-headwear-color-row').style.display = cdAppearance.headwear ? 'flex' : 'none';
8587
- document.getElementById('cd-neckwear-color-row').style.display = cdAppearance.neckwear ? 'flex' : 'none';
8588
-
8589
- // Build avatar picker grid
8590
8076
  var picker = document.getElementById('avatar-picker');
8591
8077
  var html = '';
8592
8078
  for (var i = 0; i < BUILT_IN_AVATARS.length; i++) {
@@ -8595,20 +8081,17 @@ function openProfileEditor() {
8595
8081
  }
8596
8082
  picker.innerHTML = html;
8597
8083
 
8598
- // Reset to Body tab
8599
- cdSwitchTab('body', document.querySelector('.cd-tab'));
8600
-
8601
- // Calculate and populate stats
8602
8084
  var sent = 0, received = 0;
8603
8085
  for (var i = 0; i < cachedHistory.length; i++) {
8604
8086
  if (cachedHistory[i].from === editingAgent) sent++;
8605
8087
  if (cachedHistory[i].to === editingAgent) received++;
8606
8088
  }
8089
+ var statCell = 'background:var(--surface-2);padding:7px 10px;border-radius:6px;border:1px solid var(--border)';
8607
8090
  var statsHtml =
8608
- '<div style="background:var(--surface-2);padding:10px;border-radius:6px;border:1px solid var(--border)">Sent: <b style="color:var(--text)">' + sent + '</b></div>' +
8609
- '<div style="background:var(--surface-2);padding:10px;border-radius:6px;border:1px solid var(--border)">Received: <b style="color:var(--text)">' + received + '</b></div>' +
8610
- '<div style="background:var(--surface-2);padding:10px;border-radius:6px;border:1px solid var(--border)">Provider: <b style="color:var(--text)">' + escapeHtml(agent.provider || 'unknown') + '</b></div>' +
8611
- '<div style="background:var(--surface-2);padding:10px;border-radius:6px;border:1px solid var(--border)">Branch: <b style="color:var(--text)">' + escapeHtml(agent.branch || 'main') + '</b></div>';
8091
+ '<div style="' + statCell + '">Sent: <b style="color:var(--text)">' + sent + '</b></div>' +
8092
+ '<div style="' + statCell + '">Received: <b style="color:var(--text)">' + received + '</b></div>' +
8093
+ '<div style="' + statCell + '">Provider: <b style="color:var(--text)">' + escapeHtml(agent.provider || 'unknown') + '</b></div>' +
8094
+ '<div style="' + statCell + '">Branch: <b style="color:var(--text)">' + escapeHtml(agent.branch || 'main') + '</b></div>';
8612
8095
  document.getElementById('pe-stats-container').innerHTML = statsHtml;
8613
8096
 
8614
8097
  var pq = projectParam();
@@ -8616,28 +8099,22 @@ function openProfileEditor() {
8616
8099
  if (!data || !data.agents || !data.agents[editingAgent]) return;
8617
8100
  var tu = data.agents[editingAgent];
8618
8101
  var tokenHtml =
8619
- '<div style="grid-column:1/-1;margin-top:12px;font-weight:800;color:var(--text);font-size:11px;text-transform:uppercase;border-bottom:1px solid var(--border);padding-bottom:4px">Token Usage (<span style="color:var(--accent);font-weight:600">' + escapeHtml(tu.model || 'unknown') + '</span>)</div>' +
8620
- '<div style="background:var(--surface-2);padding:10px;border-radius:6px;border:1px solid var(--border)">Input: <b style="color:var(--text)">' + (tu.input_tokens || 0).toLocaleString() + '</b> </div>' +
8621
- '<div style="background:var(--surface-2);padding:10px;border-radius:6px;border:1px solid var(--border)">Output: <b style="color:var(--text)">' + (tu.output_tokens || 0).toLocaleString() + '</b> </div>' +
8622
- '<div style="background:var(--surface-2);padding:10px;border-radius:6px;border:1px solid var(--border)">Cache: <b style="color:var(--text)">' + ((tu.cache_creation_tokens || 0) + (tu.cache_read_tokens || 0)).toLocaleString() + '</b> </div>' +
8623
- '<div style="background:var(--accent-dim);color:var(--accent);padding:10px;border-radius:6px;border:1px solid var(--accent);font-weight:700">Cost: $' + (tu.estimated_cost_usd || 0).toFixed(2) + '</div>';
8102
+ '<div style="grid-column:1/-1;margin-top:8px;font-weight:800;color:var(--text);font-size:11px;text-transform:uppercase;border-bottom:1px solid var(--border);padding-bottom:4px">Token Usage (<span style="color:var(--accent);font-weight:600">' + escapeHtml(tu.model || 'unknown') + '</span>)</div>' +
8103
+ '<div style="' + statCell + '">Input: <b style="color:var(--text)">' + (tu.input_tokens || 0).toLocaleString() + '</b></div>' +
8104
+ '<div style="' + statCell + '">Output: <b style="color:var(--text)">' + (tu.output_tokens || 0).toLocaleString() + '</b></div>' +
8105
+ '<div style="' + statCell + '">Cache: <b style="color:var(--text)">' + ((tu.cache_creation_tokens || 0) + (tu.cache_read_tokens || 0)).toLocaleString() + '</b></div>' +
8106
+ '<div style="background:var(--accent-dim);color:var(--accent);padding:7px 10px;border-radius:6px;border:1px solid var(--accent);font-weight:700">Cost: $' + (tu.estimated_cost_usd || 0).toFixed(2) + '</div>';
8624
8107
  var el = document.getElementById('pe-stats-container');
8625
8108
  if (el) el.innerHTML += tokenHtml;
8626
8109
  }).catch(function() {});
8627
8110
 
8628
- // Open the panel
8629
8111
  document.getElementById('char-designer').classList.add('open');
8630
8112
  document.getElementById('cd-backdrop').classList.add('open');
8631
-
8632
- // Update hex displays and init 3D preview
8633
- cdOnChange();
8634
- if (window._cdInitPreview) window._cdInitPreview();
8635
8113
  }
8636
8114
 
8637
8115
  function closeProfileEditor() {
8638
8116
  document.getElementById('char-designer').classList.remove('open');
8639
8117
  document.getElementById('cd-backdrop').classList.remove('open');
8640
- if (window._cdDisposePreview) window._cdDisposePreview();
8641
8118
  }
8642
8119
 
8643
8120
  function selectAvatar(idx) {
@@ -8665,31 +8142,12 @@ function handleAvatarUpload(input) {
8665
8142
  function saveProfile() {
8666
8143
  var avatarUrl = document.getElementById('pe-avatar-url').value.trim();
8667
8144
  var avatar = avatarUrl || selectedAvatar;
8668
- var app = cdGetAppearance();
8669
8145
 
8670
8146
  var body = {
8671
8147
  agent: editingAgent,
8672
8148
  display_name: document.getElementById('pe-display-name').value.trim() || editingAgent,
8673
8149
  role: document.getElementById('pe-role').value.trim(),
8674
- bio: document.getElementById('pe-bio').value.trim(),
8675
- appearance: {
8676
- head_color: app.head_color,
8677
- shirt_color: app.shirt_color,
8678
- hair_style: app.hair_style,
8679
- hair_color: app.hair_color,
8680
- eye_style: app.eye_style,
8681
- mouth_style: app.mouth_style,
8682
- pants_color: app.pants_color,
8683
- shoe_color: app.shoe_color,
8684
- glasses: app.glasses,
8685
- glasses_color: app.glasses_color,
8686
- headwear: app.headwear,
8687
- headwear_color: app.headwear_color,
8688
- neckwear: app.neckwear,
8689
- neckwear_color: app.neckwear_color,
8690
- outfit: app.outfit,
8691
- body_type: app.body_type
8692
- }
8150
+ bio: document.getElementById('pe-bio').value.trim()
8693
8151
  };
8694
8152
  if (avatar) body.avatar = avatar;
8695
8153