groove-dev 0.27.196 → 0.27.198
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/axom-integration/Screenshot_2026-07-25_at_5.25.30_PM.png +0 -0
- package/axom-integration/Screenshot_2026-07-25_at_5.33.26_PM.png +0 -0
- package/axom-integration/Screenshot_2026-07-25_at_6.11.53_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 +5 -5
- package/node_modules/@groove-dev/daemon/src/axom-connector.js +340 -0
- package/node_modules/@groove-dev/daemon/src/axom-install.js +140 -0
- package/node_modules/@groove-dev/daemon/src/axom-server.js +229 -0
- package/node_modules/@groove-dev/daemon/src/chatstore.js +104 -0
- package/node_modules/@groove-dev/daemon/src/federation.js +6 -0
- package/node_modules/@groove-dev/daemon/src/index.js +14 -2
- package/node_modules/@groove-dev/daemon/src/innerchat-docs.js +8 -1
- package/node_modules/@groove-dev/daemon/src/innerchat-relay.js +89 -0
- package/node_modules/@groove-dev/daemon/src/innerchat.js +261 -1
- package/node_modules/@groove-dev/daemon/src/introducer.js +1 -1
- package/node_modules/@groove-dev/daemon/src/network-guard.js +56 -0
- package/node_modules/@groove-dev/daemon/src/process.js +2 -2
- package/node_modules/@groove-dev/daemon/src/providers/axom.js +66 -0
- package/node_modules/@groove-dev/daemon/src/providers/index.js +2 -0
- package/node_modules/@groove-dev/daemon/src/routes/agents.js +1 -175
- package/node_modules/@groove-dev/daemon/src/routes/axom.js +196 -0
- package/node_modules/@groove-dev/daemon/src/routes/chat-history.js +34 -0
- package/node_modules/@groove-dev/daemon/src/routes/innerchat.js +152 -20
- package/node_modules/@groove-dev/daemon/test/axom-connector.test.js +412 -0
- package/node_modules/@groove-dev/daemon/test/axom-server.test.js +187 -0
- package/node_modules/@groove-dev/daemon/test/chatstore.test.js +86 -0
- package/node_modules/@groove-dev/daemon/test/innerchat-relay.test.js +251 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-RbtaI6l7.css +1 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-b9dKN6cq.js +1074 -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 -2
- package/node_modules/@groove-dev/gui/src/app.css +53 -0
- 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 +3 -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 +24 -163
- package/node_modules/@groove-dev/gui/src/stores/slices/agents-slice.js +52 -2
- package/node_modules/@groove-dev/gui/src/stores/slices/axom-slice.js +197 -0
- package/node_modules/@groove-dev/gui/src/views/axom.jsx +1060 -0
- package/node_modules/@groove-dev/gui/src/views/marketplace.jsx +1 -1
- package/node_modules/@groove-dev/gui/src/views/settings.jsx +178 -64
- package/package.json +2 -2
- package/packages/cli/package.json +1 -1
- package/packages/daemon/package.json +1 -1
- package/packages/daemon/src/api.js +5 -5
- package/packages/daemon/src/axom-connector.js +340 -0
- package/packages/daemon/src/axom-install.js +140 -0
- package/packages/daemon/src/axom-server.js +229 -0
- package/packages/daemon/src/chatstore.js +104 -0
- package/packages/daemon/src/federation.js +6 -0
- package/packages/daemon/src/index.js +14 -2
- package/packages/daemon/src/innerchat-docs.js +8 -1
- package/packages/daemon/src/innerchat-relay.js +89 -0
- package/packages/daemon/src/innerchat.js +261 -1
- package/packages/daemon/src/introducer.js +1 -1
- package/packages/daemon/src/network-guard.js +56 -0
- package/packages/daemon/src/process.js +2 -2
- package/packages/daemon/src/providers/axom.js +66 -0
- package/packages/daemon/src/providers/index.js +2 -0
- package/packages/daemon/src/routes/agents.js +1 -175
- package/packages/daemon/src/routes/axom.js +196 -0
- package/packages/daemon/src/routes/chat-history.js +34 -0
- package/packages/daemon/src/routes/innerchat.js +152 -20
- package/packages/gui/dist/assets/index-RbtaI6l7.css +1 -0
- package/packages/gui/dist/assets/index-b9dKN6cq.js +1074 -0
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/App.jsx +2 -2
- package/packages/gui/src/app.css +53 -0
- 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 +3 -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 +24 -163
- package/packages/gui/src/stores/slices/agents-slice.js +52 -2
- package/packages/gui/src/stores/slices/axom-slice.js +197 -0
- package/packages/gui/src/views/axom.jsx +1060 -0
- package/packages/gui/src/views/marketplace.jsx +1 -1
- package/packages/gui/src/views/settings.jsx +178 -64
- 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
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<link rel="icon" type="image/png" href="/favicon.png" />
|
|
8
8
|
<title>Groove GUI</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-b9dKN6cq.js"></script>
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/vendor-26L3JoZv.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/reactflow-DoBZjiHE.js">
|
|
12
12
|
<link rel="modulepreload" crossorigin href="/assets/codemirror-BYKpdS2W.js">
|
|
13
13
|
<link rel="modulepreload" crossorigin href="/assets/xterm--7_ns2zW.js">
|
|
14
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
14
|
+
<link rel="stylesheet" crossorigin href="/assets/index-RbtaI6l7.css">
|
|
15
15
|
</head>
|
|
16
16
|
<body>
|
|
17
17
|
<div id="root"></div>
|
package/packages/gui/src/App.jsx
CHANGED
|
@@ -22,10 +22,10 @@ import ModelsView from './views/models';
|
|
|
22
22
|
import FederationView from './views/federation';
|
|
23
23
|
import ModelLabView from './views/model-lab';
|
|
24
24
|
import NetworkView from './views/network';
|
|
25
|
-
import ChatView from './views/chat';
|
|
26
25
|
import MemoryView from './views/memory';
|
|
27
26
|
import FleetView from './views/fleet';
|
|
28
27
|
import AutoAgentsView from './views/auto-agents';
|
|
28
|
+
import AxomView from './views/axom';
|
|
29
29
|
|
|
30
30
|
// Agent components
|
|
31
31
|
import { AgentPanel } from './components/agents/agent-panel';
|
|
@@ -79,8 +79,8 @@ function ViewRouter() {
|
|
|
79
79
|
case 'model-lab': content = <ModelLabView />; break;
|
|
80
80
|
case 'federation': content = <FederationView />; break;
|
|
81
81
|
case 'settings': content = <SettingsView />; break;
|
|
82
|
-
case 'chat': content = <ChatView />; break;
|
|
83
82
|
case 'fleet': content = <FleetView />; break;
|
|
83
|
+
case 'axom': content = <AxomView />; break;
|
|
84
84
|
case 'memory': content = <MemoryView />; break;
|
|
85
85
|
case 'network': content = networkUnlocked ? <NetworkView /> : <AgentsView />; break;
|
|
86
86
|
default: content = <AgentsView />;
|
package/packages/gui/src/app.css
CHANGED
|
@@ -309,6 +309,59 @@ html {
|
|
|
309
309
|
box-shadow: 0 0 60px rgba(51,175,188,0.15), 0 0 120px rgba(51,175,188,0.04);
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
+
/* ── Axom Onboarding ─────────────────────────────────────── */
|
|
313
|
+
|
|
314
|
+
/* Front door backdrop — one cool bloom behind the mark on the identity pane,
|
|
315
|
+
a faint cold wash under the operations pane. Nothing else. */
|
|
316
|
+
.axom-hero-bg {
|
|
317
|
+
background:
|
|
318
|
+
radial-gradient(ellipse 760px 620px at 20% 38%, rgba(51, 175, 188, 0.11) 0%, transparent 68%),
|
|
319
|
+
radial-gradient(ellipse 900px 700px at 88% 62%, rgba(122, 176, 223, 0.045) 0%, transparent 70%),
|
|
320
|
+
var(--color-surface-1);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/* Panels read as instrument faces: light at the top edge, settling downward. */
|
|
324
|
+
.axom-panel {
|
|
325
|
+
background: linear-gradient(180deg, var(--color-surface-3) 0%, var(--color-surface-1) 100%);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/* Hairline that carries the accent only where it matters — a top edge. */
|
|
329
|
+
.axom-edge-accent {
|
|
330
|
+
background: linear-gradient(90deg, transparent 0%, rgba(51, 175, 188, 0.6) 50%, transparent 100%);
|
|
331
|
+
}
|
|
332
|
+
.axom-edge-muted {
|
|
333
|
+
background: linear-gradient(90deg, transparent 0%, var(--color-border) 50%, transparent 100%);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/* Display type — a soft vertical falloff, no color, just depth. */
|
|
337
|
+
.axom-display {
|
|
338
|
+
background: linear-gradient(180deg, #f2f4f7 0%, #9aa3b0 100%);
|
|
339
|
+
-webkit-background-clip: text;
|
|
340
|
+
background-clip: text;
|
|
341
|
+
color: transparent;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.axom-ring {
|
|
345
|
+
border-radius: 50%;
|
|
346
|
+
border: 1px solid rgba(51, 175, 188, 0.22);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
@keyframes axom-orbit { to { transform: rotate(360deg); } }
|
|
350
|
+
.animate-axom-orbit { animation: axom-orbit 26s linear infinite; }
|
|
351
|
+
.animate-axom-orbit-slow { animation: axom-orbit 44s linear infinite reverse; }
|
|
352
|
+
|
|
353
|
+
@keyframes axom-breathe {
|
|
354
|
+
0%, 100% { opacity: 0.45; }
|
|
355
|
+
50% { opacity: 0.85; }
|
|
356
|
+
}
|
|
357
|
+
.animate-axom-breathe { animation: axom-breathe 6s ease-in-out infinite; }
|
|
358
|
+
|
|
359
|
+
@media (prefers-reduced-motion: reduce) {
|
|
360
|
+
.animate-axom-orbit,
|
|
361
|
+
.animate-axom-orbit-slow,
|
|
362
|
+
.animate-axom-breathe { animation: none; }
|
|
363
|
+
}
|
|
364
|
+
|
|
312
365
|
/* ── React Flow Overrides ─────────────────────────────────── */
|
|
313
366
|
|
|
314
367
|
/* Suppress node fly-in animation — nodes appear in position instantly */
|
|
@@ -704,7 +704,7 @@ export function AgentConfig({ agent }) {
|
|
|
704
704
|
</div>
|
|
705
705
|
)}
|
|
706
706
|
{(agent.integrations || []).length === 0 && installedIntegrations.length === 0 && (
|
|
707
|
-
<span className="text-2xs text-text-4 font-sans">No integrations installed — browse the
|
|
707
|
+
<span className="text-2xs text-text-4 font-sans">No integrations installed — browse the Integrations tab</span>
|
|
708
708
|
)}
|
|
709
709
|
</div>
|
|
710
710
|
{(agent.integrations || []).length > 0 && (
|
|
@@ -775,7 +775,7 @@ export function AgentConfig({ agent }) {
|
|
|
775
775
|
</div>
|
|
776
776
|
)}
|
|
777
777
|
{(agent.repos || []).length === 0 && (
|
|
778
|
-
<span className="text-2xs text-text-4 font-sans">No repos attached — import one from the
|
|
778
|
+
<span className="text-2xs text-text-4 font-sans">No repos attached — import one from the Integrations tab</span>
|
|
779
779
|
)}
|
|
780
780
|
</div>
|
|
781
781
|
</ConfigSection>
|
|
@@ -368,7 +368,7 @@ export function SpawnWizard() {
|
|
|
368
368
|
}}
|
|
369
369
|
className="ml-auto text-2xs text-accent hover:underline font-sans cursor-pointer"
|
|
370
370
|
>
|
|
371
|
-
Install in
|
|
371
|
+
Install in Integrations
|
|
372
372
|
</button>
|
|
373
373
|
</div>
|
|
374
374
|
);
|
|
@@ -778,7 +778,7 @@ export function SpawnWizard() {
|
|
|
778
778
|
<div className="text-2xs text-text-3 font-sans truncate">{integration.description}</div>
|
|
779
779
|
)}
|
|
780
780
|
{!configured && (
|
|
781
|
-
<div className="text-2xs text-text-4 font-sans">Configure in
|
|
781
|
+
<div className="text-2xs text-text-4 font-sans">Configure in Integrations</div>
|
|
782
782
|
)}
|
|
783
783
|
</div>
|
|
784
784
|
{active && <CheckMark />}
|
|
@@ -787,7 +787,7 @@ export function SpawnWizard() {
|
|
|
787
787
|
})}
|
|
788
788
|
{installedIntegrations.length === 0 && (
|
|
789
789
|
<div className="text-center py-6 text-xs text-text-3 font-sans">
|
|
790
|
-
No integrations installed. Visit the
|
|
790
|
+
No integrations installed. Visit the Integrations tab to install some.
|
|
791
791
|
</div>
|
|
792
792
|
)}
|
|
793
793
|
</div>
|
|
@@ -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,
|
|
2
|
+
import { Network, Code2, ChartSpline, Puzzle, Users, Box, FlaskConical, Newspaper, Settings, Globe, BookOpen, LayoutList, Bot, Atom } from 'lucide-react';
|
|
3
3
|
import { cn } from '../../lib/cn';
|
|
4
4
|
import { Tooltip } from '../ui/tooltip';
|
|
5
5
|
import { useGrooveStore } from '../../stores/groove';
|
|
@@ -7,14 +7,14 @@ import { isElectron, getPlatform } from '../../lib/electron';
|
|
|
7
7
|
|
|
8
8
|
const BASE_NAV_ITEMS = [
|
|
9
9
|
{ id: 'agents', icon: Network, label: 'Agents' },
|
|
10
|
+
{ id: 'axom', icon: Atom, label: 'Axom' },
|
|
10
11
|
{ id: 'fleet', icon: LayoutList, label: 'Fleet' },
|
|
11
|
-
{ id: 'chat', icon: MessageCircle, label: 'Chat' },
|
|
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
15
|
{ id: 'auto-agents', icon: Bot, label: 'Auto Agents' },
|
|
16
16
|
{ id: 'teams', icon: Users, label: 'Teams' },
|
|
17
|
-
{ id: 'marketplace', icon: Puzzle, label: '
|
|
17
|
+
{ id: 'marketplace', icon: Puzzle, label: 'Integrations' },
|
|
18
18
|
{ id: 'models', icon: Box, label: 'Models' },
|
|
19
19
|
{ id: 'model-lab', icon: FlaskConical, label: 'Model Lab' },
|
|
20
20
|
];
|
|
@@ -13,7 +13,7 @@ const STATIC_COMMANDS = [
|
|
|
13
13
|
{ id: 'nav:agents', label: 'Go to Agents', icon: Network, category: 'Navigation', action: (s) => { s.setActiveView('agents'); } },
|
|
14
14
|
{ id: 'nav:editor', label: 'Go to Editor', icon: Code2, category: 'Navigation', action: (s) => { s.setActiveView('editor'); } },
|
|
15
15
|
{ id: 'nav:dashboard', label: 'Go to Dashboard', icon: ChartSpline, category: 'Navigation', action: (s) => { s.setActiveView('dashboard'); } },
|
|
16
|
-
{ id: 'nav:marketplace', label: 'Go to
|
|
16
|
+
{ id: 'nav:marketplace', label: 'Go to Integrations', icon: Puzzle, category: 'Navigation', action: (s) => { s.setActiveView('marketplace'); } },
|
|
17
17
|
{ id: 'nav:teams', label: 'Go to Teams', icon: Users, category: 'Navigation', action: (s) => { s.setActiveView('teams'); } },
|
|
18
18
|
{ id: 'nav:federation', label: 'Go to Federation', icon: Globe, category: 'Navigation', action: (s) => { s.setActiveView('federation'); } },
|
|
19
19
|
{ id: 'action:whitelist', label: 'Add to Federation Whitelist', icon: Shield, category: 'Federation', action: (s) => { s.setActiveView('federation'); } },
|
|
@@ -7,7 +7,6 @@ import { persistJSON, persistChatHistory } from './helpers.js';
|
|
|
7
7
|
import { createUiSlice } from './slices/ui-slice.js';
|
|
8
8
|
import { createAgentsSlice } from './slices/agents-slice.js';
|
|
9
9
|
import { createTeamsSlice } from './slices/teams-slice.js';
|
|
10
|
-
import { createChatSlice } from './slices/chat-slice.js';
|
|
11
10
|
import { createEditorSlice } from './slices/editor-slice.js';
|
|
12
11
|
import { createProvidersSlice } from './slices/providers-slice.js';
|
|
13
12
|
import { createNetworkSlice } from './slices/network-slice.js';
|
|
@@ -15,6 +14,7 @@ import { createPreviewSlice } from './slices/preview-slice.js';
|
|
|
15
14
|
import { createMarketplaceSlice } from './slices/marketplace-slice.js';
|
|
16
15
|
import { createAutomationsSlice } from './slices/automations-slice.js';
|
|
17
16
|
import { createAutoAgentsSlice } from './slices/auto-agents-slice.js';
|
|
17
|
+
import { createAxomSlice } from './slices/axom-slice.js';
|
|
18
18
|
|
|
19
19
|
const WS_URL = `ws://${window.location.hostname}:${window.location.port || 31415}`;
|
|
20
20
|
|
|
@@ -33,7 +33,6 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
33
33
|
...createUiSlice(set, get),
|
|
34
34
|
...createAgentsSlice(set, get),
|
|
35
35
|
...createTeamsSlice(set, get),
|
|
36
|
-
...createChatSlice(set, get),
|
|
37
36
|
...createEditorSlice(set, get),
|
|
38
37
|
...createProvidersSlice(set, get),
|
|
39
38
|
...createNetworkSlice(set, get),
|
|
@@ -41,6 +40,7 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
41
40
|
...createMarketplaceSlice(set, get),
|
|
42
41
|
...createAutomationsSlice(set, get),
|
|
43
42
|
...createAutoAgentsSlice(set, get),
|
|
43
|
+
...createAxomSlice(set, get),
|
|
44
44
|
|
|
45
45
|
// ── Connection ────────────────────────────────────────────
|
|
46
46
|
connected: false,
|
|
@@ -69,12 +69,13 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
69
69
|
if (isTunneled) get().fetchProjectDir();
|
|
70
70
|
}).catch(() => {});
|
|
71
71
|
get().fetchTeams();
|
|
72
|
-
get().
|
|
72
|
+
get().fetchChatHistory();
|
|
73
73
|
get().fetchApprovals();
|
|
74
74
|
get().checkMarketplaceAuth();
|
|
75
75
|
get().fetchTunnels();
|
|
76
76
|
get().fetchBetaStatus();
|
|
77
77
|
get().fetchNetworkInstallStatus();
|
|
78
|
+
get().fetchAxomStatus();
|
|
78
79
|
get().fetchTrainingStatus();
|
|
79
80
|
api.get('/config').then((cfg) => {
|
|
80
81
|
if (cfg?.dataSharingDismissed) set({ dataSharingDismissed: true });
|
|
@@ -266,27 +267,6 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
266
267
|
set({ chatHistory: history });
|
|
267
268
|
persistChatHistory(history);
|
|
268
269
|
}
|
|
269
|
-
|
|
270
|
-
const conv = get().conversations.find((c) => c.agentId === agentId);
|
|
271
|
-
if (conv) {
|
|
272
|
-
const convMsgs = { ...get().conversationMessages };
|
|
273
|
-
if (!convMsgs[conv.id]) convMsgs[conv.id] = [];
|
|
274
|
-
const convArr = [...convMsgs[conv.id]];
|
|
275
|
-
const lastConv = convArr[convArr.length - 1];
|
|
276
|
-
const isRecentConv = lastConv && lastConv.from === 'assistant' && (Date.now() - lastConv.timestamp) < 8000;
|
|
277
|
-
const isConvDupe = isRecentConv && (lastConv.text === trimmed || lastConv.text.endsWith(trimmed));
|
|
278
|
-
if (!isConvDupe) {
|
|
279
|
-
if (isRecentConv) {
|
|
280
|
-
const sep = data.subtype === 'assistant' ? '\n\n' : ' ';
|
|
281
|
-
convArr[convArr.length - 1] = { ...lastConv, text: lastConv.text + sep + trimmed, timestamp: Date.now() };
|
|
282
|
-
} else {
|
|
283
|
-
convArr.push({ from: 'assistant', text: trimmed, timestamp: Date.now() });
|
|
284
|
-
}
|
|
285
|
-
convMsgs[conv.id] = convArr.slice(-200);
|
|
286
|
-
set({ conversationMessages: convMsgs, streamingConversationId: conv.id });
|
|
287
|
-
persistJSON('groove:conversationMessages', convMsgs);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
270
|
}
|
|
291
271
|
|
|
292
272
|
if (activityText && activityText.trim()) {
|
|
@@ -347,11 +327,6 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
347
327
|
});
|
|
348
328
|
}
|
|
349
329
|
|
|
350
|
-
const exitConv = get().conversations.find((c) => c.agentId === msg.agentId);
|
|
351
|
-
if (exitConv && get().streamingConversationId === exitConv.id) {
|
|
352
|
-
set({ sendingMessage: false, streamingConversationId: null });
|
|
353
|
-
}
|
|
354
|
-
|
|
355
330
|
if (msg.error && msg.agentId) {
|
|
356
331
|
get().addChatMessage(msg.agentId, 'system', `Crashed: ${msg.error}`);
|
|
357
332
|
}
|
|
@@ -385,8 +360,10 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
385
360
|
isQuery: false,
|
|
386
361
|
innerchat: { turnId: turn.id, threadId: thread.id, kind: turn.kind, peer, direction },
|
|
387
362
|
});
|
|
363
|
+
const pushed = [];
|
|
388
364
|
const push = (agentId, item) => {
|
|
389
365
|
history[agentId] = [...(history[agentId] || []), item].slice(-100);
|
|
366
|
+
pushed.push([agentId, item]);
|
|
390
367
|
};
|
|
391
368
|
|
|
392
369
|
if (turn.kind === 'ask') {
|
|
@@ -403,6 +380,8 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
403
380
|
}
|
|
404
381
|
|
|
405
382
|
persistChatHistory(history);
|
|
383
|
+
// Mirror to the daemon so these survive an origin/port change too.
|
|
384
|
+
for (const [agentId, item] of pushed) get().persistMessageRemote(agentId, item);
|
|
406
385
|
|
|
407
386
|
const answers = { ...s.innerchatAnswers };
|
|
408
387
|
if (turn.kind === 'answer') {
|
|
@@ -677,6 +656,22 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
677
656
|
set({ gateways: msg.data || [] });
|
|
678
657
|
break;
|
|
679
658
|
|
|
659
|
+
case 'axom:status':
|
|
660
|
+
set({ axomStatus: msg.data || { endpoints: [] } });
|
|
661
|
+
break;
|
|
662
|
+
|
|
663
|
+
case 'axom:event':
|
|
664
|
+
get().ingestAxomEvent(msg.endpoint, msg.session, msg.envelope);
|
|
665
|
+
break;
|
|
666
|
+
|
|
667
|
+
case 'axom:instances':
|
|
668
|
+
set({ axomInstances: msg.data || [] });
|
|
669
|
+
break;
|
|
670
|
+
|
|
671
|
+
case 'axom:install:progress':
|
|
672
|
+
set({ axomInstall: msg.data });
|
|
673
|
+
break;
|
|
674
|
+
|
|
680
675
|
case 'provider:status-changed':
|
|
681
676
|
set({ _providerRefreshTick: Date.now() });
|
|
682
677
|
break;
|
|
@@ -967,140 +962,6 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
967
962
|
get().fetchNetworkInstallStatus();
|
|
968
963
|
break;
|
|
969
964
|
|
|
970
|
-
case 'conversation:created': {
|
|
971
|
-
const conv = msg.data;
|
|
972
|
-
if (conv) set((s) => ({ conversations: [conv, ...s.conversations.filter((c) => c.id !== conv.id)] }));
|
|
973
|
-
break;
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
case 'conversation:updated': {
|
|
977
|
-
const conv = msg.data;
|
|
978
|
-
if (conv) set((s) => ({ conversations: s.conversations.map((c) => c.id === conv.id ? { ...c, ...conv } : c) }));
|
|
979
|
-
break;
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
case 'conversation:deleted': {
|
|
983
|
-
const id = msg.data?.id || msg.id;
|
|
984
|
-
if (id) {
|
|
985
|
-
set((s) => {
|
|
986
|
-
const conversations = s.conversations.filter((c) => c.id !== id);
|
|
987
|
-
const conversationMessages = { ...s.conversationMessages };
|
|
988
|
-
delete conversationMessages[id];
|
|
989
|
-
const activeConversationId = s.activeConversationId === id ? null : s.activeConversationId;
|
|
990
|
-
if (activeConversationId !== s.activeConversationId) localStorage.setItem('groove:activeConversationId', '');
|
|
991
|
-
return { conversations, conversationMessages, activeConversationId };
|
|
992
|
-
});
|
|
993
|
-
}
|
|
994
|
-
break;
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
case 'conversation:tool': {
|
|
998
|
-
const { conversationId, name, summary } = msg.data || msg;
|
|
999
|
-
if (!conversationId) break;
|
|
1000
|
-
set((s) => {
|
|
1001
|
-
const tools = { ...s.conversationActiveTools };
|
|
1002
|
-
tools[conversationId] = { name: name || 'Tool', summary: summary || null, timestamp: Date.now() };
|
|
1003
|
-
return { conversationActiveTools: tools };
|
|
1004
|
-
});
|
|
1005
|
-
break;
|
|
1006
|
-
}
|
|
1007
|
-
|
|
1008
|
-
case 'conversation:chunk': {
|
|
1009
|
-
const { conversationId, text } = msg.data || msg;
|
|
1010
|
-
if (!conversationId || !text) break;
|
|
1011
|
-
set((s) => {
|
|
1012
|
-
const msgs = { ...s.conversationMessages };
|
|
1013
|
-
if (!msgs[conversationId]) msgs[conversationId] = [];
|
|
1014
|
-
const arr = [...msgs[conversationId]];
|
|
1015
|
-
const last = arr[arr.length - 1];
|
|
1016
|
-
if (last && last.from === 'assistant' && (Date.now() - last.timestamp) < 30000) {
|
|
1017
|
-
arr[arr.length - 1] = { ...last, text: last.text + text, timestamp: Date.now() };
|
|
1018
|
-
} else {
|
|
1019
|
-
arr.push({ from: 'assistant', text, timestamp: Date.now() });
|
|
1020
|
-
}
|
|
1021
|
-
msgs[conversationId] = arr.slice(-200);
|
|
1022
|
-
const tools = { ...s.conversationActiveTools };
|
|
1023
|
-
delete tools[conversationId];
|
|
1024
|
-
return { conversationMessages: msgs, streamingConversationId: conversationId, conversationActiveTools: tools };
|
|
1025
|
-
});
|
|
1026
|
-
break;
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
case 'conversation:complete': {
|
|
1030
|
-
const { conversationId } = msg.data || msg;
|
|
1031
|
-
if (conversationId && get().streamingConversationId === conversationId) {
|
|
1032
|
-
const tools = { ...get().conversationActiveTools };
|
|
1033
|
-
delete tools[conversationId];
|
|
1034
|
-
set({ sendingMessage: false, streamingConversationId: null, conversationActiveTools: tools });
|
|
1035
|
-
}
|
|
1036
|
-
if (conversationId) persistJSON('groove:conversationMessages', get().conversationMessages);
|
|
1037
|
-
break;
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
|
-
case 'conversation:image': {
|
|
1041
|
-
const { conversationId, prompt, url, b64_json, mimeType, model: imgModel, provider: imgProvider } = msg.data || msg;
|
|
1042
|
-
if (!conversationId) break;
|
|
1043
|
-
const imageUrl = url || (b64_json ? `data:${mimeType || 'image/png'};base64,${b64_json}` : null);
|
|
1044
|
-
set((s) => {
|
|
1045
|
-
const msgs = { ...s.conversationMessages };
|
|
1046
|
-
if (!msgs[conversationId]) msgs[conversationId] = [];
|
|
1047
|
-
const arr = [...msgs[conversationId]];
|
|
1048
|
-
const loadingIdx = arr.findLastIndex((m) => m.type === 'image-loading' && m.prompt === prompt);
|
|
1049
|
-
if (loadingIdx >= 0) {
|
|
1050
|
-
arr[loadingIdx] = { from: 'assistant', type: 'image', imageUrl, prompt, model: imgModel, provider: imgProvider, timestamp: Date.now() };
|
|
1051
|
-
} else {
|
|
1052
|
-
arr.push({ from: 'assistant', type: 'image', imageUrl, prompt, model: imgModel, provider: imgProvider, timestamp: Date.now() });
|
|
1053
|
-
}
|
|
1054
|
-
msgs[conversationId] = arr.slice(-200);
|
|
1055
|
-
persistJSON('groove:conversationMessages', msgs);
|
|
1056
|
-
const isActive = s.streamingConversationId === conversationId;
|
|
1057
|
-
return { conversationMessages: msgs, sendingMessage: isActive ? false : s.sendingMessage, streamingConversationId: isActive ? null : s.streamingConversationId };
|
|
1058
|
-
});
|
|
1059
|
-
break;
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
case 'conversation:image-progress': {
|
|
1063
|
-
const { conversationId, status, prompt: imgPrompt, error: imgError } = msg.data || msg;
|
|
1064
|
-
if (!conversationId) break;
|
|
1065
|
-
if (status === 'generating') {
|
|
1066
|
-
set((s) => {
|
|
1067
|
-
const msgs = { ...s.conversationMessages };
|
|
1068
|
-
if (!msgs[conversationId]) msgs[conversationId] = [];
|
|
1069
|
-
msgs[conversationId] = [...msgs[conversationId], { from: 'assistant', type: 'image-loading', prompt: imgPrompt, timestamp: Date.now() }];
|
|
1070
|
-
return { conversationMessages: msgs, streamingConversationId: conversationId };
|
|
1071
|
-
});
|
|
1072
|
-
} else if (status === 'error') {
|
|
1073
|
-
set((s) => {
|
|
1074
|
-
const msgs = { ...s.conversationMessages };
|
|
1075
|
-
if (!msgs[conversationId]) msgs[conversationId] = [];
|
|
1076
|
-
const arr = [...msgs[conversationId]];
|
|
1077
|
-
const loadingIdx = arr.findLastIndex((m) => m.type === 'image-loading');
|
|
1078
|
-
if (loadingIdx >= 0) arr.splice(loadingIdx, 1);
|
|
1079
|
-
arr.push({ from: 'system', text: `Image generation failed: ${imgError || 'Unknown error'}`, timestamp: Date.now() });
|
|
1080
|
-
msgs[conversationId] = arr;
|
|
1081
|
-
persistJSON('groove:conversationMessages', msgs);
|
|
1082
|
-
const isActive = s.streamingConversationId === conversationId;
|
|
1083
|
-
return { conversationMessages: msgs, sendingMessage: isActive ? false : s.sendingMessage, streamingConversationId: isActive ? null : s.streamingConversationId };
|
|
1084
|
-
});
|
|
1085
|
-
}
|
|
1086
|
-
break;
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
case 'conversation:error': {
|
|
1090
|
-
const { conversationId, error } = msg.data || msg;
|
|
1091
|
-
if (conversationId) {
|
|
1092
|
-
set((s) => {
|
|
1093
|
-
const msgs = { ...s.conversationMessages };
|
|
1094
|
-
if (!msgs[conversationId]) msgs[conversationId] = [];
|
|
1095
|
-
msgs[conversationId] = [...msgs[conversationId], { from: 'system', text: `Error: ${error || 'Unknown error'}`, timestamp: Date.now() }];
|
|
1096
|
-
persistJSON('groove:conversationMessages', msgs);
|
|
1097
|
-
const isActive = s.streamingConversationId === conversationId;
|
|
1098
|
-
return { conversationMessages: msgs, sendingMessage: isActive ? false : s.sendingMessage, streamingConversationId: isActive ? null : s.streamingConversationId };
|
|
1099
|
-
});
|
|
1100
|
-
}
|
|
1101
|
-
break;
|
|
1102
|
-
}
|
|
1103
|
-
|
|
1104
965
|
case 'network:token:timing': {
|
|
1105
966
|
const { __proto__: _a, constructor: _b, prototype: _c, ...td } = msg.data || {};
|
|
1106
967
|
const updates = {
|
|
@@ -89,6 +89,7 @@ export const createAgentsSlice = (set, get) => ({
|
|
|
89
89
|
persistJSON('groove:activityLog', activityLog);
|
|
90
90
|
return { chatHistory, activityLog, tokenTimeline };
|
|
91
91
|
});
|
|
92
|
+
api.delete(`/chat-history/${encodeURIComponent(id)}`).catch(() => {});
|
|
92
93
|
}
|
|
93
94
|
} catch (err) {
|
|
94
95
|
get().addToast('error', 'Kill failed', err.message);
|
|
@@ -166,15 +167,64 @@ export const createAgentsSlice = (set, get) => ({
|
|
|
166
167
|
// ── Chat ──────────────────────────────────────────────────
|
|
167
168
|
|
|
168
169
|
addChatMessage(agentId, from, text, isQuery = false, attachments = undefined) {
|
|
170
|
+
const msg = { from, text, timestamp: Date.now(), isQuery };
|
|
171
|
+
if (attachments?.length) msg.attachments = attachments;
|
|
169
172
|
set((s) => {
|
|
170
173
|
const history = { ...s.chatHistory };
|
|
171
174
|
if (!history[agentId]) history[agentId] = [];
|
|
172
|
-
const msg = { from, text, timestamp: Date.now(), isQuery };
|
|
173
|
-
if (attachments?.length) msg.attachments = attachments;
|
|
174
175
|
history[agentId] = [...history[agentId].slice(-100), msg];
|
|
175
176
|
persistChatHistory(history);
|
|
176
177
|
return { chatHistory: history };
|
|
177
178
|
});
|
|
179
|
+
get().persistMessageRemote(agentId, msg);
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
// Persist one message to the daemon so history survives on the server, not
|
|
183
|
+
// just in this browser's per-origin localStorage. Attachment payloads are
|
|
184
|
+
// stripped; the daemon keeps metadata only.
|
|
185
|
+
persistMessageRemote(agentId, msg) {
|
|
186
|
+
if (!agentId || !msg) return;
|
|
187
|
+
const clean = msg.attachments?.length
|
|
188
|
+
? { ...msg, attachments: msg.attachments.map(({ dataUrl, ...rest }) => rest) }
|
|
189
|
+
: msg;
|
|
190
|
+
api.post(`/chat-history/${encodeURIComponent(agentId)}`, { message: clean }).catch(() => {});
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
// Load server-side history on (re)connect and merge it in — the daemon is the
|
|
194
|
+
// source of truth, so this restores chats regardless of which tunnel port
|
|
195
|
+
// (and thus browser origin) we came up on.
|
|
196
|
+
//
|
|
197
|
+
// Also runs a ONE-TIME, per-origin seed: whatever chat history is stranded in
|
|
198
|
+
// THIS origin's localStorage gets pushed to the daemon for any agent the
|
|
199
|
+
// daemon doesn't yet know. Visiting each old origin once migrates its history
|
|
200
|
+
// server-side without clobbering anything already there.
|
|
201
|
+
async fetchChatHistory() {
|
|
202
|
+
try {
|
|
203
|
+
const { history } = await api.get('/chat-history');
|
|
204
|
+
const remote = (history && typeof history === 'object') ? history : {};
|
|
205
|
+
|
|
206
|
+
if (localStorage.getItem('groove:chatImported') !== '1') {
|
|
207
|
+
const local = get().chatHistory;
|
|
208
|
+
for (const [agentId, msgs] of Object.entries(local)) {
|
|
209
|
+
// Only seed agents the daemon has nothing for — never overwrite
|
|
210
|
+
// server history with an older local copy.
|
|
211
|
+
if (Array.isArray(msgs) && msgs.length && !(remote[agentId]?.length)) {
|
|
212
|
+
api.put(`/chat-history/${encodeURIComponent(agentId)}`, { messages: msgs }).catch(() => {});
|
|
213
|
+
remote[agentId] = msgs; // reflect so the merge below keeps it
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
localStorage.setItem('groove:chatImported', '1');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
set((s) => {
|
|
220
|
+
const merged = { ...s.chatHistory };
|
|
221
|
+
for (const [agentId, msgs] of Object.entries(remote)) {
|
|
222
|
+
if (Array.isArray(msgs) && msgs.length) merged[agentId] = msgs;
|
|
223
|
+
}
|
|
224
|
+
persistChatHistory(merged);
|
|
225
|
+
return { chatHistory: merged };
|
|
226
|
+
});
|
|
227
|
+
} catch { /* offline or old daemon without the endpoint — keep local */ }
|
|
178
228
|
},
|
|
179
229
|
|
|
180
230
|
async stopAgent(id) {
|