groove-dev 0.27.165 → 0.27.168

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 (43) hide show
  1. package/node_modules/@groove-dev/cli/package.json +1 -1
  2. package/node_modules/@groove-dev/daemon/package.json +1 -1
  3. package/node_modules/@groove-dev/daemon/src/adaptive.js +37 -10
  4. package/node_modules/@groove-dev/daemon/src/classifier.js +3 -1
  5. package/node_modules/@groove-dev/daemon/src/index.js +10 -9
  6. package/node_modules/@groove-dev/daemon/src/memory.js +9 -1
  7. package/node_modules/@groove-dev/daemon/src/process.js +14 -5
  8. package/node_modules/@groove-dev/daemon/src/registry.js +18 -3
  9. package/node_modules/@groove-dev/daemon/src/rotator.js +81 -10
  10. package/node_modules/@groove-dev/daemon/src/routes/agents.js +38 -25
  11. package/node_modules/@groove-dev/daemon/src/tunnel-manager.js +28 -21
  12. package/node_modules/@groove-dev/daemon/test/rotator.test.js +1 -1
  13. package/node_modules/@groove-dev/gui/dist/assets/{index-SZBexPhJ.js → index-CSMIQsrG.js} +94 -94
  14. package/node_modules/@groove-dev/gui/dist/index.html +1 -1
  15. package/node_modules/@groove-dev/gui/package.json +1 -1
  16. package/node_modules/@groove-dev/gui/src/components/agents/agent-panel.jsx +1 -1
  17. package/node_modules/@groove-dev/gui/src/components/editor/terminal.jsx +32 -2
  18. package/node_modules/@groove-dev/gui/src/components/fleet/fleet-content.jsx +4 -1
  19. package/node_modules/@groove-dev/gui/src/components/fleet/fleet-pane.jsx +11 -6
  20. package/node_modules/@groove-dev/gui/src/stores/groove.js +8 -4
  21. package/node_modules/@groove-dev/gui/src/stores/slices/agents-slice.js +9 -0
  22. package/package.json +1 -1
  23. package/packages/cli/package.json +1 -1
  24. package/packages/daemon/package.json +1 -1
  25. package/packages/daemon/src/adaptive.js +37 -10
  26. package/packages/daemon/src/classifier.js +3 -1
  27. package/packages/daemon/src/index.js +10 -9
  28. package/packages/daemon/src/memory.js +9 -1
  29. package/packages/daemon/src/process.js +14 -5
  30. package/packages/daemon/src/registry.js +18 -3
  31. package/packages/daemon/src/rotator.js +81 -10
  32. package/packages/daemon/src/routes/agents.js +38 -25
  33. package/packages/daemon/src/tunnel-manager.js +28 -21
  34. package/packages/gui/dist/assets/{index-SZBexPhJ.js → index-CSMIQsrG.js} +94 -94
  35. package/packages/gui/dist/index.html +1 -1
  36. package/packages/gui/package.json +1 -1
  37. package/packages/gui/src/components/agents/agent-panel.jsx +1 -1
  38. package/packages/gui/src/components/editor/terminal.jsx +32 -2
  39. package/packages/gui/src/components/fleet/fleet-content.jsx +4 -1
  40. package/packages/gui/src/components/fleet/fleet-pane.jsx +11 -6
  41. package/packages/gui/src/stores/groove.js +8 -4
  42. package/packages/gui/src/stores/slices/agents-slice.js +9 -0
  43. package/terminal/Screenshot_2026-05-19_at_12.20.15_PM.png +0 -0
@@ -6,7 +6,7 @@
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <link rel="icon" type="image/png" href="/favicon.png" />
8
8
  <title>Groove GUI</title>
9
- <script type="module" crossorigin src="/assets/index-SZBexPhJ.js"></script>
9
+ <script type="module" crossorigin src="/assets/index-CSMIQsrG.js"></script>
10
10
  <link rel="modulepreload" crossorigin href="/assets/vendor-26L3JoZv.js">
11
11
  <link rel="modulepreload" crossorigin href="/assets/reactflow-DoBZjiHE.js">
12
12
  <link rel="modulepreload" crossorigin href="/assets/codemirror-BYKpdS2W.js">
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groove-dev/gui",
3
- "version": "0.27.165",
3
+ "version": "0.27.168",
4
4
  "description": "GROOVE GUI — visual agent control plane",
5
5
  "license": "FSL-1.1-Apache-2.0",
6
6
  "type": "module",
