clay-server 3.4.0-beta.1 → 3.4.0-beta.10

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.
Files changed (68) hide show
  1. package/lib/daemon-projects.js +3 -3
  2. package/lib/daemon.js +1 -1
  3. package/lib/project-connection.js +20 -5
  4. package/lib/project-debate.js +4 -0
  5. package/lib/project-mate-interaction.js +21 -1
  6. package/lib/project-memory.js +3 -0
  7. package/lib/project-shell-command.js +160 -0
  8. package/lib/project-user-message.js +10 -0
  9. package/lib/project.js +19 -4
  10. package/lib/public/antigravity-avatar.png +0 -0
  11. package/lib/public/app.js +28 -0
  12. package/lib/public/copilot-avatar.svg +5 -0
  13. package/lib/public/css/command-palette.css +22 -2
  14. package/lib/public/css/icon-strip.css +29 -13
  15. package/lib/public/css/input.css +75 -0
  16. package/lib/public/css/mates.css +6 -0
  17. package/lib/public/css/menus.css +48 -14
  18. package/lib/public/css/messages.css +9 -0
  19. package/lib/public/css/pane.css +4 -0
  20. package/lib/public/css/pwa-mobile.css +17 -0
  21. package/lib/public/css/title-bar.css +19 -0
  22. package/lib/public/grok-avatar.svg +4 -0
  23. package/lib/public/index.html +38 -7
  24. package/lib/public/junie-avatar.svg +11 -0
  25. package/lib/public/kimi-avatar.svg +4 -0
  26. package/lib/public/modules/app-header.js +4 -0
  27. package/lib/public/modules/app-messages.js +28 -5
  28. package/lib/public/modules/app-panels.js +28 -8
  29. package/lib/public/modules/app-projects.js +3 -1
  30. package/lib/public/modules/app-rendering.js +19 -4
  31. package/lib/public/modules/input.js +58 -28
  32. package/lib/public/modules/mate-sidebar.js +11 -3
  33. package/lib/public/modules/notifications.js +7 -1
  34. package/lib/public/modules/pane-bridge.js +11 -0
  35. package/lib/public/modules/pane-links.js +23 -0
  36. package/lib/public/modules/project-switcher.js +7 -6
  37. package/lib/public/modules/shell-command.js +148 -0
  38. package/lib/public/modules/sidebar-mates.js +1 -1
  39. package/lib/public/modules/sidebar-mobile.js +2 -1
  40. package/lib/public/modules/sidebar-projects.js +34 -43
  41. package/lib/public/modules/sidebar-sessions.js +6 -6
  42. package/lib/public/modules/split-pair-ui.js +3 -2
  43. package/lib/public/modules/tools.js +6 -1
  44. package/lib/public/modules/vendor-priority.js +14 -0
  45. package/lib/public/modules/vendor-selection.js +20 -0
  46. package/lib/public/modules/worktree-location.js +17 -0
  47. package/lib/public/qwen-avatar.svg +4 -0
  48. package/lib/public/style.css +3 -2
  49. package/lib/sdk-bridge.js +49 -25
  50. package/lib/sdk-message-processor.js +10 -1
  51. package/lib/session-notes-mcp-server.js +7 -1
  52. package/lib/worktree.js +9 -4
  53. package/lib/ws-schema.js +3 -0
  54. package/lib/yoke/acp-agent-profiles.js +64 -14
  55. package/lib/yoke/adapters/antigravity.js +417 -0
  56. package/lib/yoke/adapters/codex.js +98 -3
  57. package/lib/yoke/adapters/copilot.js +7 -0
  58. package/lib/yoke/adapters/grok.js +7 -0
  59. package/lib/yoke/adapters/junie.js +7 -0
  60. package/lib/yoke/adapters/kimi.js +7 -0
  61. package/lib/yoke/adapters/qwen.js +7 -0
  62. package/lib/yoke/codex-app-server.js +25 -8
  63. package/lib/yoke/index.js +58 -24
  64. package/lib/yoke/instructions.js +3 -0
  65. package/lib/yoke/vendor-registry.js +61 -6
  66. package/package.json +1 -1
  67. package/lib/public/gemini-avatar.svg +0 -11
  68. package/lib/yoke/adapters/gemini.js +0 -7
@@ -790,6 +790,7 @@
790
790
  opacity: 0.35;
791
791
  cursor: default;
792
792
  }
