groove-dev 0.26.38 → 0.27.0
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/CHANGELOG.md +59 -0
- package/CLAUDE.md +24 -19
- package/node_modules/@groove-dev/cli/bin/groove.js +2 -0
- package/node_modules/@groove-dev/cli/package.json +1 -1
- package/node_modules/@groove-dev/cli/src/commands/nuke.js +16 -4
- package/node_modules/@groove-dev/cli/src/commands/stop.js +17 -2
- package/node_modules/@groove-dev/daemon/integrations-registry.json +681 -75
- package/node_modules/@groove-dev/daemon/package.json +1 -1
- package/node_modules/@groove-dev/daemon/src/adaptive.js +23 -25
- package/node_modules/@groove-dev/daemon/src/api.js +346 -22
- package/node_modules/@groove-dev/daemon/src/classifier.js +53 -6
- package/node_modules/@groove-dev/daemon/src/firstrun.js +14 -1
- package/node_modules/@groove-dev/daemon/src/gateways/manager.js +2 -2
- package/node_modules/@groove-dev/daemon/src/index.js +28 -4
- package/node_modules/@groove-dev/daemon/src/integrations.js +215 -14
- package/node_modules/@groove-dev/daemon/src/introducer.js +84 -11
- package/node_modules/@groove-dev/daemon/src/journalist.js +43 -1
- package/node_modules/@groove-dev/daemon/src/lockmanager.js +60 -0
- package/node_modules/@groove-dev/daemon/src/mcp-manager.js +270 -0
- package/node_modules/@groove-dev/daemon/src/memory.js +370 -0
- package/node_modules/@groove-dev/daemon/src/pm.js +1 -1
- package/node_modules/@groove-dev/daemon/src/process.js +141 -9
- package/node_modules/@groove-dev/daemon/src/registry.js +1 -1
- package/node_modules/@groove-dev/daemon/src/rotator.js +334 -31
- package/node_modules/@groove-dev/daemon/src/router.js +43 -0
- package/node_modules/@groove-dev/daemon/src/tokentracker.js +70 -18
- package/node_modules/@groove-dev/daemon/src/validate.js +5 -13
- package/node_modules/@groove-dev/daemon/templates/groove-slides.cjs +306 -0
- package/node_modules/@groove-dev/daemon/test/classifier.test.js +3 -5
- package/node_modules/@groove-dev/daemon/test/lockmanager.test.js +64 -0
- package/node_modules/@groove-dev/daemon/test/memory.test.js +252 -0
- package/node_modules/@groove-dev/daemon/test/rotator.test.js +108 -0
- package/node_modules/@groove-dev/daemon/test/router.test.js +64 -0
- package/node_modules/@groove-dev/daemon/test/slides-engine.test.js +230 -0
- package/node_modules/@groove-dev/daemon/test/tokentracker.test.js +78 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-DjORRpF0.css +1 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-eCrVowF0.js +652 -0
- package/node_modules/@groove-dev/gui/dist/index.html +2 -2
- package/node_modules/@groove-dev/gui/package.json +1 -4
- package/node_modules/@groove-dev/gui/src/components/agents/agent-chat.jsx +26 -17
- package/node_modules/@groove-dev/gui/src/components/agents/agent-config.jsx +22 -1
- package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +53 -21
- package/node_modules/@groove-dev/gui/src/components/agents/agent-node.jsx +132 -90
- package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +212 -1
- package/node_modules/@groove-dev/gui/src/components/dashboard/cache-ring.jsx +6 -2
- package/node_modules/@groove-dev/gui/src/components/dashboard/intel-panel.jsx +495 -174
- package/node_modules/@groove-dev/gui/src/components/dashboard/kpi-card.jsx +12 -2
- package/node_modules/@groove-dev/gui/src/components/dashboard/team-burn-panel.jsx +55 -0
- package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +3 -3
- package/node_modules/@groove-dev/gui/src/components/layout/app-shell.jsx +24 -19
- package/node_modules/@groove-dev/gui/src/components/layout/command-palette.jsx +2 -2
- package/node_modules/@groove-dev/gui/src/components/marketplace/integration-wizard.jsx +391 -61
- package/node_modules/@groove-dev/gui/src/components/marketplace/marketplace-card.jsx +29 -7
- package/node_modules/@groove-dev/gui/src/lib/format.js +0 -6
- package/node_modules/@groove-dev/gui/src/lib/hooks/use-dashboard.js +23 -5
- package/node_modules/@groove-dev/gui/src/stores/groove.js +59 -9
- package/node_modules/@groove-dev/gui/src/views/agents.jsx +84 -10
- package/node_modules/@groove-dev/gui/src/views/dashboard.jsx +24 -21
- package/node_modules/@groove-dev/gui/src/views/marketplace.jsx +153 -85
- package/package.json +2 -8
- package/packages/cli/bin/groove.js +2 -0
- package/packages/cli/package.json +1 -1
- package/packages/cli/src/commands/nuke.js +16 -4
- package/packages/cli/src/commands/stop.js +17 -2
- package/packages/daemon/integrations-registry.json +681 -75
- package/packages/daemon/package.json +1 -1
- package/packages/daemon/src/adaptive.js +23 -25
- package/packages/daemon/src/api.js +346 -22
- package/packages/daemon/src/classifier.js +53 -6
- package/packages/daemon/src/firstrun.js +14 -1
- package/packages/daemon/src/gateways/manager.js +2 -2
- package/packages/daemon/src/index.js +28 -4
- package/packages/daemon/src/integrations.js +215 -14
- package/packages/daemon/src/introducer.js +84 -11
- package/packages/daemon/src/journalist.js +43 -1
- package/packages/daemon/src/lockmanager.js +60 -0
- package/packages/daemon/src/mcp-manager.js +270 -0
- package/packages/daemon/src/memory.js +370 -0
- package/packages/daemon/src/pm.js +1 -1
- package/packages/daemon/src/process.js +141 -9
- package/packages/daemon/src/registry.js +1 -1
- package/packages/daemon/src/rotator.js +334 -31
- package/packages/daemon/src/router.js +43 -0
- package/packages/daemon/src/tokentracker.js +70 -18
- package/packages/daemon/src/validate.js +5 -13
- package/packages/daemon/templates/groove-slides.cjs +306 -0
- package/packages/gui/dist/assets/index-DjORRpF0.css +1 -0
- package/packages/gui/dist/assets/index-eCrVowF0.js +652 -0
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/package.json +1 -4
- package/packages/gui/src/components/agents/agent-chat.jsx +26 -17
- package/packages/gui/src/components/agents/agent-config.jsx +22 -1
- package/packages/gui/src/components/agents/agent-feed.jsx +53 -21
- package/packages/gui/src/components/agents/agent-node.jsx +132 -90
- package/packages/gui/src/components/agents/spawn-wizard.jsx +212 -1
- package/packages/gui/src/components/dashboard/cache-ring.jsx +6 -2
- package/packages/gui/src/components/dashboard/intel-panel.jsx +495 -174
- package/packages/gui/src/components/dashboard/kpi-card.jsx +12 -2
- package/packages/gui/src/components/dashboard/team-burn-panel.jsx +55 -0
- package/packages/gui/src/components/layout/activity-bar.jsx +3 -3
- package/packages/gui/src/components/layout/app-shell.jsx +24 -19
- package/packages/gui/src/components/layout/command-palette.jsx +2 -2
- package/packages/gui/src/components/marketplace/integration-wizard.jsx +391 -61
- package/packages/gui/src/components/marketplace/marketplace-card.jsx +29 -7
- package/packages/gui/src/lib/format.js +0 -6
- package/packages/gui/src/lib/hooks/use-dashboard.js +23 -5
- package/packages/gui/src/stores/groove.js +59 -9
- package/packages/gui/src/views/agents.jsx +84 -10
- package/packages/gui/src/views/dashboard.jsx +24 -21
- package/packages/gui/src/views/marketplace.jsx +153 -85
- package/node_modules/@groove-dev/gui/dist/assets/index-CEFKgLGB.css +0 -1
- package/node_modules/@groove-dev/gui/dist/assets/index-CaKBNWcK.js +0 -638
- package/node_modules/@groove-dev/gui/dist/groove-logo-short.png +0 -0
- package/node_modules/@groove-dev/gui/dist/groove-logo.png +0 -0
- package/node_modules/@groove-dev/gui/public/groove-logo-short.png +0 -0
- package/node_modules/@groove-dev/gui/public/groove-logo.png +0 -0
- package/node_modules/@groove-dev/gui/src/components/ui/dropdown-menu.jsx +0 -60
- package/node_modules/@groove-dev/gui/src/lib/hooks/use-media-query.js +0 -18
- package/node_modules/@radix-ui/react-dropdown-menu/LICENSE +0 -21
- package/node_modules/@radix-ui/react-dropdown-menu/README.md +0 -3
- package/node_modules/@radix-ui/react-dropdown-menu/dist/index.d.mts +0 -97
- package/node_modules/@radix-ui/react-dropdown-menu/dist/index.d.ts +0 -97
- package/node_modules/@radix-ui/react-dropdown-menu/dist/index.js +0 -337
- package/node_modules/@radix-ui/react-dropdown-menu/dist/index.js.map +0 -7
- package/node_modules/@radix-ui/react-dropdown-menu/dist/index.mjs +0 -305
- package/node_modules/@radix-ui/react-dropdown-menu/dist/index.mjs.map +0 -7
- package/node_modules/@radix-ui/react-dropdown-menu/package.json +0 -75
- package/node_modules/@radix-ui/react-popover/LICENSE +0 -21
- package/node_modules/@radix-ui/react-popover/README.md +0 -3
- package/node_modules/@radix-ui/react-popover/dist/index.d.mts +0 -85
- package/node_modules/@radix-ui/react-popover/dist/index.d.ts +0 -85
- package/node_modules/@radix-ui/react-popover/dist/index.js +0 -352
- package/node_modules/@radix-ui/react-popover/dist/index.js.map +0 -7
- package/node_modules/@radix-ui/react-popover/dist/index.mjs +0 -320
- package/node_modules/@radix-ui/react-popover/dist/index.mjs.map +0 -7
- package/node_modules/@radix-ui/react-popover/package.json +0 -82
- package/node_modules/@radix-ui/react-separator/LICENSE +0 -21
- package/node_modules/@radix-ui/react-separator/README.md +0 -3
- package/node_modules/@radix-ui/react-separator/dist/index.d.mts +0 -21
- package/node_modules/@radix-ui/react-separator/dist/index.d.ts +0 -21
- package/node_modules/@radix-ui/react-separator/dist/index.js +0 -65
- package/node_modules/@radix-ui/react-separator/dist/index.js.map +0 -7
- package/node_modules/@radix-ui/react-separator/dist/index.mjs +0 -32
- package/node_modules/@radix-ui/react-separator/dist/index.mjs.map +0 -7
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/LICENSE +0 -21
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/README.md +0 -3
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/dist/index.d.mts +0 -52
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/dist/index.d.ts +0 -52
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/dist/index.js +0 -80
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/dist/index.js.map +0 -7
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/dist/index.mjs +0 -47
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/dist/index.mjs.map +0 -7
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/package.json +0 -69
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot/LICENSE +0 -21
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot/README.md +0 -3
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot/dist/index.d.mts +0 -22
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot/dist/index.d.ts +0 -22
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot/dist/index.js +0 -152
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot/dist/index.js.map +0 -7
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot/dist/index.mjs +0 -119
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot/dist/index.mjs.map +0 -7
- package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot/package.json +0 -64
- package/node_modules/@radix-ui/react-separator/package.json +0 -69
- package/packages/gui/dist/assets/index-CEFKgLGB.css +0 -1
- package/packages/gui/dist/assets/index-CaKBNWcK.js +0 -638
- package/packages/gui/dist/groove-logo-short.png +0 -0
- package/packages/gui/dist/groove-logo.png +0 -0
- package/packages/gui/public/groove-logo-short.png +0 -0
- package/packages/gui/public/groove-logo.png +0 -0
- package/packages/gui/src/components/ui/dropdown-menu.jsx +0 -60
- package/packages/gui/src/lib/hooks/use-media-query.js +0 -18
|
@@ -5,12 +5,12 @@
|
|
|
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-eCrVowF0.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">
|
|
12
12
|
<link rel="modulepreload" crossorigin href="/assets/xterm--7_ns2zW.js">
|
|
13
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
13
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DjORRpF0.css">
|
|
14
14
|
</head>
|
|
15
15
|
<body>
|
|
16
16
|
<div id="root"></div>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@groove-dev/gui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"description": "GROOVE GUI — visual agent control plane",
|
|
5
5
|
"license": "FSL-1.1-Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -27,11 +27,8 @@
|
|
|
27
27
|
"@fontsource-variable/jetbrains-mono": "^5.2.8",
|
|
28
28
|
"@radix-ui/react-context-menu": "^2.2.16",
|
|
29
29
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
30
|
-
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
31
|
-
"@radix-ui/react-popover": "^1.1.15",
|
|
32
30
|
"@radix-ui/react-scroll-area": "^1.2.10",
|
|
33
31
|
"@radix-ui/react-select": "^2.2.6",
|
|
34
|
-
"@radix-ui/react-separator": "^1.1.8",
|
|
35
32
|
"@radix-ui/react-tabs": "^1.1.13",
|
|
36
33
|
"@radix-ui/react-tooltip": "^1.2.8",
|
|
37
34
|
"@tailwindcss/vite": "^4.2.2",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
2
|
import { useState, useRef, useEffect } from 'react';
|
|
3
|
-
import { Send, Loader2, MessageSquare, HelpCircle, ArrowRight, Paperclip } from 'lucide-react';
|
|
3
|
+
import { Send, Loader2, MessageSquare, HelpCircle, ArrowRight, Paperclip, Square } from 'lucide-react';
|
|
4
4
|
import { useGrooveStore } from '../../stores/groove';
|
|
5
5
|
import { cn } from '../../lib/cn';
|
|
6
6
|
import { Avatar } from '../ui/avatar';
|
|
@@ -236,28 +236,37 @@ export function AgentChat({ agent }) {
|
|
|
236
236
|
placeholder={isAlive ? 'Instruct agent... (? to query)' : 'Continue conversation...'}
|
|
237
237
|
rows={1}
|
|
238
238
|
className={cn(
|
|
239
|
-
'flex-1 resize-
|
|
239
|
+
'flex-1 resize-y rounded-xl px-4 py-2.5 text-sm',
|
|
240
240
|
'bg-surface-0 border text-text-0 font-sans',
|
|
241
241
|
'placeholder:text-text-4',
|
|
242
242
|
'focus:outline-none focus:ring-1',
|
|
243
|
-
'
|
|
243
|
+
'min-h-[40px]',
|
|
244
244
|
isQuery ? 'border-info/30 focus:ring-info/40' : 'border-border focus:ring-accent/40',
|
|
245
245
|
)}
|
|
246
|
-
style={{ height: Math.min(Math.max(40, input.split('\n').length * 22), 120) }}
|
|
247
246
|
/>
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
247
|
+
{isThinking ? (
|
|
248
|
+
<button
|
|
249
|
+
onClick={() => useGrooveStore.getState().stopAgent(agent.id)}
|
|
250
|
+
title="Stop agent"
|
|
251
|
+
className="w-10 h-10 flex items-center justify-center rounded-xl transition-all cursor-pointer bg-danger/80 text-white hover:bg-danger shadow-lg shadow-danger/20"
|
|
252
|
+
>
|
|
253
|
+
<Square size={14} fill="currentColor" />
|
|
254
|
+
</button>
|
|
255
|
+
) : (
|
|
256
|
+
<button
|
|
257
|
+
onClick={handleSend}
|
|
258
|
+
disabled={!input.trim() || sending}
|
|
259
|
+
className={cn(
|
|
260
|
+
'w-10 h-10 flex items-center justify-center rounded-xl transition-all cursor-pointer',
|
|
261
|
+
'disabled:opacity-20 disabled:cursor-not-allowed',
|
|
262
|
+
input.trim()
|
|
263
|
+
? 'bg-accent text-surface-0 hover:bg-accent/90 shadow-lg shadow-accent/20'
|
|
264
|
+
: 'bg-surface-4 text-text-4',
|
|
265
|
+
)}
|
|
266
|
+
>
|
|
267
|
+
{sending ? <Loader2 size={16} className="animate-spin" /> : <Send size={16} />}
|
|
268
|
+
</button>
|
|
269
|
+
)}
|
|
261
270
|
</div>
|
|
262
271
|
</div>
|
|
263
272
|
</div>
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
Gauge, FolderSearch, Key, Check, Eye, EyeOff,
|
|
6
6
|
AlertCircle, Layers, Activity,
|
|
7
7
|
RotateCw, Skull, Copy, Trash2,
|
|
8
|
-
Sparkles, Calendar,
|
|
8
|
+
Sparkles, Calendar, Plug,
|
|
9
9
|
} from 'lucide-react';
|
|
10
10
|
import { useGrooveStore } from '../../stores/groove';
|
|
11
11
|
import { Badge } from '../ui/badge';
|
|
@@ -436,6 +436,27 @@ export function AgentConfig({ agent }) {
|
|
|
436
436
|
/>
|
|
437
437
|
</ConfigSection>
|
|
438
438
|
|
|
439
|
+
{/* ── Integration Approvals ────────────────────────────── */}
|
|
440
|
+
{agent.integrations?.length > 0 && (
|
|
441
|
+
<ConfigSection label="Integration Approvals" icon={Plug} description="Manual = you approve dangerous actions. Auto = agent acts freely.">
|
|
442
|
+
<SegmentedControl
|
|
443
|
+
options={[
|
|
444
|
+
{ value: 'manual', label: 'Manual' },
|
|
445
|
+
{ value: 'auto', label: 'Auto' },
|
|
446
|
+
]}
|
|
447
|
+
value={agent.integrationApproval || 'manual'}
|
|
448
|
+
onChange={async (val) => {
|
|
449
|
+
try {
|
|
450
|
+
await api.patch(`/agents/${agent.id}`, { integrationApproval: val });
|
|
451
|
+
addToast('success', `Integration approvals → ${val === 'auto' ? 'Auto' : 'Manual'}`);
|
|
452
|
+
} catch (err) {
|
|
453
|
+
addToast('error', 'Update failed', err.message);
|
|
454
|
+
}
|
|
455
|
+
}}
|
|
456
|
+
/>
|
|
457
|
+
</ConfigSection>
|
|
458
|
+
)}
|
|
459
|
+
|
|
439
460
|
{/* ── Model Routing ────────────────────────────────────── */}
|
|
440
461
|
<ConfigSection label="Model Routing" icon={Activity} description="How Groove selects models for this agent's tasks.">
|
|
441
462
|
<SegmentedControl
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
|
-
import { useState, useRef, useEffect, useMemo } from 'react';
|
|
2
|
+
import { useState, useRef, useEffect, useMemo, useCallback } from 'react';
|
|
3
3
|
import {
|
|
4
|
-
Send, Loader2, MessageSquare, ArrowRight,
|
|
4
|
+
Send, Loader2, MessageSquare, ArrowRight, Square,
|
|
5
5
|
FileEdit, Search, Terminal, CheckCircle2, AlertCircle,
|
|
6
6
|
RotateCw, Zap, Wrench, Eye, Code2, Bug,
|
|
7
|
-
ChevronDown, HelpCircle, Pencil, Paperclip,
|
|
7
|
+
ChevronDown, HelpCircle, Pencil, Paperclip, GripHorizontal,
|
|
8
8
|
} from 'lucide-react';
|
|
9
9
|
import { AnimatePresence, motion } from 'framer-motion';
|
|
10
10
|
import { useGrooveStore } from '../../stores/groove';
|
|
@@ -480,10 +480,22 @@ export function AgentFeed({ agent }) {
|
|
|
480
480
|
const setInput = setStoreInput;
|
|
481
481
|
const [mode, setMode] = useState('instruct'); // instruct | query
|
|
482
482
|
const [sending, setSending] = useState(false);
|
|
483
|
+
const [inputHeight, setInputHeight] = useState(36);
|
|
484
|
+
const dragRef = useRef(null);
|
|
483
485
|
const scrollRef = useRef(null);
|
|
484
486
|
const inputRef = useRef(null);
|
|
485
487
|
const fileInputRef = useRef(null);
|
|
486
488
|
|
|
489
|
+
const onDragStart = useCallback((e) => {
|
|
490
|
+
e.preventDefault();
|
|
491
|
+
const startY = e.clientY;
|
|
492
|
+
const startH = inputHeight;
|
|
493
|
+
const onMove = (ev) => setInputHeight(Math.min(Math.max(36, startH - (ev.clientY - startY)), 280));
|
|
494
|
+
const onUp = () => { window.removeEventListener('mousemove', onMove); window.removeEventListener('mouseup', onUp); };
|
|
495
|
+
window.addEventListener('mousemove', onMove);
|
|
496
|
+
window.addEventListener('mouseup', onUp);
|
|
497
|
+
}, [inputHeight]);
|
|
498
|
+
|
|
487
499
|
const timeline = useMemo(() => {
|
|
488
500
|
const items = [];
|
|
489
501
|
const seen = new Set();
|
|
@@ -648,7 +660,17 @@ export function AgentFeed({ agent }) {
|
|
|
648
660
|
</div>
|
|
649
661
|
|
|
650
662
|
{/* Input area */}
|
|
651
|
-
<div className="
|
|
663
|
+
<div className="bg-surface-1/50 flex-shrink-0">
|
|
664
|
+
{/* Drag handle */}
|
|
665
|
+
<div
|
|
666
|
+
ref={dragRef}
|
|
667
|
+
onMouseDown={onDragStart}
|
|
668
|
+
className="flex items-center justify-center h-5 cursor-row-resize border-t border-border hover:bg-surface-3/50 transition-colors group"
|
|
669
|
+
>
|
|
670
|
+
<GripHorizontal size={12} className="text-text-4 group-hover:text-text-2 transition-colors" />
|
|
671
|
+
</div>
|
|
672
|
+
|
|
673
|
+
<div className="px-4 pb-3">
|
|
652
674
|
{/* Mode pills */}
|
|
653
675
|
<div className="flex items-center gap-1 mb-2">
|
|
654
676
|
<button
|
|
@@ -713,25 +735,35 @@ export function AgentFeed({ agent }) {
|
|
|
713
735
|
'bg-transparent text-text-0 font-sans',
|
|
714
736
|
'placeholder:text-text-4',
|
|
715
737
|
'focus:outline-none',
|
|
716
|
-
'max-h-[120px] min-h-[36px]',
|
|
717
738
|
)}
|
|
718
|
-
style={{ height:
|
|
739
|
+
style={{ height: inputHeight }}
|
|
719
740
|
/>
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
741
|
+
{isThinking ? (
|
|
742
|
+
<button
|
|
743
|
+
onClick={() => useGrooveStore.getState().stopAgent(agent.id)}
|
|
744
|
+
title="Stop agent"
|
|
745
|
+
className="w-9 h-9 flex items-center justify-center rounded-lg border border-danger/30 bg-danger/12 text-danger hover:bg-danger/20 transition-all cursor-pointer flex-shrink-0 mb-px"
|
|
746
|
+
>
|
|
747
|
+
<Square size={13} fill="currentColor" />
|
|
748
|
+
</button>
|
|
749
|
+
) : (
|
|
750
|
+
<button
|
|
751
|
+
onClick={handleSend}
|
|
752
|
+
disabled={!input.trim() || sending}
|
|
753
|
+
className={cn(
|
|
754
|
+
'w-9 h-9 flex items-center justify-center rounded-lg transition-all cursor-pointer flex-shrink-0 mb-px',
|
|
755
|
+
'disabled:opacity-15 disabled:cursor-not-allowed',
|
|
756
|
+
input.trim()
|
|
757
|
+
? mode === 'query'
|
|
758
|
+
? 'bg-info text-white hover:bg-info/85'
|
|
759
|
+
: 'bg-accent text-white hover:bg-accent/85'
|
|
760
|
+
: 'bg-transparent text-text-4',
|
|
761
|
+
)}
|
|
762
|
+
>
|
|
763
|
+
{sending ? <Loader2 size={15} className="animate-spin" /> : <Send size={15} />}
|
|
764
|
+
</button>
|
|
765
|
+
)}
|
|
766
|
+
</div>
|
|
735
767
|
</div>
|
|
736
768
|
</div>
|
|
737
769
|
</div>
|
|
@@ -1,110 +1,48 @@
|
|
|
1
1
|
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
|
-
import { memo, useState } from 'react';
|
|
2
|
+
import { memo, useState, useMemo, useRef, useEffect } from 'react';
|
|
3
3
|
import { Handle, Position } from '@xyflow/react';
|
|
4
4
|
import { useGrooveStore } from '../../stores/groove';
|
|
5
|
-
import { cn } from '../../lib/cn';
|
|
6
5
|
import { statusColor } from '../../lib/status';
|
|
7
|
-
import { fmtNum } from '../../lib/format';
|
|
6
|
+
import { fmtNum, fmtDollar, fmtUptime } from '../../lib/format';
|
|
8
7
|
|
|
9
8
|
const EMPTY = [];
|
|
9
|
+
const ERROR_RE = /error|crash|fail/i;
|
|
10
|
+
const BAR_BG = 'rgba(51, 175, 188, 0.15)';
|
|
11
|
+
const BAR_H = 'h-[2px]';
|
|
10
12
|
|
|
11
|
-
// ── Clean up model ID → short display name ───────────────
|
|
12
|
-
// "claude-haiku-4-5-20251001" → "Haiku 4.5"
|
|
13
|
-
// "claude-opus-4-6" → "Opus 4.6"
|
|
14
|
-
// "gemini-3.1-pro-preview" → "Gemini 3.1 Pro"
|
|
15
|
-
// "o4-mini" → "o4-mini"
|
|
16
13
|
function shortModel(id) {
|
|
17
14
|
if (!id || id === 'auto') return 'auto';
|
|
18
|
-
// Claude models: strip "claude-" prefix and date suffix, capitalize
|
|
19
15
|
const claude = id.match(/^claude-(opus|sonnet|haiku)-(\d+)-(\d+)(?:-\d+)?$/);
|
|
20
16
|
if (claude) {
|
|
21
17
|
const name = claude[1][0].toUpperCase() + claude[1].slice(1);
|
|
22
18
|
return `${name} ${claude[2]}.${claude[3]}`;
|
|
23
19
|
}
|
|
24
|
-
// Gemini: strip "-preview"
|
|
25
20
|
if (id.startsWith('gemini-')) {
|
|
26
21
|
return id.replace('gemini-', 'Gemini ').replace('-preview', '').replace('-flash-lite', ' Flash Lite').replace('-flash', ' Flash').replace('-pro', ' Pro');
|
|
27
22
|
}
|
|
28
|
-
// GPT: capitalize
|
|
29
23
|
if (id.startsWith('gpt-')) return id.toUpperCase().replace('GPT-', 'GPT-');
|
|
30
24
|
return id;
|
|
31
25
|
}
|
|
32
26
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
if (
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (t.includes('bash') || t.includes('exec') || t.includes('running')) return 'EXEC';
|
|
41
|
-
if (t.includes('test')) return 'TEST';
|
|
42
|
-
if (t.includes('error') || t.includes('fail')) return 'ERR';
|
|
43
|
-
if (t.includes('complet') || t.includes('done')) return 'DONE';
|
|
44
|
-
return 'WORK';
|
|
27
|
+
function burnRate(timeline) {
|
|
28
|
+
if (!timeline || timeline.length < 2) return null;
|
|
29
|
+
const samples = timeline.slice(-10);
|
|
30
|
+
const dt = (samples[samples.length - 1].t - samples[0].t) / 60000;
|
|
31
|
+
if (dt <= 0) return null;
|
|
32
|
+
const dv = samples[samples.length - 1].v - samples[0].v;
|
|
33
|
+
return dv / dt;
|
|
45
34
|
}
|
|
46
35
|
|
|
47
|
-
function
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (s < 3600) return `${Math.floor(s / 60)}m`;
|
|
52
|
-
return `${Math.floor(s / 3600)}h`;
|
|
36
|
+
function qualityColor(score) {
|
|
37
|
+
if (score >= 70) return 'var(--color-success)';
|
|
38
|
+
if (score >= 40) return 'var(--color-warning)';
|
|
39
|
+
return 'var(--color-danger)';
|
|
53
40
|
}
|
|
54
41
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
return (
|
|
61
|
-
<div
|
|
62
|
-
className="absolute left-full top-0 ml-2 z-50 pointer-events-none"
|
|
63
|
-
style={{ width: 220, animation: 'tooltip-slide-in 0.15s ease-out' }}
|
|
64
|
-
>
|
|
65
|
-
<div
|
|
66
|
-
className="overflow-hidden"
|
|
67
|
-
style={{ background: '#181b21', border: '1px solid #262a32', borderRadius: 4 }}
|
|
68
|
-
>
|
|
69
|
-
{/* Prompt */}
|
|
70
|
-
{agent.prompt && (
|
|
71
|
-
<div className="px-2.5 py-2 border-b border-[#262a32]">
|
|
72
|
-
<p className="text-[9px] font-sans text-[#8b929e] line-clamp-3 leading-snug">{agent.prompt}</p>
|
|
73
|
-
</div>
|
|
74
|
-
)}
|
|
75
|
-
|
|
76
|
-
{/* Activity log */}
|
|
77
|
-
{recent.length > 0 ? (
|
|
78
|
-
<div>
|
|
79
|
-
<div className="px-2.5 pt-1.5 pb-1">
|
|
80
|
-
<span className="text-[8px] font-mono text-[#3a3f4b] uppercase tracking-widest">Activity</span>
|
|
81
|
-
</div>
|
|
82
|
-
{recent.map((entry, i) => {
|
|
83
|
-
const label = activityLabel(entry.text);
|
|
84
|
-
const display = entry.text?.length > 45 ? entry.text.slice(0, 45) + '...' : entry.text;
|
|
85
|
-
return (
|
|
86
|
-
<div key={i} className="px-2.5 py-[3px] flex items-start gap-1.5">
|
|
87
|
-
<span className="text-[8px] font-mono text-[#333842] w-5 flex-shrink-0 text-right">{timeShort(entry.timestamp)}</span>
|
|
88
|
-
{label && (
|
|
89
|
-
<span className={cn(
|
|
90
|
-
'text-[7px] font-mono w-7 flex-shrink-0 text-center rounded-sm px-0.5 py-px',
|
|
91
|
-
label === 'ERR' ? 'text-[#e06c75] bg-[#e06c75]/10' : 'text-[#505862] bg-[#505862]/10',
|
|
92
|
-
)}>{label}</span>
|
|
93
|
-
)}
|
|
94
|
-
<span className="text-[9px] font-sans text-[#6e7681] truncate flex-1">{display}</span>
|
|
95
|
-
</div>
|
|
96
|
-
);
|
|
97
|
-
})}
|
|
98
|
-
<div className="h-1.5" />
|
|
99
|
-
</div>
|
|
100
|
-
) : (
|
|
101
|
-
<div className="px-2.5 py-3">
|
|
102
|
-
<span className="text-[9px] font-mono text-[#333842]">Awaiting activity...</span>
|
|
103
|
-
</div>
|
|
104
|
-
)}
|
|
105
|
-
</div>
|
|
106
|
-
</div>
|
|
107
|
-
);
|
|
42
|
+
function efficiencyColor(pct) {
|
|
43
|
+
if (pct >= 60) return 'var(--color-success)';
|
|
44
|
+
if (pct >= 30) return 'var(--color-warning)';
|
|
45
|
+
return 'var(--color-danger)';
|
|
108
46
|
}
|
|
109
47
|
|
|
110
48
|
// ── Status labels ────────────────────────────────────────
|
|
@@ -121,18 +59,41 @@ const AgentNode = memo(({ data, selected }) => {
|
|
|
121
59
|
const sColor = statusColor(agent.status);
|
|
122
60
|
const tokens = agent.tokensUsed || 0;
|
|
123
61
|
const [hovered, setHovered] = useState(false);
|
|
62
|
+
const nodeRef = useRef(null);
|
|
63
|
+
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
const rfNode = nodeRef.current?.closest('.react-flow__node');
|
|
66
|
+
if (rfNode) rfNode.style.zIndex = hovered ? '1000' : '';
|
|
67
|
+
}, [hovered]);
|
|
68
|
+
|
|
69
|
+
const activityLog = useGrooveStore((s) => s.activityLog[agent.id]) || EMPTY;
|
|
70
|
+
const tokenTimeline = useGrooveStore((s) => s.tokenTimeline[agent.id]) || EMPTY;
|
|
71
|
+
const rate = burnRate(tokenTimeline);
|
|
72
|
+
const errorCount = useMemo(() => activityLog.filter((e) => ERROR_RE.test(e.text)).length, [activityLog]);
|
|
73
|
+
const ctxColor = contextPct > 75 ? 'var(--color-danger)' : contextPct > 50 ? 'var(--color-warning)' : 'var(--color-success)';
|
|
74
|
+
|
|
75
|
+
const qScore = agent.qualityScore != null ? Math.round(agent.qualityScore) : null;
|
|
76
|
+
const qColor = qScore != null ? qualityColor(qScore) : null;
|
|
77
|
+
|
|
78
|
+
const effPct = agent.efficiency != null ? agent.efficiency : null;
|
|
79
|
+
const effColor = effPct != null ? efficiencyColor(effPct) : null;
|
|
80
|
+
|
|
81
|
+
const uptimeSec = agent.durationMs ? agent.durationMs / 1000
|
|
82
|
+
: agent.spawnedAt ? (Date.now() - new Date(agent.spawnedAt).getTime()) / 1000
|
|
83
|
+
: agent.createdAt ? (Date.now() - new Date(agent.createdAt).getTime()) / 1000
|
|
84
|
+
: 0;
|
|
124
85
|
|
|
125
86
|
return (
|
|
126
87
|
<div
|
|
127
|
-
|
|
88
|
+
ref={nodeRef}
|
|
128
89
|
onMouseEnter={() => setHovered(true)}
|
|
129
90
|
onMouseLeave={() => setHovered(false)}
|
|
130
91
|
>
|
|
131
92
|
<div
|
|
132
|
-
className="w-[220px] overflow-hidden transition-all duration-
|
|
93
|
+
className="w-[220px] overflow-hidden transition-all duration-200 ease-out"
|
|
133
94
|
style={{
|
|
134
|
-
background: '#1c1f26',
|
|
135
|
-
border: `1px solid ${selected ? '#2e323a' : '#262a32'}`,
|
|
95
|
+
background: hovered ? '#141720' : '#1c1f26',
|
|
96
|
+
border: `1px solid ${hovered ? '#2e3640' : selected ? '#2e323a' : '#262a32'}`,
|
|
136
97
|
borderRadius: 4,
|
|
137
98
|
}}
|
|
138
99
|
>
|
|
@@ -195,7 +156,7 @@ const AgentNode = memo(({ data, selected }) => {
|
|
|
195
156
|
</div>
|
|
196
157
|
|
|
197
158
|
{/* Context bar */}
|
|
198
|
-
<div className=
|
|
159
|
+
<div className={`mt-1.5 ${BAR_H} rounded-sm overflow-hidden`} style={{ background: BAR_BG }}>
|
|
199
160
|
<div
|
|
200
161
|
className="h-full rounded-sm transition-all duration-700"
|
|
201
162
|
style={{
|
|
@@ -207,10 +168,91 @@ const AgentNode = memo(({ data, selected }) => {
|
|
|
207
168
|
/>
|
|
208
169
|
</div>
|
|
209
170
|
</div>
|
|
210
|
-
</div>
|
|
211
171
|
|
|
212
|
-
|
|
213
|
-
|
|
172
|
+
{/* ── Expanded stats (morphs on hover) ────────── */}
|
|
173
|
+
<div
|
|
174
|
+
className="grid transition-[grid-template-rows] duration-200 ease-out"
|
|
175
|
+
style={{ gridTemplateRows: hovered ? '1fr' : '0fr' }}
|
|
176
|
+
>
|
|
177
|
+
<div className="overflow-hidden">
|
|
178
|
+
<div className="mx-3 border-t border-white/[0.04]" />
|
|
179
|
+
|
|
180
|
+
{/* Context Health */}
|
|
181
|
+
<div className="px-3 pt-1.5 pb-1">
|
|
182
|
+
<div className="flex items-center justify-between mb-1">
|
|
183
|
+
<span className="text-[9px] font-mono text-[#505862] uppercase tracking-wider">Context</span>
|
|
184
|
+
{(agent.rotations || 0) > 0 && (
|
|
185
|
+
<span className="text-[8px] font-mono text-[#606878] bg-white/[0.04] rounded px-1 py-px">{agent.rotations}x rot</span>
|
|
186
|
+
)}
|
|
187
|
+
</div>
|
|
188
|
+
<div className="flex items-center gap-2">
|
|
189
|
+
<div className={`flex-1 ${BAR_H} rounded-sm overflow-hidden`} style={{ background: BAR_BG }}>
|
|
190
|
+
<div className="h-full rounded-sm transition-all duration-500" style={{ width: `${Math.max(contextPct, 1)}%`, background: ctxColor }} />
|
|
191
|
+
</div>
|
|
192
|
+
<span className="text-[9px] font-mono font-medium" style={{ color: ctxColor }}>{contextPct}%</span>
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
|
|
196
|
+
{/* Quality */}
|
|
197
|
+
<div className="px-3 pt-1 pb-1">
|
|
198
|
+
<span className="text-[9px] font-mono text-[#505862] uppercase tracking-wider">Quality</span>
|
|
199
|
+
<div className="flex items-center gap-2 mt-1">
|
|
200
|
+
<div className={`flex-1 ${BAR_H} rounded-sm overflow-hidden`} style={{ background: BAR_BG }}>
|
|
201
|
+
<div className="h-full rounded-sm transition-all duration-500" style={{ width: `${qScore != null ? Math.max(qScore, 1) : 0}%`, background: qColor || '#505862' }} />
|
|
202
|
+
</div>
|
|
203
|
+
<span className="text-[9px] font-mono font-medium" style={{ color: qColor || '#505862' }}>{qScore != null ? qScore : '—'}</span>
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
|
|
207
|
+
{/* Efficiency (cache hit rate) */}
|
|
208
|
+
<div className="px-3 pt-1 pb-1">
|
|
209
|
+
<span className="text-[9px] font-mono text-[#505862] uppercase tracking-wider">Efficiency</span>
|
|
210
|
+
<div className="flex items-center gap-2 mt-1">
|
|
211
|
+
<div className={`flex-1 ${BAR_H} rounded-sm overflow-hidden`} style={{ background: BAR_BG }}>
|
|
212
|
+
<div className="h-full rounded-sm transition-all duration-500" style={{ width: `${effPct != null ? Math.max(effPct, 1) : 0}%`, background: effColor || '#505862' }} />
|
|
213
|
+
</div>
|
|
214
|
+
<span className="text-[9px] font-mono font-medium" style={{ color: effColor || '#505862' }}>{effPct != null ? `${effPct}%` : '—'}</span>
|
|
215
|
+
</div>
|
|
216
|
+
</div>
|
|
217
|
+
|
|
218
|
+
{/* Stats row */}
|
|
219
|
+
<div className="px-3 pt-1 pb-1">
|
|
220
|
+
<div className="grid grid-cols-3 gap-1">
|
|
221
|
+
<div>
|
|
222
|
+
<div className="text-[9px] font-mono font-medium text-[#bcc2cd]">{fmtDollar(agent.costUsd || 0)}</div>
|
|
223
|
+
<div className="text-[7px] font-mono text-[#505862]">cost</div>
|
|
224
|
+
</div>
|
|
225
|
+
<div>
|
|
226
|
+
<div className="text-[9px] font-mono font-medium text-[#bcc2cd]">{rate ? fmtNum(Math.round(rate)) : '—'}</div>
|
|
227
|
+
<div className="text-[7px] font-mono text-[#505862]">tok/m</div>
|
|
228
|
+
</div>
|
|
229
|
+
<div>
|
|
230
|
+
<div className="text-[9px] font-mono font-medium text-[#bcc2cd]">{agent.turns || 0}</div>
|
|
231
|
+
<div className="text-[7px] font-mono text-[#505862]">turns</div>
|
|
232
|
+
</div>
|
|
233
|
+
</div>
|
|
234
|
+
</div>
|
|
235
|
+
|
|
236
|
+
{/* Session */}
|
|
237
|
+
<div className="px-3 pt-1 pb-2">
|
|
238
|
+
<div className="flex items-center gap-3">
|
|
239
|
+
<div className="flex items-center gap-1">
|
|
240
|
+
<span className="text-[9px] font-mono text-[#8b929e]">{fmtUptime(Math.max(0, Math.floor(uptimeSec)))}</span>
|
|
241
|
+
<span className="text-[7px] font-mono text-[#505862]">up</span>
|
|
242
|
+
</div>
|
|
243
|
+
<div className="flex items-center gap-1">
|
|
244
|
+
{errorCount > 0 ? (
|
|
245
|
+
<span className="text-[9px] font-mono text-[var(--color-danger)]">{errorCount}</span>
|
|
246
|
+
) : (
|
|
247
|
+
<span className="text-[9px] font-mono text-[#505862]">0</span>
|
|
248
|
+
)}
|
|
249
|
+
<span className="text-[7px] font-mono text-[#505862]">err</span>
|
|
250
|
+
</div>
|
|
251
|
+
</div>
|
|
252
|
+
</div>
|
|
253
|
+
</div>
|
|
254
|
+
</div>
|
|
255
|
+
</div>
|
|
214
256
|
</div>
|
|
215
257
|
);
|
|
216
258
|
});
|