groove-dev 0.27.175 → 0.27.179

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 (61) hide show
  1. package/AUTONOMOUS_AGENT_FRAMEWORK.md +654 -0
  2. package/innerchat/Screenshot_2026-06-09_at_2.19.42_PM.png +0 -0
  3. package/node_modules/@groove-dev/cli/package.json +1 -1
  4. package/node_modules/@groove-dev/daemon/package.json +1 -1
  5. package/node_modules/@groove-dev/daemon/src/api.js +3 -1
  6. package/node_modules/@groove-dev/daemon/src/autostate.js +198 -0
  7. package/node_modules/@groove-dev/daemon/src/index.js +6 -0
  8. package/node_modules/@groove-dev/daemon/src/orchestrator.js +585 -0
  9. package/node_modules/@groove-dev/daemon/src/process.js +27 -2
  10. package/node_modules/@groove-dev/daemon/src/registry.js +2 -1
  11. package/node_modules/@groove-dev/daemon/src/routes/auto-agents.js +264 -0
  12. package/node_modules/@groove-dev/daemon/src/validate.js +6 -0
  13. package/node_modules/@groove-dev/gui/dist/assets/index-CTer01Vg.js +1065 -0
  14. package/node_modules/@groove-dev/gui/dist/assets/index-DTFtRtkx.css +1 -0
  15. package/node_modules/@groove-dev/gui/dist/index.html +2 -2
  16. package/node_modules/@groove-dev/gui/package.json +1 -1
  17. package/node_modules/@groove-dev/gui/src/App.jsx +2 -0
  18. package/node_modules/@groove-dev/gui/src/components/agents/agent-config.jsx +53 -0
  19. package/node_modules/@groove-dev/gui/src/components/agents/agent-panel.jsx +95 -2
  20. package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +50 -1
  21. package/node_modules/@groove-dev/gui/src/components/auto-agents/auto-agent-card.jsx +139 -0
  22. package/node_modules/@groove-dev/gui/src/components/auto-agents/auto-agent-detail.jsx +286 -0
  23. package/node_modules/@groove-dev/gui/src/components/auto-agents/setup-wizard.jsx +284 -0
  24. package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +2 -1
  25. package/node_modules/@groove-dev/gui/src/stores/groove.js +14 -0
  26. package/node_modules/@groove-dev/gui/src/stores/slices/auto-agents-slice.js +149 -0
  27. package/node_modules/@groove-dev/gui/src/stores/slices/ui-slice.js +14 -2
  28. package/node_modules/@groove-dev/gui/src/views/auto-agents.jsx +70 -0
  29. package/node_modules/@groove-dev/gui/src/views/settings.jsx +13 -6
  30. package/package.json +1 -1
  31. package/packages/cli/package.json +1 -1
  32. package/packages/daemon/package.json +1 -1
  33. package/packages/daemon/src/api.js +3 -1
  34. package/packages/daemon/src/autostate.js +198 -0
  35. package/packages/daemon/src/index.js +6 -0
  36. package/packages/daemon/src/orchestrator.js +585 -0
  37. package/packages/daemon/src/process.js +27 -2
  38. package/packages/daemon/src/registry.js +2 -1
  39. package/packages/daemon/src/routes/auto-agents.js +264 -0
  40. package/packages/daemon/src/validate.js +6 -0
  41. package/packages/gui/dist/assets/index-CTer01Vg.js +1065 -0
  42. package/packages/gui/dist/assets/index-DTFtRtkx.css +1 -0
  43. package/packages/gui/dist/index.html +2 -2
  44. package/packages/gui/package.json +1 -1
  45. package/packages/gui/src/App.jsx +2 -0
  46. package/packages/gui/src/components/agents/agent-config.jsx +53 -0
  47. package/packages/gui/src/components/agents/agent-panel.jsx +95 -2
  48. package/packages/gui/src/components/agents/spawn-wizard.jsx +50 -1
  49. package/packages/gui/src/components/auto-agents/auto-agent-card.jsx +139 -0
  50. package/packages/gui/src/components/auto-agents/auto-agent-detail.jsx +286 -0
  51. package/packages/gui/src/components/auto-agents/setup-wizard.jsx +284 -0
  52. package/packages/gui/src/components/layout/activity-bar.jsx +2 -1
  53. package/packages/gui/src/stores/groove.js +14 -0
  54. package/packages/gui/src/stores/slices/auto-agents-slice.js +149 -0
  55. package/packages/gui/src/stores/slices/ui-slice.js +14 -2
  56. package/packages/gui/src/views/auto-agents.jsx +70 -0
  57. package/packages/gui/src/views/settings.jsx +13 -6
  58. package/node_modules/@groove-dev/gui/dist/assets/index-BMaxZVeX.js +0 -1035
  59. package/node_modules/@groove-dev/gui/dist/assets/index-BvJwMNAX.css +0 -1
  60. package/packages/gui/dist/assets/index-BMaxZVeX.js +0 -1035
  61. package/packages/gui/dist/assets/index-BvJwMNAX.css +0 -1