793
+ .vendor-toggle-btn.disabled:not(.active) { display: none; }
793
794
  .vendor-toggle-icon {
794
795
  width: 16px;
795
796
  height: 16px;
@@ -799,6 +800,17 @@
799
800
  .vendor-toggle-label {
800
801
  pointer-events: none;
801
802
  }
803
+ .vendor-toggle-btn.experimental::after,
804
+ .vendor-experimental-badge {
805
+ content: "🧪";
806
+ font-size: 10px;
807
+ line-height: 1;
808
+ font-weight: 400;
809
+ filter: saturate(0.85);
810
+ }
811
+ .vendor-experimental-badge {
812
+ margin-left: auto;
813
+ }
802
814
  @media (min-width: 901px) {
803
815
  .vendor-toggle-btn:not(.active) .vendor-toggle-label { display: none; }
804
816
  .vendor-toggle-btn:not(.active) { padding: 0 8px; }
@@ -826,6 +838,7 @@
826
838
 
827
839
  #attach-file-btn,
828
840
  #attach-image-btn,
841
+ #shell-command-btn,
829
842
  #schedule-btn,
830
843
  #input-more-btn {
831
844
  width: 36px;
@@ -844,14 +857,76 @@
844
857
 
845
858
  #attach-file-btn .lucide,
846
859
  #attach-image-btn .lucide,
860
+ #shell-command-btn .lucide,
847
861
  #schedule-btn .lucide,
848
862
  #input-more-btn .lucide { width: 20px; height: 20px; flex-shrink: 0; }
849
863
 
850
864
  #attach-file-btn:hover,
851
865
  #attach-image-btn:hover,
866
+ #shell-command-btn:hover,
852
867
  #schedule-btn:hover,
853
868
  #input-more-btn:hover { background: rgba(var(--overlay-rgb), 0.06); color: var(--text); }
854
869
 
870
+ #shell-command-btn.active {
871
+ background: var(--accent-12);
872
+ color: var(--accent);
873
+ }
874
+
875
+ #input-row.shell-command-mode {
876
+ box-shadow: 0 0 0 1px var(--accent);
877
+ }
878
+
879
+ #input-row.shell-command-mode #input {
880
+ font-family: var(--font-mono, "SFMono-Regular", Consolas, monospace);
881
+ }
882
+
883
+ .shell-command-card {
884
+ width: min(760px, calc(100% - 24px));
885
+ margin: 10px auto;
886
+ border: 1px solid var(--border);
887
+ border-radius: 8px;
888
+ background: var(--input-bg);
889
+ overflow: hidden;
890
+ }
891
+
892
+ .shell-command-card.error { border-color: rgba(239, 68, 68, 0.45); }
893
+
894
+ .shell-command-header {
895
+ display: flex;
896
+ align-items: center;
897
+ gap: 8px;
898
+ padding: 9px 12px;
899
+ border-bottom: 1px solid var(--border);
900
+ color: var(--text-secondary);
901
+ font-size: 12px;
902
+ }
903
+
904
+ .shell-command-header code {
905
+ min-width: 0;
906
+ flex: 1;
907
+ overflow: hidden;
908
+ text-overflow: ellipsis;
909
+ white-space: nowrap;
910
+ color: var(--text);
911
+ }
912
+
913
+ .shell-command-icon { display: inline-flex; }
914
+ .shell-command-icon .lucide { width: 16px; height: 16px; }
915
+ .shell-command-status { flex-shrink: 0; color: var(--text-muted); }
916
+
917
+ .shell-command-card.running .shell-command-status { color: var(--accent); }
918
+
919
+ .shell-command-output {
920
+ max-height: 320px;
921
+ margin: 0;
922
+ padding: 12px;
923
+ overflow: auto;
924
+ color: var(--text-secondary);
925
+ font: 12px/1.5 var(--font-mono, "SFMono-Regular", Consolas, monospace);
926
+ white-space: pre-wrap;
927
+ word-break: break-word;
928
+ }
929
+
855
930
  #ask-mate-btn {
856
931
  width: 36px;
857
932
  height: 36px;
@@ -682,6 +682,12 @@
682
682
  .mate-vendor-label {
683
683
  pointer-events: none;
684
684
  }
685
+ .mate-vendor-btn.experimental::after {
686
+ content: "🧪";
687
+ font-size: 9px;
688
+ line-height: 1;
689
+ font-weight: 400;
690
+ }
685
691
  .mate-vendor-btn:not(.active) .mate-vendor-label { display: none; }
686
692
 
687
693
  /* Collapse button inside mate header: white to match header style */
@@ -44,29 +44,64 @@
44
44
  #header-rename-btn .lucide { width: 13px; height: 13px; }
45
45
 
46
46
  #header-add-worker-btn {
47
- display: flex;
47
+ display: inline-flex;
48
48
  align-items: center;
49
49
  justify-content: center;
50
- width: 24px;
51
- height: 24px;
52
- border: 1px solid transparent;
53
- background: none;
54
- color: var(--text-dimmer);
50
+ gap: 7px;
51
+ width: auto;
52
+ height: 28px;
53
+ border: 1px solid color-mix(in srgb, var(--accent2) 28%, var(--border));
54
+ background: var(--accent2-8);
55
+ color: var(--accent2);
55
56
  cursor: pointer;
56
57
  border-radius: 8px;
57
58
  flex-shrink: 0;
58
- padding: 0;
59
- margin-left: 4px;
59
+ padding: 0 9px 0 7px;
60
+ margin-left: 0;
61
+ font-family: inherit;
62
+ font-size: 11px;
63
+ font-weight: 700;
64
+ line-height: 1;
65
+ white-space: nowrap;
60
66
  transition: color 0.15s, background 0.15s, border-color 0.15s;
61
67
  }