@@ -88,7 +88,7 @@ export function AgentPanel() {
88
88
  const agentId = detailPanel?.type === 'agent' ? detailPanel.agentId : null;
89
89
  const liveAgent = agentId ? agents.find((a) => a.id === agentId) : null;
90
90
  if (liveAgent) cachedAgentRef.current = liveAgent;
91
- else if (cachedAgentRef.current && cachedAgentRef.current.id !== agentId) cachedAgentRef.current = null;
91
+ else if (cachedAgentRef.current && agentId && cachedAgentRef.current.id !== agentId) cachedAgentRef.current = null;
92
92
  const agent = liveAgent || cachedAgentRef.current;
93
93
  const isAlive = liveAgent?.status === 'running' || liveAgent?.status === 'starting';
94
94
 
@@ -85,6 +85,7 @@ function TerminalInstance({ tabId, visible, registerKill, onSelectionChange }) {
85
85
  }
86
86
 
87
87
  const requestId = `spawn-${++spawnSeq}`;
88
+ const initialCols = term.cols;
88
89
  ws.send(JSON.stringify({ type: 'terminal:spawn', cols: term.cols, rows: term.rows, requestId }));
89
90
 
90
91
  function onMessage(event) {
@@ -101,6 +102,14 @@ function TerminalInstance({ tabId, visible, registerKill, onSelectionChange }) {
101
102
  if (w?.readyState === WebSocket.OPEN) {
102
103
  w.send(JSON.stringify({ type: 'terminal:resize', id: termIdRef.current, rows: r, cols: c }));
103
104
  lastSizeRef.current = { cols: c, rows: r };
105
+ if (c !== initialCols) {
106
+ setTimeout(() => {
107
+ term.clear();
108
+ if (w.readyState === WebSocket.OPEN && termIdRef.current) {
109
+ w.send(JSON.stringify({ type: 'terminal:input', id: termIdRef.current, data: '\x0c' }));
110
+ }
111
+ }, 80);
112
+ }
104
113
  }
105
114
  }
106
115
  }, 50);
@@ -133,21 +142,42 @@ function TerminalInstance({ tabId, visible, registerKill, onSelectionChange }) {
133
142
  });
134
143
  }
135
144
 
145
+ let hasSpawned = false;
146
+ function tryInitSpawn() {
147
+ if (hasSpawned) return;
148
+ if (term.cols >= 10 && term.rows >= 2) {
149
+ hasSpawned = true;
150
+ trySpawn();
151
+ }
152
+ }
153
+
136
154
  requestAnimationFrame(() => {
137
155
  try { fitAddon.fit(); } catch {}
138
156
  requestAnimationFrame(() => {
139
157
  try { fitAddon.fit(); } catch {}
140
- trySpawn();
158
+ tryInitSpawn();
141
159
  });
142
160
  });
143
161
 
162
+ const spawnFallback = setTimeout(() => {
163
+ if (!hasSpawned) {
164
+ try { fitAddon.fit(); } catch {}
165
+ hasSpawned = true;
166
+ trySpawn();
167
+ }
168
+ }, 3000);
169
+
144
170
  const observer = new ResizeObserver(() => {
145
171
  if (!visibleRef.current) return;
146
- requestAnimationFrame(() => { try { fitAddon.fit(); } catch {} });
172
+ requestAnimationFrame(() => {
173
+ try { fitAddon.fit(); } catch {}
174
+ tryInitSpawn();
175
+ });
147
176
  });
148
177
  observer.observe(containerRef.current);
149
178
 
