groove-dev 0.27.196 → 0.27.197
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/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 -5
- package/node_modules/@groove-dev/daemon/src/chatstore.js +104 -0
- package/node_modules/@groove-dev/daemon/src/index.js +3 -2
- package/node_modules/@groove-dev/daemon/src/process.js +1 -1
- package/node_modules/@groove-dev/daemon/src/routes/agents.js +1 -175
- package/node_modules/@groove-dev/daemon/src/routes/chat-history.js +34 -0
- package/node_modules/@groove-dev/daemon/test/chatstore.test.js +86 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-Da2nbd6M.js +1043 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-zmrIbwNm.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 +0 -2
- package/node_modules/@groove-dev/gui/src/components/agents/agent-config.jsx +2 -2
- package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +3 -3
- package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +2 -3
- package/node_modules/@groove-dev/gui/src/components/layout/breadcrumb-bar.jsx +1 -1
- package/node_modules/@groove-dev/gui/src/components/layout/command-palette.jsx +1 -1
- package/node_modules/@groove-dev/gui/src/stores/groove.js +5 -163
- package/node_modules/@groove-dev/gui/src/stores/slices/agents-slice.js +52 -2
- package/node_modules/@groove-dev/gui/src/views/marketplace.jsx +1 -1
- package/node_modules/@groove-dev/gui/src/views/settings.jsx +0 -61
- 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 -5
- package/packages/daemon/src/chatstore.js +104 -0
- package/packages/daemon/src/index.js +3 -2
- package/packages/daemon/src/process.js +1 -1
- package/packages/daemon/src/routes/agents.js +1 -175
- package/packages/daemon/src/routes/chat-history.js +34 -0
- package/packages/gui/dist/assets/index-Da2nbd6M.js +1043 -0
- package/packages/gui/dist/assets/index-zmrIbwNm.css +1 -0
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/App.jsx +0 -2
- package/packages/gui/src/components/agents/agent-config.jsx +2 -2
- package/packages/gui/src/components/agents/spawn-wizard.jsx +3 -3
- package/packages/gui/src/components/layout/activity-bar.jsx +2 -3
- package/packages/gui/src/components/layout/breadcrumb-bar.jsx +1 -1
- package/packages/gui/src/components/layout/command-palette.jsx +1 -1
- package/packages/gui/src/stores/groove.js +5 -163
- package/packages/gui/src/stores/slices/agents-slice.js +52 -2
- package/packages/gui/src/views/marketplace.jsx +1 -1
- package/packages/gui/src/views/settings.jsx +0 -61
- package/node_modules/@groove-dev/daemon/src/conversations.js +0 -576
- package/node_modules/@groove-dev/gui/dist/assets/index-Cat5pJUx.css +0 -1
- package/node_modules/@groove-dev/gui/dist/assets/index-L8xSnvHj.js +0 -1070
- package/node_modules/@groove-dev/gui/src/components/chat/chat-header.jsx +0 -198
- package/node_modules/@groove-dev/gui/src/components/chat/chat-input.jsx +0 -367
- package/node_modules/@groove-dev/gui/src/components/chat/chat-messages.jsx +0 -527
- package/node_modules/@groove-dev/gui/src/components/chat/chat-view.jsx +0 -212
- package/node_modules/@groove-dev/gui/src/components/chat/conversation-list.jsx +0 -163
- package/node_modules/@groove-dev/gui/src/components/chat/model-picker.jsx +0 -201
- package/node_modules/@groove-dev/gui/src/stores/slices/chat-slice.js +0 -227
- package/node_modules/@groove-dev/gui/src/views/chat.jsx +0 -6
- package/packages/daemon/src/conversations.js +0 -576
- package/packages/gui/dist/assets/index-Cat5pJUx.css +0 -1
- package/packages/gui/dist/assets/index-L8xSnvHj.js +0 -1070
- package/packages/gui/src/components/chat/chat-header.jsx +0 -198
- package/packages/gui/src/components/chat/chat-input.jsx +0 -367
- package/packages/gui/src/components/chat/chat-messages.jsx +0 -527
- package/packages/gui/src/components/chat/chat-view.jsx +0 -212
- package/packages/gui/src/components/chat/conversation-list.jsx +0 -163
- package/packages/gui/src/components/chat/model-picker.jsx +0 -201
- package/packages/gui/src/stores/slices/chat-slice.js +0 -227
- package/packages/gui/src/views/chat.jsx +0 -6
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { resolve } from 'path';
|
|
4
4
|
import { existsSync, readFileSync, readdirSync, statSync, writeFileSync, mkdirSync } from 'fs';
|
|
5
|
-
import { validateAgentConfig
|
|
5
|
+
import { validateAgentConfig } from '../validate.js';
|
|
6
6
|
import { ROLE_INTEGRATIONS, wrapWithRoleReminder } from '../process.js';
|
|
7
|
-
import { getProvider } from '../providers/index.js';
|
|
8
7
|
import { deliverInstruction } from '../deliver.js';
|
|
9
8
|
import { renameAgent } from '../rename.js';
|
|
10
9
|
|
|
@@ -218,179 +217,6 @@ export function registerAgentRoutes(app, daemon) {
|
|
|
218
217
|
res.json(suggestion);
|
|
219
218
|
});
|
|
220
219
|
|
|
221
|
-
// --- Conversations ---
|
|
222
|
-
|
|
223
|
-
app.get('/api/conversations', (req, res) => {
|
|
224
|
-
res.json({ conversations: daemon.conversations.list() });
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
app.post('/api/conversations', async (req, res) => {
|
|
228
|
-
try {
|
|
229
|
-
const { provider, model, title, mode, reasoning_effort, verbosity } = req.body;
|
|
230
|
-
if (provider && typeof provider !== 'string') {
|
|
231
|
-
return res.status(400).json({ error: 'provider must be a string' });
|
|
232
|
-
}
|
|
233
|
-
if (mode && mode !== 'api' && mode !== 'agent') {
|
|
234
|
-
return res.status(400).json({ error: 'mode must be "api" or "agent"' });
|
|
235
|
-
}
|
|
236
|
-
const validatedEffort = validateReasoningEffort(reasoning_effort);
|
|
237
|
-
const validatedVerbosity = validateVerbosity(verbosity);
|
|
238
|
-
const conversation = await daemon.conversations.create(provider, model, title, mode || 'api', {
|
|
239
|
-
reasoningEffort: validatedEffort,
|
|
240
|
-
verbosity: validatedVerbosity,
|
|
241
|
-
});
|
|
242
|
-
daemon.audit.log('conversation.create', { id: conversation.id, provider, model, mode: conversation.mode });
|
|
243
|
-
res.status(201).json(conversation);
|
|
244
|
-
} catch (err) {
|
|
245
|
-
res.status(400).json({ error: err.message });
|
|
246
|
-
}
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
app.get('/api/conversations/:id', (req, res) => {
|
|
250
|
-
const conversation = daemon.conversations.get(req.params.id);
|
|
251
|
-
if (!conversation) return res.status(404).json({ error: 'Conversation not found' });
|
|
252
|
-
res.json(conversation);
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
app.patch('/api/conversations/:id', async (req, res) => {
|
|
256
|
-
try {
|
|
257
|
-
const conv = daemon.conversations.get(req.params.id);
|
|
258
|
-
if (!conv) return res.status(404).json({ error: 'Conversation not found' });
|
|
259
|
-
if (req.body.title !== undefined) daemon.conversations.rename(req.params.id, req.body.title);
|
|
260
|
-
if (req.body.pinned !== undefined) daemon.conversations.pin(req.params.id, req.body.pinned);
|
|
261
|
-
if (req.body.archived !== undefined) daemon.conversations.archive(req.params.id, req.body.archived);
|
|
262
|
-
if (req.body.model !== undefined || req.body.provider !== undefined) {
|
|
263
|
-
const newProvider = req.body.provider || conv.provider;
|
|
264
|
-
const newModel = req.body.model || conv.model;
|
|
265
|
-
daemon.conversations.updateModel(req.params.id, newProvider, newModel);
|
|
266
|
-
}
|
|
267
|
-
if (req.body.mode !== undefined) {
|
|
268
|
-
if (req.body.mode !== 'api' && req.body.mode !== 'agent') {
|
|
269
|
-
return res.status(400).json({ error: 'mode must be "api" or "agent"' });
|
|
270
|
-
}
|
|
271
|
-
await daemon.conversations.setMode(req.params.id, req.body.mode);
|
|
272
|
-
}
|
|
273
|
-
if (req.body.reasoning_effort !== undefined || req.body.verbosity !== undefined) {
|
|
274
|
-
const validatedEffort = req.body.reasoning_effort !== undefined ? validateReasoningEffort(req.body.reasoning_effort) : undefined;
|
|
275
|
-
const validatedVerbosity = req.body.verbosity !== undefined ? validateVerbosity(req.body.verbosity) : undefined;
|
|
276
|
-
daemon.conversations.updateReasoningSettings(req.params.id, validatedEffort, validatedVerbosity);
|
|
277
|
-
}
|
|
278
|
-
daemon.audit.log('conversation.update', { id: req.params.id, provider: req.body.provider, model: req.body.model, mode: req.body.mode });
|
|
279
|
-
res.json(daemon.conversations.get(req.params.id));
|
|
280
|
-
} catch (err) {
|
|
281
|
-
res.status(400).json({ error: err.message });
|
|
282
|
-
}
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
app.delete('/api/conversations/:id', async (req, res) => {
|
|
286
|
-
try {
|
|
287
|
-
const conv = daemon.conversations.get(req.params.id);
|
|
288
|
-
if (!conv) return res.status(404).json({ error: 'Conversation not found' });
|
|
289
|
-
await daemon.conversations.delete(req.params.id);
|
|
290
|
-
daemon.audit.log('conversation.delete', { id: req.params.id });
|
|
291
|
-
res.json({ ok: true });
|
|
292
|
-
} catch (err) {
|
|
293
|
-
res.status(400).json({ error: err.message });
|
|
294
|
-
}
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
app.post('/api/conversations/:id/message', async (req, res) => {
|
|
298
|
-
try {
|
|
299
|
-
const { message, history, reasoning_effort, verbosity } = req.body;
|
|
300
|
-
if (!message || typeof message !== 'string' || !message.trim()) {
|
|
301
|
-
return res.status(400).json({ error: 'message is required' });
|
|
302
|
-
}
|
|
303
|
-
const validatedEffort = validateReasoningEffort(reasoning_effort);
|
|
304
|
-
const validatedVerbosity = validateVerbosity(verbosity);
|
|
305
|
-
|
|
306
|
-
const conv = daemon.conversations.get(req.params.id);
|
|
307
|
-
if (!conv) return res.status(404).json({ error: 'Conversation not found' });
|
|
308
|
-
|
|
309
|
-
daemon.conversations.autoTitle(req.params.id, message.trim());
|
|
310
|
-
daemon.conversations.touchUpdatedAt(req.params.id);
|
|
311
|
-
|
|
312
|
-
await daemon.conversations.sendMessage(req.params.id, message.trim(), history || [], {
|
|
313
|
-
reasoningEffort: validatedEffort,
|
|
314
|
-
verbosity: validatedVerbosity,
|
|
315
|
-
});
|
|
316
|
-
daemon.audit.log('conversation.message', { id: req.params.id, mode: conv.mode || 'api' });
|
|
317
|
-
res.json({ status: 'streaming', mode: conv.mode || 'api' });
|
|
318
|
-
} catch (err) {
|
|
319
|
-
res.status(400).json({ error: err.message });
|
|
320
|
-
}
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
app.post('/api/conversations/:id/stop', (req, res) => {
|
|
324
|
-
try {
|
|
325
|
-
const conv = daemon.conversations.get(req.params.id);
|
|
326
|
-
if (!conv) return res.status(404).json({ error: 'Conversation not found' });
|
|
327
|
-
daemon.conversations.stopStreaming(req.params.id);
|
|
328
|
-
res.json({ ok: true });
|
|
329
|
-
} catch (err) {
|
|
330
|
-
res.status(400).json({ error: err.message });
|
|
331
|
-
}
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
// --- Image Generation ---
|
|
335
|
-
|
|
336
|
-
app.post('/api/conversations/:id/generate-image', async (req, res) => {
|
|
337
|
-
try {
|
|
338
|
-
const { prompt, model, size, quality } = req.body;
|
|
339
|
-
if (!prompt || typeof prompt !== 'string' || !prompt.trim()) {
|
|
340
|
-
return res.status(400).json({ error: 'prompt is required' });
|
|
341
|
-
}
|
|
342
|
-
const conv = daemon.conversations.get(req.params.id);
|
|
343
|
-
if (!conv) return res.status(404).json({ error: 'Conversation not found' });
|
|
344
|
-
|
|
345
|
-
let providerName = conv.provider;
|
|
346
|
-
let provider = getProvider(providerName);
|
|
347
|
-
|
|
348
|
-
// If a specific image model was requested, find the right provider
|
|
349
|
-
if (model) {
|
|
350
|
-
const imageProviders = ['codex', 'grok', 'nano-banana'];
|
|
351
|
-
for (const pid of imageProviders) {
|
|
352
|
-
const p = getProvider(pid);
|
|
353
|
-
if (p?.constructor.models.some((m) => m.id === model)) {
|
|
354
|
-
provider = p;
|
|
355
|
-
providerName = pid;
|
|
356
|
-
break;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
if (!provider?.generateImage) {
|
|
362
|
-
return res.status(400).json({ error: 'Provider does not support image generation' });
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
const apiKey = daemon.conversations._getApiKey(providerName);
|
|
366
|
-
if (!apiKey) {
|
|
367
|
-
return res.status(400).json({ error: `No API key configured for ${providerName}` });
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
daemon.broadcast({
|
|
371
|
-
type: 'conversation:image-progress',
|
|
372
|
-
data: { conversationId: req.params.id, status: 'generating', prompt: prompt.trim() },
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
const result = await provider.generateImage(prompt.trim(), { model, size, quality, apiKey });
|
|
376
|
-
|
|
377
|
-
daemon.broadcast({
|
|
378
|
-
type: 'conversation:image',
|
|
379
|
-
data: { conversationId: req.params.id, ...result, prompt: prompt.trim() },
|
|
380
|
-
});
|
|
381
|
-
|
|
382
|
-
daemon.conversations.touchUpdatedAt(req.params.id);
|
|
383
|
-
daemon.audit.log('conversation.image', { id: req.params.id, model: result.model, provider: result.provider });
|
|
384
|
-
res.json(result);
|
|
385
|
-
} catch (err) {
|
|
386
|
-
daemon.broadcast({
|
|
387
|
-
type: 'conversation:image-progress',
|
|
388
|
-
data: { conversationId: req.params.id, status: 'error', error: err.message },
|
|
389
|
-
});
|
|
390
|
-
res.status(500).json({ error: err.message });
|
|
391
|
-
}
|
|
392
|
-
});
|
|
393
|
-
|
|
394
220
|
// Stop an agent's current work without killing the agent
|
|
395
221
|
app.post('/api/agents/:id/stop', async (req, res) => {
|
|
396
222
|
try {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
|
+
|
|
3
|
+
export function registerChatHistoryRoutes(app, daemon) {
|
|
4
|
+
// Full history for all agents — the GUI loads this on connect so chats are
|
|
5
|
+
// present regardless of which origin/port the tunnel came up on.
|
|
6
|
+
app.get('/api/chat-history', (req, res) => {
|
|
7
|
+
res.json({ history: daemon.chatStore.getAll() });
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
// Append a single message for an agent.
|
|
11
|
+
app.post('/api/chat-history/:agentId', (req, res) => {
|
|
12
|
+
const { message } = req.body || {};
|
|
13
|
+
if (!message || typeof message !== 'object') {
|
|
14
|
+
return res.status(400).json({ error: 'message object required' });
|
|
15
|
+
}
|
|
16
|
+
daemon.chatStore.append(req.params.agentId, message);
|
|
17
|
+
res.json({ ok: true });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// Replace an agent's whole history (batch sync).
|
|
21
|
+
app.put('/api/chat-history/:agentId', (req, res) => {
|
|
22
|
+
const { messages } = req.body || {};
|
|
23
|
+
if (!Array.isArray(messages)) {
|
|
24
|
+
return res.status(400).json({ error: 'messages array required' });
|
|
25
|
+
}
|
|
26
|
+
daemon.chatStore.replace(req.params.agentId, messages);
|
|
27
|
+
res.json({ ok: true });
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
app.delete('/api/chat-history/:agentId', (req, res) => {
|
|
31
|
+
daemon.chatStore.remove(req.params.agentId);
|
|
32
|
+
res.json({ ok: true });
|
|
33
|
+
});
|
|
34
|
+
}
|