62
- #header-add-worker-btn:hover { color: var(--text-secondary); background: rgba(var(--overlay-rgb),0.04); border-color: var(--border); }
63
- #header-add-worker-btn .lucide { width: 13px; height: 13px; }
68
+ #header-add-worker-btn:hover {
69
+ color: var(--accent2-hover);
70
+ background: var(--accent2-15);
71
+ border-color: color-mix(in srgb, var(--accent2) 48%, var(--border));
72
+ }
73
+ .header-worker-icon {
74
+ position: relative;
75
+ display: inline-flex;
76
+ align-items: center;
77
+ justify-content: center;
78
+ width: 16px;
79
+ height: 16px;
80
+ flex-shrink: 0;
81
+ }
82
+ .header-worker-icon > .lucide { width: 15px; height: 15px; }
83
+ .header-worker-add-badge {
84
+ position: absolute;
85
+ right: -4px;
86
+ bottom: -3px;
87
+ display: inline-flex;
88
+ align-items: center;
89
+ justify-content: center;
90
+ width: 9px;
91
+ height: 9px;
92
+ border-radius: 50%;
93
+ background: var(--accent2);
94
+ color: var(--bg);
95
+ box-shadow: 0 0 0 2px var(--bg);
96
+ }
97
+ .header-worker-add-badge .lucide { width: 7px; height: 7px; stroke-width: 3; }
98
+ .header-worker-label { letter-spacing: 0.01em; }
64
99
  #header-add-worker-btn.hidden { display: none; }
65
100
  body.dm-mode #header-add-worker-btn { display: none; }
66
101
 
67
- /* "Close terminal" button: sits just left of the rename pencil during a live
68
- TUI session. Matches the rename button's chrome; reds out on hover since it
69
- stops the running claude (the session stays resumable). */
102
+ /* "Close terminal" button for a live TUI session. Matches the session-control
103
+ chrome; reds out on hover since it stops the running agent while preserving
104
+ the resumable session. */
70
105
  #header-tui-close-btn {
71
106
  display: flex;
72
107
  align-items: center;
@@ -980,4 +1015,3 @@ body.dm-mode #header-add-worker-btn { display: none; }
980
1015
  #config-chip .config-chip-icon { display: inline; width: 14px; height: 14px; }
981
1016
  #config-chip-label { display: none; }
982
1017
  }
983
-
@@ -5,12 +5,21 @@
5
5
  #messages {
6
6
  flex: 1;
7
7
  overflow-y: auto;
8
+ overflow-x: hidden;
8
9
  -webkit-overflow-scrolling: touch;
9
10
  overscroll-behavior-y: contain;
10
11
  padding: 20px 0 12px;
11
12
  position: relative;
12
13
  }
13
14
 
15
+ /* Rich message content can contain wide tables, diagrams, or generated HTML.
16
+ Keep that content inside the chat scroller; nested code blocks still own
17
+ their intentional horizontal scrolling. */
18
+ #messages > * {
19
+ min-width: 0;
20
+ max-width: 100%;
21
+ }
22
+
14
23
  /* --- Remote cursors (Figma-style) --- */
15
24
  .remote-cursor {
16
25
  pointer-events: none;
@@ -23,6 +23,10 @@ body.pane-mode #sticky-notes-container {
23
23
  display: none !important;
24
24
  }
25
25
 
26
+ body.pane-mode .notif-banner-container {
27
+ display: none !important;
28
+ }
29
+
26
30
  body.pane-mode #main-area {
27
31
  border: 0;
28
32
  border-radius: 0;
@@ -0,0 +1,17 @@
1
+ /* In an iOS Home Screen app, a 100%-tall root can be measured against the
2
+ shortened safe-area viewport. Use the physical viewport for the root while
3
+ retaining safe-area padding on controls inside the app. */
4
+ @media (max-width: 768px) {
5
+ html.pwa-standalone,
6
+ html.pwa-standalone body {
7
+ height: 100vh;
8
+ }
9
+ }
10
+
11
+ @media (max-width: 768px) and (display-mode: standalone),
12
+ (max-width: 768px) and (display-mode: fullscreen) {
13
+ html,
14
+ body {
15
+ height: 100vh;
16
+ }
17
+ }
@@ -324,6 +324,25 @@
324
324
  display: none !important;
325
325
  }
326
326
 
327
+ .title-bar-content {
328
+ height: calc(48px + var(--safe-top));
329
+ padding: var(--safe-top) 16px 0;
330
+ }
331
+
332
+ /* The inline title editor must own the header while the keyboard is open.
333
+ Leaving the other controls visible made its usable width nearly zero. */
334
+ .title-bar-content.session-renaming .status,
335
+ .title-bar-content.session-renaming #header-left > :not(.header-rename-input) {
336
+ display: none !important;
337
+ }
338
+
339
+ .title-bar-content.session-renaming .header-rename-input {
340
+ flex: 1 1 100%;
341
+ width: 100%;
342
+ min-height: 36px;
343
+ font-size: 16px;
344
+ }
345
+
327
346
  #sidebar-column {
328
347
  width: 0 !important;
329
348
  min-width: 0 !important;
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="Grok Build">
2
+ <rect width="64" height="64" rx="14" fill="#111"/>
3
+ <path d="M45 17A21 21 0 1 0 49 42H35v-9h22v4A25 25 0 1 1 51 13z" fill="#fff"/>
4
+ </svg>
@@ -30,7 +30,7 @@
30
30
  (function(){try{var k="clay-theme-vars",v=localStorage.getItem(k),r=document.documentElement;if(v){var o=JSON.parse(v),p;for(p in o)r.style.setProperty(p,o[p]);var vt=localStorage.getItem(k.replace("-vars","-variant"));if(vt==="light"){r.classList.add("light-theme");r.classList.remove("dark-theme")}else{r.classList.add("dark-theme");r.classList.remove("light-theme")}var m=document.querySelector('meta[name="theme-color"]');if(m&&o["--bg"])m.setAttribute("content",o["--bg"])}else{var sl=window.matchMedia&&window.matchMedia("(prefers-color-scheme: light)").matches;if(sl){r.classList.add("light-theme");r.classList.remove("dark-theme")}}}catch(e){}})();