150
179
  return () => {
180
+ clearTimeout(spawnFallback);
151
181
  observer.disconnect();
152
182
  if (handlerRef.current) {
153
183
  handlerRef.current.ws.removeEventListener('message', handlerRef.current.handler);
@@ -5,7 +5,10 @@ import { useGrooveStore } from '../../stores/groove';
5
5
  import { FleetPane } from './fleet-pane';
6
6
 
7
7
  export function FleetContent() {
8
- const selected = useGrooveStore((s) => s.fleetSelectedAgents);
8
+ const rawSelected = useGrooveStore((s) => s.fleetSelectedAgents);
9
+ const lastSelectedRef = useRef(rawSelected);
10
+ if (rawSelected[0] || rawSelected[1]) lastSelectedRef.current = rawSelected;
11
+ const selected = (rawSelected[0] || rawSelected[1]) ? rawSelected : lastSelectedRef.current;
9
12
  const splitMode = useGrooveStore((s) => s.fleetSplitMode);
10
13
  const fleetSelectAgent = useGrooveStore((s) => s.fleetSelectAgent);
11
14
 
@@ -28,7 +28,12 @@ const STATUS_LABEL = {
28
28
  };
29
29
 
30
30
  export function FleetPane({ agentId, paneIndex }) {
31
- const liveAgent = useGrooveStore((s) => s.agents.find((a) => a.id === agentId));
31
+ const effectiveId = agentId || null;
32
+ const lastIdRef = useRef(effectiveId);
33
+ if (effectiveId) lastIdRef.current = effectiveId;
34
+ const resolvedId = effectiveId || lastIdRef.current;
35
+
36
+ const liveAgent = useGrooveStore((s) => resolvedId ? s.agents.find((a) => a.id === resolvedId) : null);
32
37
  const fleetSelectAgent = useGrooveStore((s) => s.fleetSelectAgent);
33
38
  const fleetMarkRead = useGrooveStore((s) => s.fleetMarkRead);
34
39
 
@@ -43,15 +48,15 @@ export function FleetPane({ agentId, paneIndex }) {
43
48
  }
44
49
 
45
50
  useEffect(() => {
46
- if (!liveAgent && agentId && !gone) {
47
- goneTimer.current = setTimeout(() => setGone(true), 2000);
51
+ if (!liveAgent && resolvedId && !gone) {
52
+ goneTimer.current = setTimeout(() => setGone(true), 3000);
48
53
  return () => { if (goneTimer.current) clearTimeout(goneTimer.current); };
49
54
  }
50
- }, [liveAgent, agentId, gone]);
55
+ }, [liveAgent, resolvedId, gone]);
51
56
 
52
57
  useEffect(() => {
53
- if (agentId) fleetMarkRead(agentId);
54
- }, [agentId, fleetMarkRead]);
58
+ if (effectiveId) fleetMarkRead(effectiveId);
59
+ }, [effectiveId, fleetMarkRead]);
55
60
 
56
61
  const agent = liveAgent || lastAgentRef.current;
57
62
 
@@ -199,9 +199,6 @@ export const useGrooveStore = create((set, get) => ({
199
199
  }
200
200
  for (const id of removed) delete timeline[id];
201
201
  const updates = { agents, tokenTimeline: timeline, hydrated: true };
202
- if (removed.length > 0 && st.detailPanel?.type === 'agent' && removed.includes(st.detailPanel.agentId)) {
203
- updates.detailPanel = null;
204
- }
205
202
  set(updates);
206
203
  break;
207
204
  }
@@ -527,8 +524,15 @@ export const useGrooveStore = create((set, get) => ({
527
524
  const tid = get().activeTeamId;
528
525
  teamDetailPanels = { ...s.teamDetailPanels, [tid]: newPanel };
529
526
  }
527
+ let fleetSelectedAgents = s.fleetSelectedAgents;
528
+ if (fleetSelectedAgents[0] === oldId || fleetSelectedAgents[1] === oldId) {
529
+ fleetSelectedAgents = [
530
+ fleetSelectedAgents[0] === oldId ? newId : fleetSelectedAgents[0],
531
+ fleetSelectedAgents[1] === oldId ? newId : fleetSelectedAgents[1],
532
+ ];
533
+ }
530
534
  try { localStorage.setItem('groove:chatHistory', JSON.stringify(chatHistory)); } catch {}
531
- return { chatHistory, tokenTimeline, activityLog, chatInputs, detailPanel, teamDetailPanels };
535
+ return { chatHistory, tokenTimeline, activityLog, chatInputs, detailPanel, teamDetailPanels, fleetSelectedAgents };
532
536
  });
533
537
  break;
534
538
  }
@@ -66,6 +66,15 @@ export const createAgentsSlice = (set, get) => ({
66
66
  async killAgent(id, purge = false) {
67
67
  try {
68
68
  await api.delete(`/agents/${encodeURIComponent(id)}?purge=${purge}`);
69
+ const dp = get().detailPanel;
70
+ if (dp?.type === 'agent' && dp.agentId === id) {
71
+ const tid = get().activeTeamId;
72
+ set({ detailPanel: null, teamDetailPanels: { ...get().teamDetailPanels, [tid]: null } });
73
+ }
74
+ const sel = get().fleetSelectedAgents;
75
+ if (sel[0] === id || sel[1] === id) {
76
+ set({ fleetSelectedAgents: [sel[0] === id ? null : sel[0], sel[1] === id ? null : sel[1]] });
77
+ }
69
78
  if (purge) {
70
79
  set((s) => {
71
80
  const chatHistory = { ...s.chatHistory };