@@ -0,0 +1,284 @@
1
+ // FSL-1.1-Apache-2.0 — see LICENSE
2
+ import { useState, useRef, useEffect, useCallback } from 'react';
3
+ import { useGrooveStore } from '../../stores/groove';
4
+ import { Button } from '../ui/button';
5
+ import { Badge } from '../ui/badge';
6
+ import { Sheet, SheetContent } from '../ui/sheet';
7
+ import { cn } from '../../lib/cn';
8
+ import { api } from '../../lib/api';
9
+ import {
10
+ Bot, Send, ArrowRight, Sparkles, Clock, Shield, FileText,
11
+ ChevronDown, ChevronUp, Edit3, Check, X,
12
+ } from 'lucide-react';
13
+
14
+ const CADENCE_PRESETS = [
15
+ { label: 'Every 15 min', value: '*/15 * * * *' },
16
+ { label: 'Every 30 min', value: '*/30 * * * *' },
17
+ { label: 'Every hour', value: '0 * * * *' },
18
+ { label: 'Every 2 hours', value: '0 */2 * * *' },
19
+ { label: 'Every 6 hours', value: '0 */6 * * *' },
20
+ { label: 'Daily 9 AM', value: '0 9 * * *' },
21
+ { label: 'Weekdays 9 AM', value: '0 9 * * 1-5' },
22
+ ];
23
+
24
+ const SETUP_SYSTEM_PROMPT = `You are a setup assistant for creating autonomous AI agents in Groove.
25
+ Your job is to interview the user about what they want their auto agent to do, then generate a complete configuration.
26
+
27
+ Ask 3-5 focused questions to understand:
28
+ 1. What is the agent's goal/objective?
29
+ 2. What tools, APIs, or commands will it need?
30
+ 3. What should it never do? (guardrails)
31
+ 4. How often should it check in? (cadence)
32
+ 5. What does success look like? (milestones/roadmap)
33
+
34
+ After gathering enough info, generate a JSON config block wrapped in \`\`\`json ... \`\`\` with this exact shape:
35
+ {
36
+ "ready": true,
37
+ "name": "short-kebab-name",
38
+ "description": "One-line description",
39
+ "cadence": "0 * * * *",
40
+ "role": "fullstack",
41
+ "prompt": "Full system prompt for the agent...",
42
+ "roadmap": "## Stage 1: ...\\n## Stage 2: ...",
43
+ "guardrails": ["Never do X", "Always do Y"],
44
+ "maxIterations": null
45
+ }
46
+
47
+ Make the prompt comprehensive — include identity, philosophy, available tools, failure handling, and the never-park directive.
48
+ Make the roadmap have 2-4 stages with clear graduation criteria.
49
+ Keep the conversation friendly and efficient. Ask one question at a time.`;
50
+
51
+ function ChatMessage({ msg }) {
52
+ const isUser = msg.role === 'user';
53
+ return (
54
+ <div className={cn('flex gap-2', isUser ? 'justify-end' : 'justify-start')}>
55
+ {!isUser && (
56
+ <div className="w-6 h-6 rounded-full bg-accent/10 flex items-center justify-center flex-shrink-0 mt-0.5">
57
+ <Sparkles size={12} className="text-accent" />
58
+ </div>
59
+ )}
60
+ <div className={cn(
61
+ 'max-w-[85%] rounded-lg px-3 py-2 text-xs font-sans',
62
+ isUser
63
+ ? 'bg-accent text-surface-0 rounded-br-sm'
64
+ : 'bg-surface-2 text-text-1 rounded-bl-sm',
65
+ )}>
66
+ <p className="whitespace-pre-wrap">{msg.content}</p>
67
+ </div>
68
+ </div>
69
+ );
70
+ }
71
+
72
+ function ConfigPreview({ config, onDeploy, onEdit, deploying }) {
73
+ const [expanded, setExpanded] = useState(false);
74
+
75
+ return (
76
+ <div className="rounded-lg border border-accent/30 bg-accent/5 p-4 space-y-3">
77
+ <div className="flex items-center gap-2">
78
+ <Bot size={16} className="text-accent" />
79
+ <span className="text-sm font-semibold text-text-0 font-sans">Ready to Deploy</span>
80
+ </div>
81
+
82
+ <div className="grid grid-cols-2 gap-2 text-xs font-sans">
83
+ <div>
84
+ <span className="text-text-4 text-2xs">Name</span>
85
+ <p className="text-text-0">{config.name}</p>
86
+ </div>
87
+ <div>
88
+ <span className="text-text-4 text-2xs">Cadence</span>
89
+ <p className="text-text-0">{CADENCE_PRESETS.find(p => p.value === config.cadence)?.label || config.cadence}</p>
90
+ </div>
91
+ </div>
92
+
93
+ {config.description && (
94
+ <p className="text-xs text-text-2 font-sans">{config.description}</p>
95
+ )}
96
+
97
+ {config.guardrails?.length > 0 && (
98
+ <div>
99
+ <span className="text-2xs text-text-4 font-sans flex items-center gap-1">
100
+ <Shield size={10} /> Guardrails
101
+ </span>
102
+ <ul className="text-2xs text-text-2 font-sans mt-1 space-y-0.5">
103
+ {config.guardrails.map((g, i) => <li key={i}>• {g}</li>)}
104
+ </ul>
105
+ </div>
106
+ )}
107
+
108
+ <button
109
+ onClick={() => setExpanded(!expanded)}
110
+ className="text-2xs text-accent font-sans flex items-center gap-1 cursor-pointer hover:underline"
111
+ >
112
+ {expanded ? <ChevronUp size={10} /> : <ChevronDown size={10} />}
113
+ {expanded ? 'Hide' : 'Show'} full config
114
+ </button>
115
+
116
+ {expanded && (
117
+ <pre className="text-2xs font-mono text-text-3 bg-surface-0 rounded p-2 overflow-x-auto max-h-48 overflow-y-auto border border-border-subtle">
118
+ {JSON.stringify(config, null, 2)}
119
+ </pre>
120
+ )}
121
+
122
+ <div className="flex gap-2 pt-1">
123
+ <Button variant="primary" size="sm" onClick={onDeploy} disabled={deploying} className="flex-1 gap-1.5">
124
+ {deploying ? 'Deploying...' : <><Check size={12} /> Deploy Agent</>}
125
+ </Button>
126
+ <Button variant="outline" size="sm" onClick={onEdit} className="gap-1.5">
127
+ <Edit3 size={12} /> Edit
128
+ </Button>
129
+ </div>
130
+ </div>
131
+ );
132
+ }
133
+
134
+ export function AutoAgentSetupWizard() {
135
+ const open = useGrooveStore((s) => s.autoAgentWizardOpen);
136
+ const closeWizard = useGrooveStore((s) => s.closeAutoAgentWizard);
137
+ const createAutoAgent = useGrooveStore((s) => s.createAutoAgent);
138
+ const setActiveView = useGrooveStore((s) => s.setActiveView);
139
+
140
+ const [messages, setMessages] = useState([]);
141
+ const [input, setInput] = useState('');
142
+ const [loading, setLoading] = useState(false);
143
+ const [config, setConfig] = useState(null);
144
+ const [deploying, setDeploying] = useState(false);
145
+ const scrollRef = useRef(null);
146
+ const inputRef = useRef(null);
147
+
148
+ useEffect(() => {
149
+ if (open && messages.length === 0) {
150
+ setMessages([{
151
+ role: 'assistant',
152
+ content: "Hey! I'll help you set up an autonomous agent. What task do you want it to handle? Tell me what you're trying to accomplish and I'll help configure everything.",
153
+ }]);
154
+ setConfig(null);
155
+ }
156
+ }, [open]);
157
+
158
+ useEffect(() => {
159
+ if (scrollRef.current) {
160
+ scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
161
+ }
162
+ }, [messages]);
163
+
164
+ const sendMessage = useCallback(async () => {
165
+ if (!input.trim() || loading) return;
166
+ const userMsg = { role: 'user', content: input.trim() };
167
+ const newMessages = [...messages, userMsg];
168
+ setMessages(newMessages);
169
+ setInput('');
170
+ setLoading(true);
171
+
172
+ try {
173
+ const chatHistory = newMessages.map(m => ({ role: m.role, content: m.content }));
174
+ const result = await api.post('/auto-agents/setup-chat', {
175
+ messages: chatHistory,
176
+ systemPrompt: SETUP_SYSTEM_PROMPT,
177
+ });
178
+
179
+ const assistantMsg = { role: 'assistant', content: result.response };
180
+ setMessages(prev => [...prev, assistantMsg]);
181
+
182
+ if (result.config) {
183
+ setConfig(result.config);
184
+ }
185
+ } catch (err) {
186
+ setMessages(prev => [...prev, {
187
+ role: 'assistant',
188
+ content: `Sorry, I hit an error: ${err.message}. Let me try again — could you rephrase that?`,
189
+ }]);
190
+ } finally {
191
+ setLoading(false);
192
+ }
193
+ }, [input, messages, loading]);
194
+
195
+ const handleDeploy = useCallback(async () => {
196
+ if (!config) return;
197
+ setDeploying(true);
198
+ try {
199
+ const result = await createAutoAgent(config);
200
+ if (result) {
201
+ setActiveView('auto-agents');
202
+ }
203
+ } finally {
204
+ setDeploying(false);
205
+ }
206
+ }, [config]);
207
+
208
+ const handleClose = () => {
209
+ closeWizard();
210
+ setMessages([]);
211
+ setConfig(null);
212
+ setInput('');
213
+ };
214
+
215
+ return (
216
+ <Sheet open={open} onOpenChange={(o) => { if (!o) handleClose(); }}>
217
+ <SheetContent title="New Auto Agent" width={520} onClose={handleClose}>
218
+ <div className="flex flex-col h-full">
219
+ {/* Chat area */}
220
+ <div ref={scrollRef} className="flex-1 overflow-y-auto px-4 py-4 space-y-3">
221
+ {messages.map((msg, i) => <ChatMessage key={i} msg={msg} />)}
222
+
223
+ {loading && (
224
+ <div className="flex gap-2">
225
+ <div className="w-6 h-6 rounded-full bg-accent/10 flex items-center justify-center flex-shrink-0">
226
+ <Sparkles size={12} className="text-accent animate-pulse" />
227
+ </div>
228
+ <div className="bg-surface-2 rounded-lg px-3 py-2 rounded-bl-sm">
229
+ <span className="text-xs text-text-3 font-sans animate-pulse">Thinking...</span>
230
+ </div>
231
+ </div>
232
+ )}
233
+
234
+ {config && (
235
+ <ConfigPreview
236
+ config={config}
237
+ onDeploy={handleDeploy}
238
+ onEdit={() => setConfig(null)}
239
+ deploying={deploying}
240
+ />
241
+ )}
242
+ </div>
243
+
244
+ {/* Input area */}
245
+ <div className="border-t border-border-subtle px-4 py-3">
246
+ <div className="flex items-center gap-2">
247
+ <input
248
+ ref={inputRef}
249
+ type="text"
250
+ value={input}
251
+ onChange={(e) => setInput(e.target.value)}
252
+ onKeyDown={(e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } }}
253
+ placeholder={config ? 'Ask to adjust the config...' : 'Describe what you want the agent to do...'}
254
+ className="flex-1 bg-surface-0 border border-border-subtle rounded-md px-3 py-2 text-xs text-text-0 font-sans placeholder:text-text-4 focus:outline-none focus:border-accent"
255
+ disabled={loading}
256
+ />
257
+ <Button
258
+ variant="primary"
259
+ size="icon-sm"
260
+ onClick={sendMessage}
261
+ disabled={!input.trim() || loading}
262
+ >
263
+ <Send size={14} />
264
+ </Button>
265
+ </div>
266
+ {!config && (
267
+ <div className="flex flex-wrap gap-1.5 mt-2">
268
+ {['ML training pipeline', 'Content generation', 'Data monitoring', 'Code review'].map(s => (
269
+ <button
270
+ key={s}
271
+ onClick={() => { setInput(s); inputRef.current?.focus(); }}
272
+ className="px-2 py-0.5 text-2xs font-sans text-text-3 bg-surface-2 rounded-full hover:bg-surface-3 cursor-pointer transition-colors"
273
+ >
274
+ {s}
275
+ </button>
276
+ ))}
277
+ </div>
278
+ )}
279
+ </div>
280
+ </div>
281
+ </SheetContent>
282
+ </Sheet>
283
+ );
284
+ }
@@ -1,5 +1,5 @@
1
1
  // FSL-1.1-Apache-2.0 — see LICENSE