31
31
  </script>
32
32
  <script>if(window.navigator.standalone||window.matchMedia("(display-mode:standalone)").matches){document.documentElement.classList.add("pwa-standalone")}</script>
33
- <link rel="stylesheet" href="style.css?v=20260407">
33
+ <link rel="stylesheet" href="style.css?v=20260824f">
34
34
  <style>
35
35
  @media(max-width:768px){
36
36
  /* User messages: vertical stack, avatar on top, right-aligned */
@@ -373,12 +373,11 @@
373
373
  </div>
374
374
  <button id="hamburger-btn" aria-label="Toggle sidebar" title="Toggle sidebar"><i data-lucide="menu"></i></button>
375
375
  <span class="header-title" id="header-title">Connecting...</span>
376
+ <button id="header-rename-btn" type="button" title="Rename session"><i data-lucide="pencil"></i></button>
376
377
  <button id="header-info-btn" type="button" title="Session info"><i data-lucide="info"></i></button>
377
378
  <button id="header-full-access-btn" type="button" class="session-full-access hidden" role="switch" aria-checked="false" title="Skip Permissions is off — permission prompts are on">
378
379
  <span class="session-full-access-label">Skip Permissions</span><span class="session-full-access-track" aria-hidden="true"><span></span></span>
379
380
  </button>
380
- <button id="header-rename-btn" type="button" title="Rename session"><i data-lucide="pencil"></i></button>
381
- <button id="header-add-worker-btn" type="button" title="Add Worker — this session becomes the Driver of a pair"><i data-lucide="user-plus"></i></button>
382
381
  <button type="button" id="header-tui-close-btn" class="header-tui-close-btn hidden" title="Close terminal (keeps history, resume anytime)" aria-label="Close terminal"><i data-lucide="power"></i></button>
383
382
  <div id="mate-mobile-title" class="mate-mobile-title hidden">
384
383
  <img id="mate-mobile-avatar" class="mate-mobile-avatar" alt="">
@@ -389,6 +388,13 @@
389
388
  <div id="ralph-sticky" class="hidden"></div>
390
389
  <div id="debate-sticky" class="hidden"></div>
391
390
  <div class="status">
391
+ <button id="header-add-worker-btn" type="button" aria-label="Add AI Worker" title="Add an AI Worker — this session becomes the Driver of a pair">
392
+ <span class="header-worker-icon" aria-hidden="true">
393
+ <i data-lucide="bot"></i>
394
+ <span class="header-worker-add-badge"><i data-lucide="plus"></i></span>
395
+ </span>
396
+ <span class="header-worker-label">Add Worker</span>
397
+ </button>
392
398
  <button type="button" id="header-tui-font-btn" class="header-tui-font-btn hidden" title="Terminal font" aria-label="Terminal font">
393
399
  <i data-lucide="type"></i>
394
400
  </button>
@@ -505,6 +511,7 @@
505
511
  <button id="input-more-btn" type="button" aria-label="More options" title="More options" class="mobile-only"><i data-lucide="plus"></i></button>
506
512
  <button id="attach-file-btn" type="button" aria-label="Attach file" title="Attach file" class="desktop-only"><i data-lucide="paperclip"></i></button>
507
513
  <button id="attach-image-btn" type="button" aria-label="Attach image" title="Attach image" class="desktop-only"><i data-lucide="image"></i></button>
514
+ <button id="shell-command-btn" type="button" aria-label="Run shell command" aria-pressed="false" title="Run a shell command and add its output to the next agent message" class="desktop-only"><i data-lucide="square-terminal"></i></button>
508
515
  <button id="stt-btn" type="button" aria-label="Voice input" title="Voice input"><i data-lucide="mic"></i></button>
509
516
  <button id="schedule-btn" type="button" aria-label="Schedule message" title="Schedule message"><i data-lucide="clock"></i></button>
510
517
  <button id="ask-mate-btn" type="button" aria-label="Ask Mate" title="Ask a Mate for advice on this session"><i data-lucide="at-sign"></i></button>
@@ -527,14 +534,34 @@
527
534
  <img src="/codex-avatar.png" class="vendor-toggle-icon" alt="Codex">
528
535
  <span class="vendor-toggle-label">Codex</span>
529
536
  </button>
530
- <button id="vendor-btn-gemini" class="vendor-toggle-btn" data-vendor="gemini">
531
- <img src="/gemini-avatar.svg" class="vendor-toggle-icon" alt="Gemini">
532
- <span class="vendor-toggle-label">Gemini CLI</span>
537
+ <button id="vendor-btn-antigravity" class="vendor-toggle-btn" data-vendor="antigravity">
538
+ <img src="/antigravity-avatar.png" class="vendor-toggle-icon" alt="Antigravity">
539
+ <span class="vendor-toggle-label">Antigravity CLI</span>
533
540
  </button>
534
541
  <button id="vendor-btn-opencode" class="vendor-toggle-btn" data-vendor="opencode">
535
542
  <img src="/opencode-avatar.svg" class="vendor-toggle-icon" alt="OpenCode">
536
543
  <span class="vendor-toggle-label">OpenCode</span>
537
544
  </button>
545
+ <button id="vendor-btn-kimi" class="vendor-toggle-btn" data-vendor="kimi">
546
+ <img src="/kimi-avatar.svg" class="vendor-toggle-icon" alt="Kimi">
547
+ <span class="vendor-toggle-label">Kimi Code</span>
548
+ </button>
549
+ <button id="vendor-btn-grok" class="vendor-toggle-btn" data-vendor="grok">
550
+ <img src="/grok-avatar.svg" class="vendor-toggle-icon" alt="Grok">
551
+ <span class="vendor-toggle-label">Grok Build</span>
552
+ </button>
553
+ <button id="vendor-btn-copilot" class="vendor-toggle-btn" data-vendor="copilot">
554
+ <img src="/copilot-avatar.svg" class="vendor-toggle-icon" alt="GitHub Copilot">
555
+ <span class="vendor-toggle-label">GitHub Copilot CLI</span>
556
+ </button>
557
+ <button id="vendor-btn-qwen" class="vendor-toggle-btn" data-vendor="qwen">
558
+ <img src="/qwen-avatar.svg" class="vendor-toggle-icon" alt="Qwen">
559
+ <span class="vendor-toggle-label">Qwen Code</span>
560
+ </button>
561
+ <button id="vendor-btn-junie" class="vendor-toggle-btn" data-vendor="junie">
562
+ <img src="/junie-avatar.svg" class="vendor-toggle-icon" alt="Junie">
563
+ <span class="vendor-toggle-label">Junie CLI</span>
564
+ </button>
538
565
  <button id="vendor-btn-kiro" class="vendor-toggle-btn" data-vendor="kiro">
539
566
  <img src="/kiro-avatar.svg" class="vendor-toggle-icon" alt="Kiro">
540
567
  <span class="vendor-toggle-label">Kiro CLI</span>
@@ -615,6 +642,10 @@
615
642
  <i data-lucide="image"></i>
616
643
  <span>Image</span>
617
644
  </button>
645
+ <button class="input-more-action" id="input-more-shell">
646
+ <i data-lucide="square-terminal"></i>
647
+ <span>Shell command</span>
648
+ </button>
618
649
  </div>
619
650
  <div class="input-more-divider"></div>
620
651
  <div class="input-more-section-label">
@@ -2333,7 +2364,7 @@
2333
2364
  <script src="https://cdn.jsdelivr.net/npm/@xterm/addon-fit@0/lib/addon-fit.min.js"></script>
2334
2365
  <script src="https://cdn.jsdelivr.net/npm/@xterm/addon-web-links@0/lib/addon-web-links.min.js"></script>
2335
2366
  <script src="https://cdn.jsdelivr.net/npm/@xterm/addon-webgl@0/lib/addon-webgl.min.js"></script>
2336
- <script type="module" src="app.js"></script>
2367
+ <script type="module" src="app.js?v=20260824g"></script>
2337
2368
  <div id="pwa-install-modal" class="pwa-modal hidden">
2338
2369
  <div class="pwa-modal-backdrop"></div>
2339
2370
  <div class="pwa-modal-card">
@@ -0,0 +1,11 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="Junie">
2
+ <defs>
3
+ <linearGradient id="junie-gradient" x1="8" y1="8" x2="56" y2="56" gradientUnits="userSpaceOnUse">
4
+ <stop stop-color="#8B5CF6"/>
5
+ <stop offset="1" stop-color="#F97316"/>
6
+ </linearGradient>
7
+ </defs>
8
+ <rect width="64" height="64" rx="15" fill="url(#junie-gradient)"/>
9
+ <path d="M39 15v26c0 7.7-5.4 12-13.2 12-5.3 0-9.4-2.2-12-6.4l7.1-5c1.3 2 2.7 3 4.6 3 2.5 0 4-1.4 4-4.4V15H39Z" fill="#fff"/>
10
+ <circle cx="47.5" cy="17.5" r="4.5" fill="#fff" fill-opacity=".9"/>
11
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="Kimi Code">
2
+ <rect width="64" height="64" rx="14" fill="#1769ff"/>
3
+ <path d="M18 14h9v15l13-15h11L36 31l16 19H40L27 34v16h-9z" fill="#fff"/>
4
+ </svg>
@@ -93,6 +93,8 @@ export function initHeader() {
93
93
  input.type = "text";
94
94
  input.className = "header-rename-input";
95
95
  input.value = currentText;
96
+ var titleBar = headerTitleEl.closest(".title-bar-content");
97
+ if (titleBar) titleBar.classList.add("session-renaming");
96
98
  headerTitleEl.style.display = "none";
97
99
  headerRenameBtn.style.display = "none";
98
100
  headerTitleEl.parentNode.insertBefore(input, headerTitleEl.nextSibling);
@@ -117,6 +119,7 @@ export function initHeader() {
117
119
  }
118
120
  }
119
121
  input.remove();
122
+ if (titleBar) titleBar.classList.remove("session-renaming");
120
123
  headerTitleEl.style.display = "";
121
124
  headerRenameBtn.style.display = "";
122
125
  }
