groove-dev 0.25.18 → 0.25.20
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/daemon/src/api.js +3 -2
- package/node_modules/@groove-dev/daemon/src/journalist.js +32 -2
- package/node_modules/@groove-dev/daemon/src/process.js +10 -4
- package/node_modules/@groove-dev/daemon/src/providers/ollama.js +7 -3
- package/node_modules/@groove-dev/gui/dist/assets/{index-Ca4wKXQ9.js → index-H_e3KvZp.js} +124 -124
- package/node_modules/@groove-dev/gui/dist/index.html +1 -1
- package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +9 -12
- package/package.json +1 -1
- package/packages/daemon/src/api.js +3 -2
- package/packages/daemon/src/journalist.js +32 -2
- package/packages/daemon/src/process.js +10 -4
- package/packages/daemon/src/providers/ollama.js +7 -3
- package/packages/gui/dist/assets/{index-Ca4wKXQ9.js → index-H_e3KvZp.js} +124 -124
- package/packages/gui/dist/index.html +1 -1
- package/packages/gui/src/components/agents/spawn-wizard.jsx +9 -12
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<link rel="icon" type="image/png" href="/favicon.png" />
|
|
7
7
|
<title>Groove GUI</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-H_e3KvZp.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/vendor-C0HXlhrU.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/reactflow-BQPfi37R.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/codemirror-BBL3i_JW.js">
|
|
@@ -3,7 +3,7 @@ import { useState, useEffect } from 'react';
|
|
|
3
3
|
import { useGrooveStore } from '../../stores/groove';
|
|
4
4
|
import { Sheet, SheetContent } from '../ui/sheet';
|
|
5
5
|
import { Button } from '../ui/button';
|
|
6
|
-
import { Input
|
|
6
|
+
import { Input } from '../ui/input';
|
|
7
7
|
import { Badge } from '../ui/badge';
|
|
8
8
|
import { cn } from '../../lib/cn';
|
|
9
9
|
import { roleColor } from '../../lib/status';
|
|
@@ -190,8 +190,10 @@ export function SpawnWizard() {
|
|
|
190
190
|
className="w-full h-8 px-3 pr-8 text-sm rounded-md bg-surface-1 border border-border text-text-0 font-sans appearance-none cursor-pointer focus:outline-none focus:ring-1 focus:ring-accent"
|
|
191
191
|
>
|
|
192
192
|
<option value="">Auto</option>
|
|
193
|
-
{
|
|
194
|
-
<option key={p.id} value={p.id}
|
|
193
|
+
{providers.map((p) => (
|
|
194
|
+
<option key={p.id} value={p.id} disabled={!p.installed}>
|
|
195
|
+
{p.name}{!p.installed ? ' (not installed)' : ''}
|
|
196
|
+
</option>
|
|
195
197
|
))}
|
|
196
198
|
</select>
|
|
197
199
|
<ChevronDown size={14} className="absolute right-2 top-1/2 -translate-y-1/2 text-text-3 pointer-events-none" />
|
|
@@ -220,10 +222,12 @@ export function SpawnWizard() {
|
|
|
220
222
|
{/* Provider status hints */}
|
|
221
223
|
{provider && selectedProvider && (
|
|
222
224
|
<div className="text-2xs text-text-3 font-sans flex items-center gap-2">
|
|
223
|
-
{selectedProvider.
|
|
224
|
-
<Badge variant="success">
|
|
225
|
+
{selectedProvider.authType === 'local' ? (
|
|
226
|
+
<Badge variant="success">Local</Badge>
|
|
225
227
|
) : selectedProvider.authType === 'subscription' ? (
|
|
226
228
|
<Badge variant="accent">Subscription</Badge>
|
|
229
|
+
) : selectedProvider.hasKey ? (
|
|
230
|
+
<Badge variant="success">API key set</Badge>
|
|
227
231
|
) : (
|
|
228
232
|
<Badge variant="warning">No API key — set with: groove set-key {provider} YOUR_KEY</Badge>
|
|
229
233
|
)}
|
|
@@ -324,13 +328,6 @@ export function SpawnWizard() {
|
|
|
324
328
|
</DialogContent>
|
|
325
329
|
</Dialog>
|
|
326
330
|
|
|
327
|
-
<Textarea
|
|
328
|
-
label="Prompt (optional)"
|
|
329
|
-
value={prompt}
|
|
330
|
-
onChange={(e) => setPrompt(e.target.value)}
|
|
331
|
-
placeholder="What should this agent work on?"
|
|
332
|
-
rows={3}
|
|
333
|
-
/>
|
|
334
331
|
</div>
|
|
335
332
|
)}
|
|
336
333
|
</div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "groove-dev",
|
|
3
|
-
"version": "0.25.
|
|
3
|
+
"version": "0.25.20",
|
|
4
4
|
"description": "Open-source agent orchestration layer — the AI company OS. 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.",
|
|
5
5
|
"license": "FSL-1.1-Apache-2.0",
|
|
6
6
|
"author": "Groove Dev <hello@groovedev.ai> (https://groovedev.ai)",
|
|
@@ -403,11 +403,12 @@ export function createApi(app, daemon) {
|
|
|
403
403
|
const activity = daemon.classifier?.agentWindows?.[agent.id] || [];
|
|
404
404
|
const recentActivity = activity.slice(-20).map((e) => e.data || e.text || '').join('\n');
|
|
405
405
|
|
|
406
|
+
// Truncate the agent's original prompt to avoid massive payloads
|
|
407
|
+
const taskSummary = agent.prompt ? agent.prompt.slice(0, 500) : '';
|
|
406
408
|
const prompt = [
|
|
407
409
|
`You are answering a question about agent "${agent.name}" (role: ${agent.role}).`,
|
|
408
|
-
`This agent's file scope: ${(agent.scope || []).join(', ') || 'unrestricted'}`,
|
|
409
410
|
`Provider: ${agent.provider}, Tokens used: ${agent.tokensUsed || 0}`,
|
|
410
|
-
|
|
411
|
+
taskSummary ? `Task summary: ${taskSummary}` : '',
|
|
411
412
|
recentActivity ? `\nRecent activity:\n${recentActivity}` : '',
|
|
412
413
|
`\nUser question: ${message.trim()}`,
|
|
413
414
|
'\nAnswer concisely based on the agent context above.',
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import { readFileSync, writeFileSync, existsSync, mkdirSync, statSync } from 'fs';
|
|
5
5
|
import { resolve } from 'path';
|
|
6
|
-
import { execFile } from 'child_process';
|
|
6
|
+
import { execFile, spawn as cpSpawn } from 'child_process';
|
|
7
7
|
import { getProvider, getInstalledProviders } from './providers/index.js';
|
|
8
8
|
|
|
9
9
|
const DEFAULT_INTERVAL = 120_000; // 2 minutes
|
|
@@ -325,9 +325,39 @@ export class Journalist {
|
|
|
325
325
|
|| provider.constructor.models?.[0];
|
|
326
326
|
const modelId = lightModel?.id || null;
|
|
327
327
|
|
|
328
|
-
const
|
|
328
|
+
const headlessCmd = provider.buildHeadlessCommand(prompt, modelId);
|
|
329
|
+
const { command, args, env, stdin: stdinData } = headlessCmd;
|
|
329
330
|
|
|
330
331
|
return new Promise((resolve, reject) => {
|
|
332
|
+
// Use spawn with stdin pipe if provider needs it (e.g., Ollama)
|
|
333
|
+
if (stdinData) {
|
|
334
|
+
let stdout = '';
|
|
335
|
+
const proc = cpSpawn(command, args, {
|
|
336
|
+
env: { ...process.env, ...env },
|
|
337
|
+
cwd: this.daemon.projectDir,
|
|
338
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
339
|
+
});
|
|
340
|
+
proc.stdin.write(stdinData);
|
|
341
|
+
proc.stdin.end();
|
|
342
|
+
proc.stdout.on('data', (d) => { stdout += d.toString(); });
|
|
343
|
+
const timer = setTimeout(() => { proc.kill(); reject(new Error('Headless timeout')); }, 60_000);
|
|
344
|
+
proc.on('exit', (code) => {
|
|
345
|
+
clearTimeout(timer);
|
|
346
|
+
if (code !== 0) return reject(new Error(`Headless exited with code ${code}`));
|
|
347
|
+
// Process stdout same as execFile path below
|
|
348
|
+
const lines = stdout.split('\n');
|
|
349
|
+
for (const line of lines) {
|
|
350
|
+
try {
|
|
351
|
+
const json = JSON.parse(line);
|
|
352
|
+
if (json.result) return resolve(json.result);
|
|
353
|
+
if (json.content?.[0]?.text) return resolve(json.content[0].text);
|
|
354
|
+
} catch { /* not json */ }
|
|
355
|
+
}
|
|
356
|
+
resolve(stdout.trim());
|
|
357
|
+
});
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
|
|
331
361
|
const proc = execFile(command, args, {
|
|
332
362
|
env: { ...process.env, ...env },
|
|
333
363
|
cwd: this.daemon.projectDir,
|
|
@@ -324,7 +324,8 @@ For normal file edits within your scope, proceed without review.
|
|
|
324
324
|
integrationEnv = this.daemon.integrations.getSpawnEnv(config.integrations);
|
|
325
325
|
}
|
|
326
326
|
|
|
327
|
-
const
|
|
327
|
+
const spawnCmd = provider.buildSpawnCommand(spawnConfig);
|
|
328
|
+
const { command, args, env, stdin: stdinData } = spawnCmd;
|
|
328
329
|
|
|
329
330
|
// Set up log capture
|
|
330
331
|
const logDir = resolve(this.daemon.grooveDir, 'logs');
|
|
@@ -347,15 +348,20 @@ For normal file edits within your scope, proceed without review.
|
|
|
347
348
|
}
|
|
348
349
|
}
|
|
349
350
|
|
|
350
|
-
// Spawn the process
|
|
351
|
+
// Spawn the process (use pipe for stdin if provider needs to send prompt via stdin)
|
|
351
352
|
const proc = cpSpawn(command, args, {
|
|
352
353
|
cwd: agent.workingDir || this.daemon.projectDir,
|
|
353
354
|
env: { ...process.env, ...env, ...integrationEnv, GROOVE_AGENT_ID: agent.id, GROOVE_AGENT_NAME: agent.name },
|
|
354
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
355
|
-
// Don't let agent process prevent daemon from exiting
|
|
355
|
+
stdio: [stdinData ? 'pipe' : 'ignore', 'pipe', 'pipe'],
|
|
356
356
|
detached: false,
|
|
357
357
|
});
|
|
358
358
|
|
|
359
|
+
// Write prompt via stdin if provider requested it (e.g., Ollama avoids arg length limits)
|
|
360
|
+
if (stdinData && proc.stdin) {
|
|
361
|
+
proc.stdin.write(stdinData);
|
|
362
|
+
proc.stdin.end();
|
|
363
|
+
}
|
|
364
|
+
|
|
359
365
|
if (!proc.pid) {
|
|
360
366
|
registry.remove(agent.id);
|
|
361
367
|
locks.release(agent.id);
|
|
@@ -226,13 +226,17 @@ export class OllamaProvider extends Provider {
|
|
|
226
226
|
buildSpawnCommand(agent) {
|
|
227
227
|
const model = agent.model || 'qwen2.5-coder:7b';
|
|
228
228
|
const args = ['run', model];
|
|
229
|
-
|
|
230
|
-
return {
|
|
229
|
+
// Pass prompt via stdin to avoid OS arg length limits on long prompts
|
|
230
|
+
return {
|
|
231
|
+
command: 'ollama', args,
|
|
232
|
+
env: { OLLAMA_API_BASE: 'http://localhost:11434' },
|
|
233
|
+
stdin: agent.prompt || undefined,
|
|
234
|
+
};
|
|
231
235
|
}
|
|
232
236
|
|
|
233
237
|
buildHeadlessCommand(prompt, model) {
|
|
234
238
|
const m = model || 'qwen2.5-coder:7b';
|
|
235
|
-
return { command: 'ollama', args: ['run', m
|
|
239
|
+
return { command: 'ollama', args: ['run', m], env: {}, stdin: prompt };
|
|
236
240
|
}
|
|
237
241
|
|
|
238
242
|
switchModel(agent, newModel) {
|