2
- import { Network, Code2, ChartSpline, Puzzle, Users, Box, FlaskConical, Newspaper, Settings, Globe, MessageCircle, BookOpen, LayoutList } from 'lucide-react';
2
+ import { Network, Code2, ChartSpline, Puzzle, Users, Box, FlaskConical, Newspaper, Settings, Globe, MessageCircle, BookOpen, LayoutList, Bot } from 'lucide-react';
3
3
  import { cn } from '../../lib/cn';
4
4
  import { Tooltip } from '../ui/tooltip';
5
5
  import { useGrooveStore } from '../../stores/groove';
@@ -12,6 +12,7 @@ const BASE_NAV_ITEMS = [
12
12
  { id: 'editor', icon: Code2, label: 'Editor' },
13
13
  { id: 'dashboard', icon: ChartSpline, label: 'Dashboard' },
14
14
  { id: 'memory', icon: BookOpen, label: 'Memory' },
15
+ { id: 'auto-agents', icon: Bot, label: 'Auto Agents' },
15
16
  { id: 'teams', icon: Users, label: 'Teams' },
16
17
  { id: 'marketplace', icon: Puzzle, label: 'Marketplace' },
17
18
  { id: 'models', icon: Box, label: 'Models' },
@@ -14,6 +14,7 @@ import { createNetworkSlice } from './slices/network-slice.js';
14
14
  import { createPreviewSlice } from './slices/preview-slice.js';
15
15
  import { createMarketplaceSlice } from './slices/marketplace-slice.js';
16
16
  import { createAutomationsSlice } from './slices/automations-slice.js';
17
+ import { createAutoAgentsSlice } from './slices/auto-agents-slice.js';
17
18
 
18
19
  const WS_URL = `ws://${window.location.hostname}:${window.location.port || 31415}`;
19
20
 
@@ -39,6 +40,7 @@ export const useGrooveStore = create((set, get) => ({
39
40
  ...createPreviewSlice(set, get),
40
41
  ...createMarketplaceSlice(set, get),
41
42
  ...createAutomationsSlice(set, get),
43
+ ...createAutoAgentsSlice(set, get),
42
44
 
43
45
  // ── Connection ────────────────────────────────────────────
44
46
  connected: false,
@@ -654,6 +656,18 @@ export const useGrooveStore = create((set, get) => ({
654
656
  get().fetchAutomations();
655
657
  break;
656
658
 
659
+ case 'auto-agent:created':
660
+ case 'auto-agent:updated':
661
+ case 'auto-agent:deleted':
662
+ case 'auto-agent:paused':
663
+ case 'auto-agent:resumed':
664
+ case 'auto-agent:cycle-complete':
665
+ case 'auto-agent:auto-paused':
666
+ case 'auto-agent:state-updated':
667
+ case 'auto-agent:iteration-started':
668
+ get().fetchAutoAgents();
669
+ break;
670
+
657
671
  case 'gateway:status':
658
672
  set({ gateways: msg.data || [] });
659
673
  break;
@@ -0,0 +1,149 @@
1
+ // FSL-1.1-Apache-2.0 — see LICENSE
2
+
3
+ import { api } from '../../lib/api';
4
+
5
+ export const createAutoAgentsSlice = (set, get) => ({
6
+ autoAgents: [],
7
+ autoAgentWizardOpen: false,
8
+ autoAgentWizardStep: 'chat',
9
+ autoAgentSetupMessages: [],
10
+ autoAgentSetupConfig: null,
11
+ selectedAutoAgentId: null,
12
+
13
+ async fetchAutoAgents() {
14
+ try {
15
+ const data = await api.get('/auto-agents');
16
+ set({ autoAgents: Array.isArray(data) ? data : [] });
17
+ } catch { /* ignore */ }
18
+ },
19
+
20
+ async createAutoAgent(config) {
21
+ try {
22
+ const result = await api.post('/auto-agents', config);
23
+ get().addToast('success', 'Auto agent created');
24
+ get().fetchAutoAgents();
25
+ set({ autoAgentWizardOpen: false, autoAgentSetupMessages: [], autoAgentSetupConfig: null });
26
+ return result;
27
+ } catch (err) {
28
+ get().addToast('error', 'Failed to create auto agent', err.message);
29
+ }
30
+ },
31
+
32
+ async updateAutoAgent(id, updates) {
33
+ try {
34
+ await api.patch(`/auto-agents/${encodeURIComponent(id)}`, updates);
35
+ get().fetchAutoAgents();
36
+ } catch (err) {
37
+ get().addToast('error', 'Failed to update', err.message);
38
+ }
39
+ },
40
+
41
+ async deleteAutoAgent(id) {
42
+ try {
43
+ await api.delete(`/auto-agents/${encodeURIComponent(id)}`);
44
+ get().addToast('info', 'Auto agent deleted');
45
+ get().fetchAutoAgents();
46
+ if (get().selectedAutoAgentId === id) set({ selectedAutoAgentId: null });
47
+ } catch (err) {
48
+ get().addToast('error', 'Failed to delete', err.message);
49
+ }
50
+ },
51
+
52
+ async pauseAutoAgent(id) {
53
+ try {
54
+ await api.post(`/auto-agents/${encodeURIComponent(id)}/pause`);
55
+ get().fetchAutoAgents();
56
+ } catch (err) {
57
+ get().addToast('error', 'Failed to pause', err.message);
58
+ }
59
+ },
60
+
61
+ async resumeAutoAgent(id) {
62
+ try {
63
+ await api.post(`/auto-agents/${encodeURIComponent(id)}/resume`);
64
+ get().fetchAutoAgents();
65
+ } catch (err) {
66
+ get().addToast('error', 'Failed to resume', err.message);
67
+ }
68
+ },
69
+
70
+ async triggerAutoAgent(id) {
71
+ try {
72
+ const result = await api.post(`/auto-agents/${encodeURIComponent(id)}/trigger`);
73
+ get().addToast('success', 'Iteration triggered');
74
+ get().fetchAutoAgents();
75
+ return result;
76
+ } catch (err) {
77
+ get().addToast('error', 'Failed to trigger', err.message);
78
+ }
79
+ },
80
+
81
+ async fetchAutoAgentDetail(id) {
82
+ try {
83
+ return await api.get(`/auto-agents/${encodeURIComponent(id)}`);
84
+ } catch { return null; }
85
+ },
86
+
87
+ async fetchAutoAgentJournal(id) {
88
+ try {
89
+ return await api.get(`/auto-agents/${encodeURIComponent(id)}/journal?limit=100`);
90
+ } catch { return []; }
91
+ },
92
+
93
+ async fetchAutoAgentRuns(id) {
94
+ try {
95
+ return await api.get(`/auto-agents/${encodeURIComponent(id)}/runs?limit=30`);
96
+ } catch { return []; }
97
+ },
98
+
99
+ async fetchAutoAgentRoadmap(id) {
100
+ try {
101
+ const res = await fetch(`/api/auto-agents/${encodeURIComponent(id)}/roadmap`);
102
+ return await res.text();
103
+ } catch { return ''; }
104
+ },
105
+
106
+ async fetchAutoAgentPrompt(id) {
107
+ try {
108
+ const res = await fetch(`/api/auto-agents/${encodeURIComponent(id)}/prompt`);
109
+ return await res.text();
110
+ } catch { return ''; }
111
+ },
112
+
113
+ async updateAutoAgentPrompt(id, content) {
114
+ try {
115
+ await api.put(`/auto-agents/${encodeURIComponent(id)}/prompt`, { content });
116
+ get().addToast('success', 'Prompt updated');
117
+ } catch (err) {
118
+ get().addToast('error', 'Failed to update prompt', err.message);
119
+ }
120
+ },
121
+
122
+ async updateAutoAgentRoadmap(id, content) {
123
+ try {
124
+ await api.put(`/auto-agents/${encodeURIComponent(id)}/roadmap`, { content });
125
+ get().addToast('success', 'Roadmap updated');
126
+ } catch (err) {
127
+ get().addToast('error', 'Failed to update roadmap', err.message);
128
+ }
129
+ },
130
+
131
+ openAutoAgentWizard() {
132
+ set({
133
+ autoAgentWizardOpen: true,
134
+ autoAgentWizardStep: 'chat',
135
+ autoAgentSetupMessages: [],
136
+ autoAgentSetupConfig: null,
137
+ });
138
+ },
139
+ closeAutoAgentWizard() {
140
+ set({ autoAgentWizardOpen: false, autoAgentSetupMessages: [], autoAgentSetupConfig: null });
141
+ },
142
+ selectAutoAgent(id) { set({ selectedAutoAgentId: id }); },
143
+
144
+ addAutoAgentSetupMessage(msg) {
145
+ set((s) => ({ autoAgentSetupMessages: [...s.autoAgentSetupMessages, msg] }));
146
+ },
147
+ setAutoAgentSetupConfig(config) { set({ autoAgentSetupConfig: config }); },
148
+ setAutoAgentWizardStep(step) { set({ autoAgentWizardStep: step }); },
149
+ });
@@ -6,7 +6,7 @@ let toastCounter = 0;
6
6
 
7
7
  export const createUiSlice = (set, get) => ({
8
8
  // ── Navigation ────────────────────────────────────────────
9
- activeView: 'agents', // 'agents' | 'editor' | 'dashboard' | 'marketplace' | 'teams' | 'settings'
9
+ activeView: 'agents', // 'agents' | 'editor' | 'dashboard' | 'marketplace' | 'teams' | 'auto-agents' | 'settings'
10
10
  detailPanel: null, // null | { type: 'agent', agentId } | { type: 'spawn' } | { type: 'journalist' }
11
11
  teamDetailPanels: {}, // { [teamId]: detailPanel } — persists panel state per team
12
12
  commandPaletteOpen: false,
@@ -91,7 +91,19 @@ export const createUiSlice = (set, get) => ({
91
91
  localStorage.setItem('groove:detailWidth', String(w));
92
92
  },
93
93
  setTerminalVisible(v) {
94
- set({ terminalVisible: v });
94
+ const updates = { terminalVisible: v };
95
+ // In fleet view, opening the terminal auto-opens the agent panel so the
96
+ // terminal is constrained to the left column rather than spanning full width.
97
+ if (v && get().activeView === 'fleet') {
98
+ const selectedId = get().fleetSelectedAgents[0];
99
+ if (selectedId && !get().detailPanel) {
100
+ const tid = get().activeTeamId;
101
+ const panel = { type: 'agent', agentId: selectedId };
102
+ updates.detailPanel = panel;
103
+ updates.teamDetailPanels = { ...get().teamDetailPanels, [tid]: panel };
104
+ }
105
+ }
106
+ set(updates);
95
107
  localStorage.setItem('groove:terminalVisible', String(v));
96
108
  },
97
109
  setTerminalHeight(h) {
@@ -0,0 +1,70 @@
1
+ // FSL-1.1-Apache-2.0 — see LICENSE
2
+ import { useEffect } from 'react';
3
+ import { useGrooveStore } from '../stores/groove';
4
+ import { Button } from '../components/ui/button';
5
+ import { AutoAgentCard } from '../components/auto-agents/auto-agent-card';
6
+ import { AutoAgentDetail } from '../components/auto-agents/auto-agent-detail';
7
+ import { AutoAgentSetupWizard } from '../components/auto-agents/setup-wizard';
8
+ import { Plus, Bot } from 'lucide-react';
9
+
10
+ export default function AutoAgentsView() {
11
+ const autoAgents = useGrooveStore((s) => s.autoAgents);
12
+ const fetchAutoAgents = useGrooveStore((s) => s.fetchAutoAgents);
13
+ const openWizard = useGrooveStore((s) => s.openAutoAgentWizard);
14
+ const selectedId = useGrooveStore((s) => s.selectedAutoAgentId);
15
+ const selectAutoAgent = useGrooveStore((s) => s.selectAutoAgent);
16
+
17
+ useEffect(() => {
18
+ fetchAutoAgents();
19
+ const interval = setInterval(fetchAutoAgents, 10000);
20
+ return () => clearInterval(interval);
21
+ }, []);
22
+
23
+ // Detail view
24
+ if (selectedId) {
25
+ return (
26
+ <div className="flex flex-col h-full">
27
+ <AutoAgentDetail agentId={selectedId} onBack={() => selectAutoAgent(null)} />
28
+ <AutoAgentSetupWizard />
29
+ </div>
30
+ );
31
+ }
32
+
33
+ return (
34
+ <div className="flex flex-col h-full">
35
+ {/* Header */}
36
+ <div className="flex items-center justify-between px-5 py-4 border-b border-border-subtle bg-surface-1">
37
+ <h2 className="text-sm font-semibold text-text-0 font-sans uppercase tracking-wide">Auto Agents</h2>
38
+ <Button variant="primary" size="sm" onClick={openWizard} className="gap-1.5">
39
+ <Plus size={12} /> New Auto Agent
40
+ </Button>
41
+ </div>
42
+
43
+ {/* Content */}
44
+ <div className="flex-1 overflow-y-auto">
45
+ {autoAgents.length === 0 ? (
46
+ <div className="flex-1 flex items-center justify-center h-full">
47
+ <div className="text-center space-y-3 py-20">
48
+ <Bot size={28} className="mx-auto text-text-4" />
49
+ <p className="text-xs font-sans text-text-3">No auto agents yet</p>
50
+ <p className="text-2xs font-sans text-text-4 max-w-xs mx-auto">
51
+ Auto agents run autonomously on a schedule — iterating, learning, and making progress on complex tasks 24/7.
52
+ </p>
53
+ <Button variant="primary" size="sm" onClick={openWizard} className="gap-1.5 mt-2">
54
+ <Plus size={12} /> Create Auto Agent
55
+ </Button>
56
+ </div>
57
+ </div>
58
+ ) : (
59
+ <div className="p-4 space-y-3">
60
+ {autoAgents.map((a) => (
61
+ <AutoAgentCard key={a.id} agent={a} />
62
+ ))}
63
+ </div>
64
+ )}
65
+ </div>
66
+
67
+ <AutoAgentSetupWizard />
68
+ </div>
69
+ );
70
+ }
@@ -418,12 +418,19 @@ function ProviderCard({ provider, onKeyChange }) {
418
418
 
419
419
  {/* Connected state */}
420
420
  {provider.hasKey && !settingKey && (
421
- <div className="flex items-center gap-2 mb-3">
422
- <div className="flex-1 flex items-center gap-1.5 h-8 px-2.5 bg-success/8 border border-success/20 rounded-md text-2xs font-sans text-success">
423
- <Check size={10} /> API Connected
421
+ <div className="space-y-1.5 mb-3">
422
+ {isSubscription && isReady && (
423
+ <div className="flex items-center gap-1.5 h-8 px-2.5 bg-accent/8 border border-accent/20 rounded-md text-2xs font-sans text-accent">
424
+ <Check size={10} /> Subscription active
425
+ </div>
426
+ )}
427
+ <div className="flex items-center gap-2">
428
+ <div className="flex-1 flex items-center gap-1.5 h-8 px-2.5 bg-success/8 border border-success/20 rounded-md text-2xs font-sans text-success">
429
+ <Check size={10} /> {isSubscription ? 'API key stored (usage credits)' : 'API Connected'}
430
+ </div>
431
+ <button onClick={() => { setSettingKey(true); setShowKey(false); setKeyInput(''); }} className="text-2xs text-text-4 hover:text-accent cursor-pointer font-sans">Edit</button>
432
+ <button onClick={handleDeleteKey} className="text-2xs text-text-4 hover:text-danger cursor-pointer font-sans">Remove</button>
424
433
  </div>
425
- <button onClick={() => { setSettingKey(true); setShowKey(false); setKeyInput(''); }} className="text-2xs text-text-4 hover:text-accent cursor-pointer font-sans">Edit</button>
426
- <button onClick={handleDeleteKey} className="text-2xs text-text-4 hover:text-danger cursor-pointer font-sans">Remove</button>
427
434
  </div>
428
435
  )}
429
436
 
@@ -492,7 +499,7 @@ function ProviderCard({ provider, onKeyChange }) {
492
499
  className="w-full h-8 text-2xs gap-1.5 mt-2"
493
500
  >
494
501
  <Key size={11} />
495
- Add API key for headless mode
502
+ {provider.id === 'claude-code' ? 'Add API key (usage credits + headless)' : 'Add API key for headless mode'}
496
503
  </Button>
497
504
  )}
498
505
  </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "groove-dev",
3
- "version": "0.27.175",
3
+ "version": "0.27.179",
4
4
  "description": "Open-source agent orchestration layer — the AI company OS. Local model agent engine (GGUF/Ollama/llama-server), HuggingFace model browser, MCP integrations (Slack, Gmail, Stripe, 15+), agent scheduling (cron), business roles (CMO, CFO, EA). GUI dashboard, multi-agent coordination, zero cold-start, infinite sessions. Works with Claude Code, Codex, Gemini CLI, Ollama, any local model.",
5
5
  "license": "FSL-1.1-Apache-2.0",
6
6
  "author": "Groove Dev <hello@groovedev.ai> (https://groovedev.ai)",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groove-dev/cli",
3
- "version": "0.27.175",
3
+ "version": "0.27.179",
4
4
  "description": "GROOVE CLI — manage AI coding agents from your terminal",
5
5
  "license": "FSL-1.1-Apache-2.0",
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groove-dev/daemon",
3
- "version": "0.27.175",
3
+ "version": "0.27.179",
4
4
  "description": "GROOVE daemon — agent orchestration engine",
5
5
  "license": "FSL-1.1-Apache-2.0",
6
6
  "type": "module",
@@ -28,6 +28,7 @@ import { registerFileRoutes, resetEditorRoot } from './routes/files.js';
28
28
  import { registerNetworkRoutes } from './routes/network.js';
29
29
  import { registerScheduleRoutes } from './routes/schedules.js';
30
30
  import { registerInnerChatRoutes } from './routes/innerchat.js';
31
+ import { registerAutoAgentRoutes } from './routes/auto-agents.js';
31
32
 
32
33
  const __dirname = dirname(fileURLToPath(import.meta.url));
33
34
  const pkgVersion = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version;
@@ -127,7 +128,7 @@ export function createApi(app, daemon) {
127
128
  next();
128
129
  });
129
130
 
130
- app.use(express.json({ limit: '6mb' }));
131
+ app.use(express.json({ limit: '100mb' }));
131
132
 
132
133
  // Health check
133
134
  app.get('/api/health', (req, res) => {
@@ -177,6 +178,7 @@ export function createApi(app, daemon) {
177
178
  registerNetworkRoutes(app, daemon);
178
179
  registerScheduleRoutes(app, daemon);
179
180
  registerInnerChatRoutes(app, daemon);
181
+ registerAutoAgentRoutes(app, daemon);
180
182
 
181
183
 
182
184
  // Token usage