@@ -126,6 +129,7 @@ export function initHeader() {
126
129
  if (e.key === "Escape") {
127
130
  e.preventDefault();
128
131
  input.remove();
132
+ if (titleBar) titleBar.classList.remove("session-renaming");
129
133
  headerTitleEl.style.display = "";
130
134
  headerRenameBtn.style.display = "";
131
135
  }
@@ -22,7 +22,7 @@ import { handleFindInSessionResults } from './session-search.js';
22
22
  import { syncPaneTitles, maybeRestoreSplitGroup, openGroup } from './split-view.js';
23
23
  import { showPairDialog, handlePairCreated, handleSplitDelegation, showWorkerDelegationNotice, hideWorkerDelegationNotice } from './split-pair-ui.js';
24
24
  import { renderWorkerProposal, updateWorkerProposal } from './worker-proposal.js';
25
- import { handleInputSync, autoResize, builtinCommands, setScheduleBtnDisabled } from './input.js';
25
+ import { handleInputSync, autoResize, builtinCommands, setScheduleBtnDisabled, sendShellResultToAgent } from './input.js';
26
26
  import { startThinking, appendThinking, stopThinking, resetThinkingGroup, createToolItem, updateToolExecuting, updateToolResult, markAllToolsDone, closeToolGroup, removeToolFromGroup, resetToolState, getTools, getPlanContent, setPlanContent, renderPlanBanner, renderPlanCard, getTodoTools, handleTodoWrite, handleTaskCreate, handleTaskUpdate, applyDeadSessionTodoCompaction, isPlanFilePath, enableMainInput, addTurnMeta, updateSubagentActivity, addSubagentToolEntry, markSubagentDone, initSubagentStop, updateSubagentProgress, updateSubagentTaskStatus, renderAskUserQuestion, markAskUserAnswered, renderPermissionRequest, markPermissionCancelled, markPermissionResolved, renderElicitationRequest, markElicitationResolved, renderUserDialogRequest, markUserDialogResolved, updateThinkingTokens } from './tools.js';
27
27
  import { showDoneNotification, playDoneSound, isNotifAlertEnabled, isNotifSoundEnabled } from './notifications.js';
28
28
  import { handleFsList, handleFsRead, handleFileChanged, handleDirChanged, handleFileHistory, handleGitDiff, handleFileAt, refreshIfOpen, getPendingNavigate, handleFsSearch, presentMarkdownEdit } from './filebrowser.js';
@@ -42,6 +42,7 @@ import { checkAdminAccess } from './admin.js';
42
42
  import { mateAvatarUrl } from './avatar.js';
43
43
  import { showImageModal, sendExtensionCommand, handleMcpToolCallMessage } from './app-misc.js';
44
44
  import { handleMcpServersState } from './mcp-ui.js';
45
+ import { handleShellCommandResult } from './shell-command.js';
45
46
  import { handleLoopRegistryUpdated, handleScheduleRunStarted, handleScheduleRunFinished, handleLoopScheduled, isSchedulerOpen, enterCraftingMode, exitCraftingMode, handleLoopRegistryFiles } from './scheduler.js';
46
47
 
47
48
  // --- App module imports ---
@@ -51,6 +52,7 @@ import { setStatus } from './app-connection.js';
51
52
  import { handleWhatsNewState, handleWhatsNewSeenResult, setKnownEntries as setWhatsNewKnownEntries } from './whats-new.js';
52
53
  import { closeArticle as closeWhatsNewArticle } from './whats-new-article.js';
53
54
  import { resolvePaneSession, resolveSwitchedVendor } from './pane-session.js';
55
+ import { selectDefaultVendorForBlankSession } from './vendor-selection.js';
54
56
  import { getModelEffortLevels, accumulateUsage, updateUsagePanel, accumulateContext, updateContextPanel, renderCtxPopover, updateStatusPanel } from './app-panels.js';
55
57
  import { updateProjectList, resetClientState, showUpdateAvailable, handleRemoveProjectCheckResult, handleRemoveProjectResult, handleBrowseDirResult, handleAddProjectResult, handleCloneProgress } from './app-projects.js';
56
58
  import { updateHistorySentinel, prependOlderHistory } from './app-header.js';
@@ -413,6 +415,15 @@ export function processMessage(msg) {
413
415
  break;
414
416
 
415
417
  case "model_info": {
418
+ // Installation state applies across vendors. Keep it even when the
419
+ // model payload itself is stale for the active session.
420
+ var _vendorState = {};
421
+ if (msg.availableVendors) _vendorState.availableVendors = msg.availableVendors;
422
+ if (msg.installedVendors) _vendorState.installedVendors = msg.installedVendors;
423
+ if (Object.keys(_vendorState).length > 0) {
424
+ store.set(_vendorState);
425
+ selectDefaultVendorForBlankSession();
426
+ }
416
427
  // Drop stale model_info from a vendor that doesn't match the active
417
428
  // session's vendor. On high-latency connections, the server's default-
418
429
  // adapter model_info can arrive after session_switched has already
@@ -446,8 +457,6 @@ export function processMessage(msg) {
446
457
  _miUpdate.currentModel = store.get('currentModel');
447
458
  }
448
459
  if (msg.vendor && !store.get('vendorSelectionLocked')) _miUpdate.currentVendor = msg.vendor;
449
- if (msg.availableVendors) _miUpdate.availableVendors = msg.availableVendors;
450
- if (msg.installedVendors) _miUpdate.installedVendors = msg.installedVendors;
451
460
  if (msg.capabilities) _miUpdate.vendorCapabilities = msg.capabilities;
452
461
  store.set(_miUpdate);
453
462
  updateSettingsModels(_modelVal, msg.models || []);
@@ -660,7 +669,7 @@ export function processMessage(msg) {
660
669
  var _effectiveTerminalId = (typeof msg.runtimeTerminalId === "number")
661
670
  ? msg.runtimeTerminalId
662
671
  : (typeof msg.terminalId === "number" ? msg.terminalId : null);
663
- store.set({ activeSessionId: msg.id, cliSessionId: msg.cliSessionId || null, currentModel: msg.model || store.get('currentModel'), currentEffort: msg.effort || store.get('currentEffort'), vendorCapabilities: msg.capabilities || {}, sessionIsProcessing: !!msg.isProcessing, activeSessionMode: _effectiveMode, activeTerminalId: _effectiveTerminalId, sessionHasHistory: !!msg.hasHistory, sessionFullAccess: msg.permissionMode === "bypassPermissions", currentMode: msg.permissionMode || store.get('currentMode') });
672
+ store.set({ activeSessionId: msg.id, cliSessionId: msg.cliSessionId || null, currentModel: msg.model || store.get('currentModel'), currentEffort: msg.effort || store.get('currentEffort'), vendorCapabilities: msg.capabilities || {}, sessionIsProcessing: !!msg.isProcessing, activeSessionMode: _effectiveMode, activeTerminalId: _effectiveTerminalId, sessionHasHistory: !!msg.hasHistory, sessionVendorBound: !!msg.vendor || !!msg.hasHistory, sessionFullAccess: msg.permissionMode === "bypassPermissions", currentMode: msg.permissionMode || store.get('currentMode') });
664
673
  // TUI sessions swap the chat UI for an embedded xterm running
665
674
  // `claude` inside a real PTY. Mount or tear down before the rest of
666
675
  // the chat-side bookkeeping runs so we don't waste work on hidden DOM.
@@ -705,7 +714,7 @@ export function processMessage(msg) {
705
714
  }
706
715
  }
707
716
  if (!msg.hasHistory && !msg.vendor) {
708
- // Preserve explicit pre-message vendor choice on brand-new sessions.
717
+ selectDefaultVendorForBlankSession();
709
718
  }
710
719
  // Vendor toggle visibility + active-vendor indicator next to the
711
720
  // model chip.
@@ -1209,6 +1218,15 @@ export function processMessage(msg) {
1209
1218
  addConflictMessage(msg);
1210
1219
  break;
1211
1220
 
1221
+ case "session_writer_conflict":
1222
+ removeMatePreThinking();
1223
+ setActivity(null);
1224
+ stopThinking();
1225
+ markAllToolsDone();
1226
+ closeToolGroup();
1227
+ addSystemMessage(msg.text || "This session is already open in another process.", true);
1228
+ break;
1229
+
1212
1230
  case "context_overflow":
1213
1231
  removeMatePreThinking();
1214
1232
  setActivity(null);
@@ -1371,6 +1389,11 @@ export function processMessage(msg) {
1371
1389
  updateTerminalList(msg.terminals);
1372
1390
  break;
1373
1391
 
1392
+ case "shell_command_result":
1393
+ handleShellCommandResult(msg);
1394
+ if (!msg.error) sendShellResultToAgent(msg);
1395
+ break;
1396
+
1374
1397
  case "context_sources_state":
1375
1398
  handleContextSourcesState(msg);
1376
1399
  break;
@@ -5,7 +5,7 @@ import { refreshIcons } from "./icons.js";
5
5
  import { escapeHtml, showToast } from "./utils.js";
6
6
  import { store } from './store.js';
7
7
  import { getWs } from './ws-ref.js';
8
- import { VENDOR_NAMES } from './app-rendering.js';
8
+ import { VENDOR_NAMES, isExperimentalVendor } from './app-rendering.js';
9
9
  import { reportPaneContext } from './pane-bridge.js';
10
10
 
11
11
  // --- Module-owned state (not in store) ---
@@ -89,7 +89,7 @@ var EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
89
89
  var EFFORT_LEVELS_BY_VENDOR = {
90
90
  claude: ["low", "medium", "high", "xhigh", "max"],
91
91
  codex: ["minimal", "low", "medium", "high", "xhigh"],
92
- gemini: [],
92
+ antigravity: [],
93
93
  opencode: [],
94
94
  kiro: ["low", "medium", "high", "xhigh", "max"],
95
95
  };
@@ -443,14 +443,24 @@ export function initPanels() {
443
443
  var vendorToggleWrap = $("vendor-toggle-wrap");
444
444
  var vendorBtnClaude = $("vendor-btn-claude");
445
445
  var vendorBtnCodex = $("vendor-btn-codex");
446
- var vendorBtnGemini = $("vendor-btn-gemini");
446
+ var vendorBtnAntigravity = $("vendor-btn-antigravity");
447
447
  var vendorBtnOpenCode = $("vendor-btn-opencode");
448
+ var vendorBtnKimi = $("vendor-btn-kimi");
449
+ var vendorBtnGrok = $("vendor-btn-grok");
450
+ var vendorBtnCopilot = $("vendor-btn-copilot");
451
+ var vendorBtnQwen = $("vendor-btn-qwen");
452
+ var vendorBtnJunie = $("vendor-btn-junie");
448
453
  var vendorBtnKiro = $("vendor-btn-kiro");
449
454
  var vendorBtns = {
450
455
  claude: vendorBtnClaude,
451
456
  codex: vendorBtnCodex,
452
- gemini: vendorBtnGemini,
457
+ antigravity: vendorBtnAntigravity,
453
458
  opencode: vendorBtnOpenCode,
459
+ kimi: vendorBtnKimi,
460
+ grok: vendorBtnGrok,
461
+ copilot: vendorBtnCopilot,
462
+ qwen: vendorBtnQwen,
463
+ junie: vendorBtnJunie,
454
464
  kiro: vendorBtnKiro,
455
465
  };
456
466
 
@@ -466,7 +476,10 @@ export function initPanels() {
466
476
  var isInstalled = installed.indexOf(v) !== -1;
467
477
  btn.classList.toggle("active", v === current);
468
478
  btn.classList.toggle("disabled", !isInstalled);
469
- btn.title = isInstalled ? (VENDOR_NAMES[v] || v) : (VENDOR_NAMES[v] || v) + " is not installed";
479
+ btn.classList.toggle("experimental", isExperimentalVendor(v));
480
+ var title = isInstalled ? (VENDOR_NAMES[v] || v) : (VENDOR_NAMES[v] || v) + " is not installed";
481
+ if (isExperimentalVendor(v)) title += " · Experimental integration; not yet validated through direct use testing";
482
+ btn.title = title;
470
483
  }
471
484
  }
472
485
 
@@ -474,15 +487,20 @@ export function initPanels() {
474
487
  if (vendor === (store.get('currentVendor') || "claude")) return;
475
488
  var installed = store.get('installedVendors') || [];
476
489
  if (installed.indexOf(vendor) === -1) return;
477
- store.set({ currentVendor: vendor, currentModel: "", currentModels: [], vendorSelectionLocked: true });
490
+ store.set({ currentVendor: vendor, currentModel: "", currentModels: [], vendorSelectionLocked: true, sessionVendorBound: true });
478
491
  var ws = getWs();
479
492
  if (ws) ws.send(JSON.stringify({ type: "set_vendor", vendor: vendor }));
480
493
  }
481
494
 
482
495
  if (vendorBtnClaude) vendorBtnClaude.addEventListener("click", function() { onVendorClick("claude"); });
483
496
  if (vendorBtnCodex) vendorBtnCodex.addEventListener("click", function() { onVendorClick("codex"); });
484
- if (vendorBtnGemini) vendorBtnGemini.addEventListener("click", function() { onVendorClick("gemini"); });
497
+ if (vendorBtnAntigravity) vendorBtnAntigravity.addEventListener("click", function() { onVendorClick("antigravity"); });
485
498
  if (vendorBtnOpenCode) vendorBtnOpenCode.addEventListener("click", function() { onVendorClick("opencode"); });
499
+ if (vendorBtnKimi) vendorBtnKimi.addEventListener("click", function() { onVendorClick("kimi"); });
500
+ if (vendorBtnGrok) vendorBtnGrok.addEventListener("click", function() { onVendorClick("grok"); });
501
+ if (vendorBtnCopilot) vendorBtnCopilot.addEventListener("click", function() { onVendorClick("copilot"); });
502
+ if (vendorBtnQwen) vendorBtnQwen.addEventListener("click", function() { onVendorClick("qwen"); });
503
+ if (vendorBtnJunie) vendorBtnJunie.addEventListener("click", function() { onVendorClick("junie"); });
486
504
  if (vendorBtnKiro) vendorBtnKiro.addEventListener("click", function() { onVendorClick("kiro"); });
487
505
 
488
506
  // --- Reactive UI sync ---
@@ -861,7 +879,9 @@ export function updateContextPanel() {
861
879
  hCtxEl = document.createElement("div");
862
880
  hCtxEl.className = "header-context";
863
881
  hCtxEl.innerHTML = '<div class="header-context-bar"><div class="header-context-fill"></div></div><span class="header-context-label"></span>';
864
- statusArea.insertBefore(hCtxEl, statusArea.firstChild);
882
+ var workerBtn = document.getElementById("header-add-worker-btn");
883
+ var contextAnchor = workerBtn && workerBtn.parentNode === statusArea ? workerBtn.nextSibling : statusArea.firstChild;
884
+ statusArea.insertBefore(hCtxEl, contextAnchor);
865
885
  hCtxEl.addEventListener("mouseenter", function() {
866
886
  if (store.get('richContextUsage')) {
867
887
  showCtxPopover();
@@ -24,6 +24,7 @@ import { isSchedulerOpen, closeScheduler, resetScheduler } from './scheduler.js'
24
24
  import { connect, cancelReconnect, setStatus } from './app-connection.js';
25
25
  import { setTurnCounter, setPrependAnchor, setActivityEl, setIsUserScrolledUp, hideSuggestionChips } from './app-rendering.js';
26
26
  import { resetToolState, enableMainInput, resetTurnMetaCost } from './tools.js';
27
+ import { resetShellCommand } from './shell-command.js';
27
28
  import { clearPendingImages } from './input.js';
28
29
  import { clearAllMentionActive } from './sidebar-mates.js';
29
30
  import { setRewindMode } from './rewind.js';
@@ -321,7 +322,7 @@ export function renderProjectList() {
321
322
  isWorktree: p.isWorktree || false,
322
323
  parentSlug: p.parentSlug || null,
323
324
  branch: p.branch || null,
324
- worktreeAccessible: p.worktreeAccessible !== undefined ? p.worktreeAccessible : true,
325
+ worktreeExternal: p.worktreeExternal === true,
325
326
  };
326
327
  });
327
328
  var st = store.snap();
@@ -368,6 +369,7 @@ export function resetClientState() {
368
369
  store.set({ currentFullText: "" });
369
370
  resetToolState();
370
371
  clearPendingImages();
372
+ resetShellCommand();
371
373
  clearAllMentionActive();
372
374
  setActivityEl(null);
373
375
  store.set({ processing: false });