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.
- package/AUTONOMOUS_AGENT_FRAMEWORK.md +654 -0
- package/innerchat/Screenshot_2026-06-09_at_2.19.42_PM.png +0 -0
- package/node_modules/@groove-dev/cli/package.json +1 -1
- package/node_modules/@groove-dev/daemon/package.json +1 -1
- package/node_modules/@groove-dev/daemon/src/api.js +3 -1
- package/node_modules/@groove-dev/daemon/src/autostate.js +198 -0
- package/node_modules/@groove-dev/daemon/src/index.js +6 -0
- package/node_modules/@groove-dev/daemon/src/orchestrator.js +585 -0
- package/node_modules/@groove-dev/daemon/src/process.js +27 -2
- package/node_modules/@groove-dev/daemon/src/registry.js +2 -1
- package/node_modules/@groove-dev/daemon/src/routes/auto-agents.js +264 -0
- package/node_modules/@groove-dev/daemon/src/validate.js +6 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-CTer01Vg.js +1065 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-DTFtRtkx.css +1 -0
- package/node_modules/@groove-dev/gui/dist/index.html +2 -2
- package/node_modules/@groove-dev/gui/package.json +1 -1
- package/node_modules/@groove-dev/gui/src/App.jsx +2 -0
- package/node_modules/@groove-dev/gui/src/components/agents/agent-config.jsx +53 -0
- package/node_modules/@groove-dev/gui/src/components/agents/agent-panel.jsx +95 -2
- package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +50 -1
- package/node_modules/@groove-dev/gui/src/components/auto-agents/auto-agent-card.jsx +139 -0
- package/node_modules/@groove-dev/gui/src/components/auto-agents/auto-agent-detail.jsx +286 -0
- package/node_modules/@groove-dev/gui/src/components/auto-agents/setup-wizard.jsx +284 -0
- package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +2 -1
- package/node_modules/@groove-dev/gui/src/stores/groove.js +14 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/auto-agents-slice.js +149 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/ui-slice.js +14 -2
- package/node_modules/@groove-dev/gui/src/views/auto-agents.jsx +70 -0
- package/node_modules/@groove-dev/gui/src/views/settings.jsx +13 -6
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/daemon/package.json +1 -1
- package/packages/daemon/src/api.js +3 -1
- package/packages/daemon/src/autostate.js +198 -0
- package/packages/daemon/src/index.js +6 -0
- package/packages/daemon/src/orchestrator.js +585 -0
- package/packages/daemon/src/process.js +27 -2
- package/packages/daemon/src/registry.js +2 -1
- package/packages/daemon/src/routes/auto-agents.js +264 -0
- package/packages/daemon/src/validate.js +6 -0
- package/packages/gui/dist/assets/index-CTer01Vg.js +1065 -0
- package/packages/gui/dist/assets/index-DTFtRtkx.css +1 -0
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/App.jsx +2 -0
- package/packages/gui/src/components/agents/agent-config.jsx +53 -0
- package/packages/gui/src/components/agents/agent-panel.jsx +95 -2
- package/packages/gui/src/components/agents/spawn-wizard.jsx +50 -1
- package/packages/gui/src/components/auto-agents/auto-agent-card.jsx +139 -0
- package/packages/gui/src/components/auto-agents/auto-agent-detail.jsx +286 -0
- package/packages/gui/src/components/auto-agents/setup-wizard.jsx +284 -0
- package/packages/gui/src/components/layout/activity-bar.jsx +2 -1
- package/packages/gui/src/stores/groove.js +14 -0
- package/packages/gui/src/stores/slices/auto-agents-slice.js +149 -0
- package/packages/gui/src/stores/slices/ui-slice.js +14 -2
- package/packages/gui/src/views/auto-agents.jsx +70 -0
- package/packages/gui/src/views/settings.jsx +13 -6
- package/node_modules/@groove-dev/gui/dist/assets/index-BMaxZVeX.js +0 -1035
- package/node_modules/@groove-dev/gui/dist/assets/index-BvJwMNAX.css +0 -1
- package/packages/gui/dist/assets/index-BMaxZVeX.js +0 -1035
- 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
|
-
|
|
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="
|
|
422
|
-
|
|
423
|
-
<
|
|
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.
|
|
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)",
|
|
@@ -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: '
|
|
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
|