groove-dev 0.27.15 → 0.27.18
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/CLAUDE.md +0 -10
- package/README.md +37 -1
- package/developerID_application.cer +0 -0
- package/node_modules/@groove-dev/daemon/src/api.js +586 -67
- package/node_modules/@groove-dev/daemon/src/classifier.js +24 -0
- package/node_modules/@groove-dev/daemon/src/credentials.js +12 -2
- package/node_modules/@groove-dev/daemon/src/federation/ambassador.js +204 -0
- package/node_modules/@groove-dev/daemon/src/federation/connection.js +359 -0
- package/node_modules/@groove-dev/daemon/src/federation/contracts.js +112 -0
- package/node_modules/@groove-dev/daemon/src/federation/whitelist.js +190 -0
- package/node_modules/@groove-dev/daemon/src/federation.js +166 -7
- package/node_modules/@groove-dev/daemon/src/index.js +172 -19
- package/node_modules/@groove-dev/daemon/src/introducer.js +52 -7
- package/node_modules/@groove-dev/daemon/src/journalist.js +46 -1
- package/node_modules/@groove-dev/daemon/src/memory.js +36 -16
- package/node_modules/@groove-dev/daemon/src/process.js +140 -23
- package/node_modules/@groove-dev/daemon/src/providers/base.js +1 -0
- package/node_modules/@groove-dev/daemon/src/providers/claude-code.js +14 -0
- package/node_modules/@groove-dev/daemon/src/providers/codex.js +124 -28
- package/node_modules/@groove-dev/daemon/src/providers/gemini.js +104 -17
- package/node_modules/@groove-dev/daemon/src/providers/index.js +17 -0
- package/node_modules/@groove-dev/daemon/src/registry.js +10 -1
- package/node_modules/@groove-dev/daemon/src/rotator.js +93 -30
- package/node_modules/@groove-dev/daemon/src/skills.js +33 -3
- package/node_modules/@groove-dev/daemon/src/terminal-pty.js +9 -1
- package/node_modules/@groove-dev/daemon/src/tool-executor.js +11 -5
- package/node_modules/@groove-dev/daemon/src/toys.js +69 -0
- package/node_modules/@groove-dev/daemon/src/tunnel-manager.js +24 -5
- package/node_modules/@groove-dev/daemon/templates/toys-catalog.json +242 -0
- package/node_modules/@groove-dev/daemon/test/classifier.test.js +98 -0
- package/node_modules/@groove-dev/daemon/test/introducer.test.js +72 -1
- package/node_modules/@groove-dev/daemon/test/journalist.test.js +117 -0
- package/node_modules/@groove-dev/daemon/test/memory.test.js +37 -1
- package/node_modules/@groove-dev/daemon/test/rotator.test.js +183 -4
- package/node_modules/@groove-dev/gui/dist/assets/index-Bg6_D2xK.css +1 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-D3rvwTHD.js +8607 -0
- package/node_modules/@groove-dev/gui/dist/index.html +3 -2
- package/node_modules/@groove-dev/gui/index.html +1 -0
- package/node_modules/@groove-dev/gui/src/app.css +7 -0
- package/node_modules/@groove-dev/gui/src/app.jsx +37 -10
- package/node_modules/@groove-dev/gui/src/components/agents/agent-chat.jsx +21 -31
- package/node_modules/@groove-dev/gui/src/components/agents/agent-config.jsx +11 -6
- package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +2 -2
- package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +42 -1
- package/node_modules/@groove-dev/gui/src/components/editor/breadcrumbs.jsx +30 -0
- package/node_modules/@groove-dev/gui/src/components/editor/code-editor.jsx +33 -2
- package/node_modules/@groove-dev/gui/src/components/editor/editor-status-bar.jsx +26 -0
- package/node_modules/@groove-dev/gui/src/components/editor/editor-tabs.jsx +113 -34
- package/node_modules/@groove-dev/gui/src/components/editor/goto-line.jsx +35 -0
- package/node_modules/@groove-dev/gui/src/components/editor/terminal.jsx +12 -6
- package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +15 -3
- package/node_modules/@groove-dev/gui/src/components/layout/app-shell.jsx +0 -1
- package/node_modules/@groove-dev/gui/src/components/layout/breadcrumb-bar.jsx +165 -47
- package/node_modules/@groove-dev/gui/src/components/layout/command-palette.jsx +6 -2
- package/node_modules/@groove-dev/gui/src/components/layout/status-bar.jsx +11 -1
- package/node_modules/@groove-dev/gui/src/components/layout/terminal-panel.jsx +10 -9
- package/node_modules/@groove-dev/gui/src/components/marketplace/repo-import.jsx +9 -1
- package/node_modules/@groove-dev/gui/src/components/onboarding/provider-card.jsx +134 -0
- package/node_modules/@groove-dev/gui/src/components/onboarding/setup-wizard.jsx +819 -0
- package/node_modules/@groove-dev/gui/src/components/pro/pro-gate.jsx +12 -5
- package/node_modules/@groove-dev/gui/src/components/pro/upgrade-card.jsx +15 -8
- package/node_modules/@groove-dev/gui/src/components/pro/upgrade-modal.jsx +151 -0
- package/node_modules/@groove-dev/gui/src/components/settings/federation-activity.jsx +98 -0
- package/node_modules/@groove-dev/gui/src/components/settings/federation-panel.jsx +290 -0
- package/node_modules/@groove-dev/gui/src/components/settings/federation-peers.jsx +126 -0
- package/node_modules/@groove-dev/gui/src/components/settings/federation-wizard.jsx +293 -0
- package/node_modules/@groove-dev/gui/src/components/settings/quick-connect.jsx +110 -67
- package/node_modules/@groove-dev/gui/src/components/settings/remote-server-card.jsx +3 -3
- package/node_modules/@groove-dev/gui/src/components/settings/server-detail.jsx +310 -0
- package/node_modules/@groove-dev/gui/src/components/settings/server-dialog.jsx +4 -1
- package/node_modules/@groove-dev/gui/src/components/settings/server-list.jsx +59 -0
- package/node_modules/@groove-dev/gui/src/components/settings/ssh-wizard.jsx +549 -0
- package/node_modules/@groove-dev/gui/src/components/toys/toy-card.jsx +78 -0
- package/node_modules/@groove-dev/gui/src/components/toys/toy-creator.jsx +144 -0
- package/node_modules/@groove-dev/gui/src/components/toys/toy-launcher.jsx +187 -0
- package/node_modules/@groove-dev/gui/src/components/ui/toast.jsx +2 -2
- package/node_modules/@groove-dev/gui/src/lib/electron.js +15 -0
- package/node_modules/@groove-dev/gui/src/lib/format.js +1 -0
- package/node_modules/@groove-dev/gui/src/stores/groove.js +388 -63
- package/node_modules/@groove-dev/gui/src/views/agents.jsx +148 -42
- package/node_modules/@groove-dev/gui/src/views/editor.jsx +92 -2
- package/node_modules/@groove-dev/gui/src/views/federation.jsx +37 -0
- package/node_modules/@groove-dev/gui/src/views/marketplace.jsx +2 -42
- package/node_modules/@groove-dev/gui/src/views/settings.jsx +35 -134
- package/node_modules/@groove-dev/gui/src/views/subscription-panel.jsx +327 -0
- package/node_modules/@groove-dev/gui/src/views/teams.jsx +3 -3
- package/node_modules/@groove-dev/gui/src/views/toys.jsx +162 -0
- package/package.json +1 -1
- package/packages/daemon/src/api.js +586 -67
- package/packages/daemon/src/classifier.js +24 -0
- package/packages/daemon/src/credentials.js +12 -2
- package/packages/daemon/src/federation/ambassador.js +204 -0
- package/packages/daemon/src/federation/connection.js +359 -0
- package/packages/daemon/src/federation/contracts.js +112 -0
- package/packages/daemon/src/federation/whitelist.js +190 -0
- package/packages/daemon/src/federation.js +166 -7
- package/packages/daemon/src/index.js +172 -19
- package/packages/daemon/src/introducer.js +52 -7
- package/packages/daemon/src/journalist.js +46 -1
- package/packages/daemon/src/memory.js +36 -16
- package/packages/daemon/src/process.js +140 -23
- package/packages/daemon/src/providers/base.js +1 -0
- package/packages/daemon/src/providers/claude-code.js +14 -0
- package/packages/daemon/src/providers/codex.js +124 -28
- package/packages/daemon/src/providers/gemini.js +104 -17
- package/packages/daemon/src/providers/index.js +17 -0
- package/packages/daemon/src/registry.js +10 -1
- package/packages/daemon/src/rotator.js +93 -30
- package/packages/daemon/src/skills.js +33 -3
- package/packages/daemon/src/terminal-pty.js +9 -1
- package/packages/daemon/src/tool-executor.js +11 -5
- package/packages/daemon/src/toys.js +69 -0
- package/packages/daemon/src/tunnel-manager.js +24 -5
- package/packages/daemon/templates/toys-catalog.json +242 -0
- package/packages/gui/dist/assets/index-Bg6_D2xK.css +1 -0
- package/packages/gui/dist/assets/index-D3rvwTHD.js +8607 -0
- package/packages/gui/dist/index.html +3 -2
- package/packages/gui/index.html +1 -0
- package/packages/gui/src/app.css +7 -0
- package/packages/gui/src/app.jsx +37 -10
- package/packages/gui/src/components/agents/agent-chat.jsx +21 -31
- package/packages/gui/src/components/agents/agent-config.jsx +11 -6
- package/packages/gui/src/components/agents/agent-feed.jsx +2 -2
- package/packages/gui/src/components/agents/spawn-wizard.jsx +42 -1
- package/packages/gui/src/components/editor/breadcrumbs.jsx +30 -0
- package/packages/gui/src/components/editor/code-editor.jsx +33 -2
- package/packages/gui/src/components/editor/editor-status-bar.jsx +26 -0
- package/packages/gui/src/components/editor/editor-tabs.jsx +113 -34
- package/packages/gui/src/components/editor/goto-line.jsx +35 -0
- package/packages/gui/src/components/editor/terminal.jsx +12 -6
- package/packages/gui/src/components/layout/activity-bar.jsx +15 -3
- package/packages/gui/src/components/layout/app-shell.jsx +0 -1
- package/packages/gui/src/components/layout/breadcrumb-bar.jsx +165 -47
- package/packages/gui/src/components/layout/command-palette.jsx +6 -2
- package/packages/gui/src/components/layout/status-bar.jsx +11 -1
- package/packages/gui/src/components/layout/terminal-panel.jsx +10 -9
- package/packages/gui/src/components/marketplace/repo-import.jsx +9 -1
- package/packages/gui/src/components/onboarding/provider-card.jsx +134 -0
- package/packages/gui/src/components/onboarding/setup-wizard.jsx +819 -0
- package/packages/gui/src/components/pro/pro-gate.jsx +12 -5
- package/packages/gui/src/components/pro/upgrade-card.jsx +15 -8
- package/packages/gui/src/components/pro/upgrade-modal.jsx +151 -0
- package/packages/gui/src/components/settings/federation-activity.jsx +98 -0
- package/packages/gui/src/components/settings/federation-panel.jsx +290 -0
- package/packages/gui/src/components/settings/federation-peers.jsx +126 -0
- package/packages/gui/src/components/settings/federation-wizard.jsx +293 -0
- package/packages/gui/src/components/settings/quick-connect.jsx +110 -67
- package/packages/gui/src/components/settings/remote-server-card.jsx +3 -3
- package/packages/gui/src/components/settings/server-detail.jsx +310 -0
- package/packages/gui/src/components/settings/server-dialog.jsx +4 -1
- package/packages/gui/src/components/settings/server-list.jsx +59 -0
- package/packages/gui/src/components/settings/ssh-wizard.jsx +549 -0
- package/packages/gui/src/components/toys/toy-card.jsx +78 -0
- package/packages/gui/src/components/toys/toy-creator.jsx +144 -0
- package/packages/gui/src/components/toys/toy-launcher.jsx +187 -0
- package/packages/gui/src/components/ui/toast.jsx +2 -2
- package/packages/gui/src/lib/electron.js +15 -0
- package/packages/gui/src/lib/format.js +1 -0
- package/packages/gui/src/stores/groove.js +388 -63
- package/packages/gui/src/views/agents.jsx +148 -42
- package/packages/gui/src/views/editor.jsx +92 -2
- package/packages/gui/src/views/federation.jsx +37 -0
- package/packages/gui/src/views/marketplace.jsx +2 -42
- package/packages/gui/src/views/settings.jsx +35 -134
- package/packages/gui/src/views/subscription-panel.jsx +327 -0
- package/packages/gui/src/views/teams.jsx +3 -3
- package/packages/gui/src/views/toys.jsx +162 -0
- package/plans/chat-persistence-refactor.md +154 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-BE6lYcd7.css +0 -1
- package/node_modules/@groove-dev/gui/dist/assets/index-zdzOLAZM.js +0 -677
- package/packages/gui/dist/assets/index-BE6lYcd7.css +0 -1
- package/packages/gui/dist/assets/index-zdzOLAZM.js +0 -677
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; connect-src 'self' ws://localhost:* ws://127.0.0.1:* http://localhost:* http://127.0.0.1:*; font-src 'self' data:; object-src 'none'; base-uri 'self'; frame-ancestors 'none';">
|
|
5
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
7
|
<link rel="icon" type="image/png" href="/favicon.png" />
|
|
7
8
|
<title>Groove GUI</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-D3rvwTHD.js"></script>
|
|
9
10
|
<link rel="modulepreload" crossorigin href="/assets/vendor-C0HXlhrU.js">
|
|
10
11
|
<link rel="modulepreload" crossorigin href="/assets/reactflow-BQPfi37R.js">
|
|
11
12
|
<link rel="modulepreload" crossorigin href="/assets/codemirror-BBL3i_JW.js">
|
|
12
13
|
<link rel="modulepreload" crossorigin href="/assets/xterm--7_ns2zW.js">
|
|
13
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
14
|
+
<link rel="stylesheet" crossorigin href="/assets/index-Bg6_D2xK.css">
|
|
14
15
|
</head>
|
|
15
16
|
<body>
|
|
16
17
|
<div id="root"></div>
|
package/packages/gui/index.html
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; connect-src 'self' ws://localhost:* ws://127.0.0.1:* http://localhost:* http://127.0.0.1:*; font-src 'self' data:; object-src 'none'; base-uri 'self'; frame-ancestors 'none';">
|
|
5
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
7
|
<link rel="icon" type="image/png" href="/favicon.png" />
|
|
7
8
|
<title>Groove GUI</title>
|
package/packages/gui/src/app.css
CHANGED
|
@@ -201,6 +201,13 @@ html {
|
|
|
201
201
|
filter: drop-shadow(0 0 4px rgba(97, 175, 239, 0.4));
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
+
/* ── Onboarding Wizard ───────────────────────────────────── */
|
|
205
|
+
|
|
206
|
+
@keyframes wizard-checkmark {
|
|
207
|
+
0% { stroke-dashoffset: 24; }
|
|
208
|
+
100% { stroke-dashoffset: 0; }
|
|
209
|
+
}
|
|
210
|
+
|
|
204
211
|
/* ── Electron Desktop App ────────────────────────────────── */
|
|
205
212
|
|
|
206
213
|
.electron-drag {
|
package/packages/gui/src/app.jsx
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
// GROOVE GUI v2 — App Root
|
|
2
2
|
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
3
3
|
|
|
4
|
-
import React, { useEffect } from 'react';
|
|
4
|
+
import React, { useEffect, useMemo } from 'react';
|
|
5
5
|
import { useGrooveStore } from './stores/groove';
|
|
6
6
|
import { AppShell } from './components/layout/app-shell';
|
|
7
|
+
import { SetupWizard } from './components/onboarding/setup-wizard';
|
|
8
|
+
import { useKeyboard } from './lib/hooks/use-keyboard';
|
|
9
|
+
import { UpgradeModal } from './components/pro/upgrade-modal';
|
|
7
10
|
|
|
8
11
|
// Views
|
|
9
12
|
import AgentsView from './views/agents';
|
|
@@ -13,6 +16,8 @@ import MarketplaceView from './views/marketplace';
|
|
|
13
16
|
import TeamsView from './views/teams';
|
|
14
17
|
import SettingsView from './views/settings';
|
|
15
18
|
import ModelsView from './views/models';
|
|
19
|
+
import FederationView from './views/federation';
|
|
20
|
+
import ToysView from './views/toys';
|
|
16
21
|
|
|
17
22
|
// Agent components
|
|
18
23
|
import { AgentPanel } from './components/agents/agent-panel';
|
|
@@ -57,8 +62,10 @@ function ViewRouter() {
|
|
|
57
62
|
case 'editor': content = <EditorView />; break;
|
|
58
63
|
case 'dashboard': content = <DashboardView />; break;
|
|
59
64
|
case 'marketplace': content = <MarketplaceView />; break;
|
|
65
|
+
case 'toys': content = <ToysView />; break;
|
|
60
66
|
case 'teams': content = <TeamsView />; break;
|
|
61
67
|
case 'models': content = <ModelsView />; break;
|
|
68
|
+
case 'federation': content = <FederationView />; break;
|
|
62
69
|
case 'settings': content = <SettingsView />; break;
|
|
63
70
|
default: content = <AgentsView />;
|
|
64
71
|
}
|
|
@@ -100,25 +107,45 @@ export default function App() {
|
|
|
100
107
|
const connect = useGrooveStore((s) => s.connect);
|
|
101
108
|
const hydrated = useGrooveStore((s) => s.hydrated);
|
|
102
109
|
const tunneled = useGrooveStore((s) => s.tunneled);
|
|
110
|
+
const onboardingComplete = useGrooveStore((s) => s.onboardingComplete);
|
|
103
111
|
useEffect(() => { connect(); }, [connect]);
|
|
104
112
|
|
|
105
113
|
useEffect(() => {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
+
async function setTitle() {
|
|
115
|
+
if (window.groove?.getInstanceInfo) {
|
|
116
|
+
const info = await window.groove.getInstanceInfo();
|
|
117
|
+
if (info?.name) {
|
|
118
|
+
document.title = `${info.name} — Groove`;
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const params = new URLSearchParams(window.location.search);
|
|
123
|
+
const instance = params.get('instance');
|
|
124
|
+
if (instance) {
|
|
125
|
+
const sanitized = instance.replace(/[\x00-\x1F]/g, '').slice(0, 50);
|
|
126
|
+
document.title = `${sanitized} — Groove`;
|
|
127
|
+
} else if (tunneled) {
|
|
128
|
+
document.title = 'Remote — Groove';
|
|
129
|
+
} else {
|
|
130
|
+
document.title = 'Groove';
|
|
131
|
+
}
|
|
114
132
|
}
|
|
133
|
+
setTitle();
|
|
115
134
|
}, [tunneled]);
|
|
116
135
|
|
|
136
|
+
const openFolderShortcuts = useMemo(() =>
|
|
137
|
+
window.groove?.openFolder
|
|
138
|
+
? [{ key: 'o', meta: true, handler: () => window.groove.openFolder() }]
|
|
139
|
+
: [],
|
|
140
|
+
[]);
|
|
141
|
+
useKeyboard(openFolderShortcuts);
|
|
142
|
+
|
|
117
143
|
if (!hydrated) return <LoadingScreen />;
|
|
118
144
|
|
|
119
145
|
return (
|
|
120
146
|
<ErrorBoundary>
|
|
121
|
-
<ViewRouter />
|
|
147
|
+
{onboardingComplete ? <ViewRouter /> : <SetupWizard />}
|
|
148
|
+
<UpgradeModal />
|
|
122
149
|
</ErrorBoundary>
|
|
123
150
|
);
|
|
124
151
|
}
|
|
@@ -105,7 +105,6 @@ export function AgentChat({ agent }) {
|
|
|
105
105
|
const chatHistory = useGrooveStore((s) => s.chatHistory[agent.id]) || EMPTY;
|
|
106
106
|
const activityLog = useGrooveStore((s) => s.activityLog[agent.id]) || EMPTY;
|
|
107
107
|
const instructAgent = useGrooveStore((s) => s.instructAgent);
|
|
108
|
-
const queryAgent = useGrooveStore((s) => s.queryAgent);
|
|
109
108
|
const isThinking = useGrooveStore((s) => s.thinkingAgents?.has(agent.id));
|
|
110
109
|
|
|
111
110
|
const storeInput = useGrooveStore((s) => s.chatInputs[agent.id] || '');
|
|
@@ -143,11 +142,7 @@ export function AgentChat({ agent }) {
|
|
|
143
142
|
setAttachedFiles([]);
|
|
144
143
|
setSending(true);
|
|
145
144
|
try {
|
|
146
|
-
|
|
147
|
-
await queryAgent(agent.id, text.slice(1).trim());
|
|
148
|
-
} else {
|
|
149
|
-
await instructAgent(agent.id, text);
|
|
150
|
-
}
|
|
145
|
+
await instructAgent(agent.id, text);
|
|
151
146
|
} catch { /* toast handles */ }
|
|
152
147
|
setSending(false);
|
|
153
148
|
inputRef.current?.focus();
|
|
@@ -160,7 +155,6 @@ export function AgentChat({ agent }) {
|
|
|
160
155
|
}
|
|
161
156
|
}
|
|
162
157
|
|
|
163
|
-
const isQuery = input.startsWith('?');
|
|
164
158
|
const isAlive = agent.status === 'running' || agent.status === 'starting';
|
|
165
159
|
|
|
166
160
|
// Build merged timeline
|
|
@@ -183,7 +177,7 @@ export function AgentChat({ agent }) {
|
|
|
183
177
|
</p>
|
|
184
178
|
<p className="text-xs text-text-3 font-sans mt-1 max-w-[240px]">
|
|
185
179
|
{isAlive
|
|
186
|
-
? 'Send
|
|
180
|
+
? 'Send a message to guide this agent. Use the stop button to interrupt.'
|
|
187
181
|
: 'Reply to continue the conversation — a new session starts with full context.'}
|
|
188
182
|
</p>
|
|
189
183
|
</div>
|
|
@@ -200,14 +194,11 @@ export function AgentChat({ agent }) {
|
|
|
200
194
|
<div className="border-t border-border-subtle px-4 py-3 bg-surface-1">
|
|
201
195
|
{/* Mode indicator */}
|
|
202
196
|
<div className="flex items-center gap-2 mb-2">
|
|
203
|
-
<span className=
|
|
204
|
-
|
|
205
|
-
isQuery ? 'bg-info/12 text-info' : 'bg-accent/12 text-accent',
|
|
206
|
-
)}>
|
|
207
|
-
{isQuery ? 'Query' : isAlive ? 'Instruct' : 'Continue'}
|
|
197
|
+
<span className="text-2xs font-semibold font-sans px-2 py-0.5 rounded-full bg-accent/12 text-accent">
|
|
198
|
+
{isAlive ? 'Instruct' : 'Continue'}
|
|
208
199
|
</span>
|
|
209
200
|
<span className="text-2xs text-text-4 font-sans">
|
|
210
|
-
{
|
|
201
|
+
{isAlive ? 'Message goes directly to this agent' : 'Resumes with full context'}
|
|
211
202
|
</span>
|
|
212
203
|
</div>
|
|
213
204
|
|
|
@@ -233,7 +224,7 @@ export function AgentChat({ agent }) {
|
|
|
233
224
|
value={input}
|
|
234
225
|
onChange={(e) => setInput(e.target.value)}
|
|
235
226
|
onKeyDown={onKeyDown}
|
|
236
|
-
placeholder={isAlive ? 'Instruct agent...
|
|
227
|
+
placeholder={isAlive ? 'Instruct this agent...' : 'Continue conversation...'}
|
|
237
228
|
rows={1}
|
|
238
229
|
className={cn(
|
|
239
230
|
'flex-1 resize-y rounded-xl px-4 py-2.5 text-sm',
|
|
@@ -241,10 +232,10 @@ export function AgentChat({ agent }) {
|
|
|
241
232
|
'placeholder:text-text-4',
|
|
242
233
|
'focus:outline-none focus:ring-1',
|
|
243
234
|
'min-h-[40px]',
|
|
244
|
-
|
|
235
|
+
'border-border focus:ring-accent/40',
|
|
245
236
|
)}
|
|
246
237
|
/>
|
|
247
|
-
{
|
|
238
|
+
{isAlive && (
|
|
248
239
|
<button
|
|
249
240
|
onClick={() => useGrooveStore.getState().stopAgent(agent.id)}
|
|
250
241
|
title="Stop agent"
|
|
@@ -252,21 +243,20 @@ export function AgentChat({ agent }) {
|
|
|
252
243
|
>
|
|
253
244
|
<Square size={14} fill="currentColor" />
|
|
254
245
|
</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
246
|
)}
|
|
247
|
+
<button
|
|
248
|
+
onClick={handleSend}
|
|
249
|
+
disabled={!input.trim() || sending}
|
|
250
|
+
className={cn(
|
|
251
|
+
'w-10 h-10 flex items-center justify-center rounded-xl transition-all cursor-pointer',
|
|
252
|
+
'disabled:opacity-20 disabled:cursor-not-allowed',
|
|
253
|
+
input.trim()
|
|
254
|
+
? 'bg-accent/15 text-accent hover:bg-accent/25 border border-accent/25'
|
|
255
|
+
: 'bg-surface-4 text-text-4',
|
|
256
|
+
)}
|
|
257
|
+
>
|
|
258
|
+
{sending ? <Loader2 size={16} className="animate-spin" /> : <Send size={16} />}
|
|
259
|
+
</button>
|
|
270
260
|
</div>
|
|
271
261
|
</div>
|
|
272
262
|
</div>
|
|
@@ -79,7 +79,7 @@ function AgentActions({ agent }) {
|
|
|
79
79
|
async function handleKill() {
|
|
80
80
|
if (!confirmKill) { setConfirmKill(true); setTimeout(() => setConfirmKill(false), 3000); return; }
|
|
81
81
|
setLoading('kill');
|
|
82
|
-
try { await killAgent(agent.id); closeDetail(); } catch {}
|
|
82
|
+
try { await killAgent(agent.id, !isAlive); closeDetail(); } catch {}
|
|
83
83
|
setLoading(null);
|
|
84
84
|
setConfirmKill(false);
|
|
85
85
|
}
|
|
@@ -189,13 +189,18 @@ export function AgentConfig({ agent }) {
|
|
|
189
189
|
|
|
190
190
|
useEffect(() => {
|
|
191
191
|
setPersonalityLoaded(false);
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
192
|
+
if (agent.personality) {
|
|
193
|
+
api.get(`/personalities/${agent.name}`).then((data) => {
|
|
194
|
+
setPersonalityContent(data?.content || '');
|
|
195
|
+
setPersonalityLoaded(true);
|
|
196
|
+
}).catch(() => {
|
|
197
|
+
setPersonalityContent('');
|
|
198
|
+
setPersonalityLoaded(true);
|
|
199
|
+
});
|
|
200
|
+
} else {
|
|
196
201
|
setPersonalityContent('');
|
|
197
202
|
setPersonalityLoaded(true);
|
|
198
|
-
}
|
|
203
|
+
}
|
|
199
204
|
api.get('/personalities').then((data) => {
|
|
200
205
|
setPersonalities(Array.isArray(data) ? data : data.personalities || []);
|
|
201
206
|
}).catch(() => {});
|
|
@@ -755,8 +755,8 @@ export function AgentFeed({ agent }) {
|
|
|
755
755
|
'disabled:opacity-15 disabled:cursor-not-allowed',
|
|
756
756
|
input.trim()
|
|
757
757
|
? mode === 'query'
|
|
758
|
-
? 'bg-info text-
|
|
759
|
-
: 'bg-accent text-
|
|
758
|
+
? 'bg-info/15 text-info hover:bg-info/25 border border-info/25'
|
|
759
|
+
: 'bg-accent/15 text-accent hover:bg-accent/25 border border-accent/25'
|
|
760
760
|
: 'bg-transparent text-text-4',
|
|
761
761
|
)}
|
|
762
762
|
>
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
Server, Monitor, Code2, TestTube, Cloud, FileText,
|
|
12
12
|
Shield, Database, Megaphone, Calculator, UserCheck,
|
|
13
13
|
Headphones, BarChart3, Rocket, ChevronDown, Pen, Presentation,
|
|
14
|
-
Sparkles, X, Search, AlertTriangle, Plug, MessageCircle, GitBranch,
|
|
14
|
+
Sparkles, X, Search, AlertTriangle, Plug, MessageCircle, GitBranch, Globe,
|
|
15
15
|
} from 'lucide-react';
|
|
16
16
|
import { api } from '../../lib/api';
|
|
17
17
|
import { Dialog, DialogContent } from '../ui/dialog';
|
|
@@ -70,6 +70,7 @@ const ROLE_PRESETS = [
|
|
|
70
70
|
{ id: 'analyst', label: 'Analyst', desc: 'Data analysis, insights', icon: BarChart3, tier: 'Medium' },
|
|
71
71
|
{ id: 'creative', label: 'Writer', desc: 'Copy, articles, proposals', icon: Pen, tier: 'Heavy', skillHint: true },
|
|
72
72
|
{ id: 'slides', label: 'Slides', desc: 'Pitch decks, presentations', icon: Presentation, tier: 'Heavy', skillHint: true },
|
|
73
|
+
{ id: 'ambassador', label: 'Ambassador', desc: 'Bridge to federated server', icon: Globe, tier: 'Light' },
|
|
73
74
|
];
|
|
74
75
|
|
|
75
76
|
function CheckMark() {
|
|
@@ -112,6 +113,8 @@ export function SpawnWizard() {
|
|
|
112
113
|
const [selectedPersonality, setSelectedPersonality] = useState('');
|
|
113
114
|
const [showAdvanced, setShowAdvanced] = useState(false);
|
|
114
115
|
const [spawning, setSpawning] = useState(false);
|
|
116
|
+
const [selectedPeerId, setSelectedPeerId] = useState('');
|
|
117
|
+
const federation = useGrooveStore((s) => s.federation);
|
|
115
118
|
|
|
116
119
|
useEffect(() => {
|
|
117
120
|
if (open) {
|
|
@@ -144,6 +147,7 @@ export function SpawnWizard() {
|
|
|
144
147
|
setIntegrationApproval('manual');
|
|
145
148
|
setSelectedRepos([]);
|
|
146
149
|
setSelectedPersonality('');
|
|
150
|
+
setSelectedPeerId('');
|
|
147
151
|
setShowAdvanced(false);
|
|
148
152
|
}
|
|
149
153
|
}, [open, fetchProviders]);
|
|
@@ -168,6 +172,7 @@ export function SpawnWizard() {
|
|
|
168
172
|
...(selectedIntegrations.length > 0 && { integrationApproval }),
|
|
169
173
|
...(selectedRepos.length > 0 && { repos: selectedRepos }),
|
|
170
174
|
...(selectedPersonality && { personality: selectedPersonality }),
|
|
175
|
+
...(selectedRole === 'ambassador' && selectedPeerId && { peerId: selectedPeerId }),
|
|
171
176
|
};
|
|
172
177
|
await spawnAgent(config);
|
|
173
178
|
closeDetail();
|
|
@@ -232,6 +237,42 @@ export function SpawnWizard() {
|
|
|
232
237
|
</div>
|
|
233
238
|
</div>
|
|
234
239
|
|
|
240
|
+
{/* Ambassador server picker */}
|
|
241
|
+
{selectedRole === 'ambassador' && (() => {
|
|
242
|
+
const eligible = federation.whitelist.filter((e) => typeof e === 'object' && (e.status === 'mutual' || e.status === 'connected'));
|
|
243
|
+
if (eligible.length === 0) {
|
|
244
|
+
return (
|
|
245
|
+
<div className="rounded-lg border border-dashed border-border-subtle bg-surface-1/50 px-4 py-4 text-center">
|
|
246
|
+
<Globe size={18} className="text-text-4 mx-auto mb-1.5" />
|
|
247
|
+
<p className="text-2xs text-text-3 font-sans mb-2">No federated servers connected. Add one in the Federation view.</p>
|
|
248
|
+
<Button variant="ghost" size="sm" className="text-2xs text-accent" onClick={() => { closeDetail(); useGrooveStore.getState().setActiveView('federation'); }}>
|
|
249
|
+
Go to Federation
|
|
250
|
+
</Button>
|
|
251
|
+
</div>
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
return (
|
|
255
|
+
<div>
|
|
256
|
+
<label className="text-xs font-semibold text-text-2 font-sans uppercase tracking-wider block mb-2">
|
|
257
|
+
Target Server
|
|
258
|
+
</label>
|
|
259
|
+
<div className="relative">
|
|
260
|
+
<select
|
|
261
|
+
value={selectedPeerId}
|
|
262
|
+
onChange={(e) => setSelectedPeerId(e.target.value)}
|
|
263
|
+
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"
|
|
264
|
+
>
|
|
265
|
+
<option value="">Select a server...</option>
|
|
266
|
+
{eligible.map((e) => (
|
|
267
|
+
<option key={e.ip} value={e.ip}>{e.name || `${e.ip}:${e.port || 31415}`}</option>
|
|
268
|
+
))}
|
|
269
|
+
</select>
|
|
270
|
+
<ChevronDown size={14} className="absolute right-2 top-1/2 -translate-y-1/2 text-text-3 pointer-events-none" />
|
|
271
|
+
</div>
|
|
272
|
+
</div>
|
|
273
|
+
);
|
|
274
|
+
})()}
|
|
275
|
+
|
|
235
276
|
{/* Section 2: Configuration */}
|
|
236
277
|
{selectedRole && (
|
|
237
278
|
<div className="space-y-4">
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
|
+
import { ChevronRight } from 'lucide-react';
|
|
3
|
+
|
|
4
|
+
export function Breadcrumbs({ path }) {
|
|
5
|
+
if (!path) return null;
|
|
6
|
+
|
|
7
|
+
const segments = path.split('/').filter(Boolean);
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<div className="flex items-center h-7 px-3 bg-surface-2 border-b border-border-subtle text-2xs font-sans text-text-3 overflow-hidden flex-shrink-0 select-none">
|
|
11
|
+
{segments.map((segment, i) => {
|
|
12
|
+
const isLast = i === segments.length - 1;
|
|
13
|
+
return (
|
|
14
|
+
<span key={i} className="flex items-center min-w-0">
|
|
15
|
+
{i > 0 && <ChevronRight size={10} className="mx-0.5 flex-shrink-0 text-text-4" />}
|
|
16
|
+
<span
|
|
17
|
+
className={
|
|
18
|
+
isLast
|
|
19
|
+
? 'text-text-1 font-medium truncate'
|
|
20
|
+
: 'hover:text-text-1 cursor-pointer truncate transition-colors'
|
|
21
|
+
}
|
|
22
|
+
>
|
|
23
|
+
{segment}
|
|
24
|
+
</span>
|
|
25
|
+
</span>
|
|
26
|
+
);
|
|
27
|
+
})}
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
@@ -34,16 +34,41 @@ const grooveTheme = EditorView.theme({
|
|
|
34
34
|
'.cm-activeLineGutter': { backgroundColor: '#2c313a' },
|
|
35
35
|
'.cm-activeLine': { backgroundColor: 'rgba(44, 49, 58, 0.5)' },
|
|
36
36
|
'&.cm-focused .cm-selectionBackground, .cm-selectionBackground': { backgroundColor: 'rgba(51, 175, 188, 0.15)' },
|
|
37
|
+
// Search panel styling
|
|
38
|
+
'.cm-panels': { backgroundColor: '#24282f', borderBottom: '1px solid #3e4451' },
|
|
39
|
+
'.cm-panels.cm-panels-top': { borderBottom: '1px solid #3e4451' },
|
|
40
|
+
'.cm-panels.cm-panels-bottom': { borderTop: '1px solid #3e4451' },
|
|
41
|
+
'.cm-search': { padding: '6px 8px', gap: '4px', fontFamily: 'var(--font-sans)', fontSize: '12px', display: 'flex', flexWrap: 'wrap', alignItems: 'center' },
|
|
42
|
+
'.cm-search label': { display: 'flex', alignItems: 'center', gap: '4px', color: '#8b929e', fontSize: '11px' },
|
|
43
|
+
'.cm-search input, .cm-search .cm-textfield': {
|
|
44
|
+
backgroundColor: '#1a1e25', border: '1px solid #2c313a', borderRadius: '4px', color: '#e6e6e6',
|
|
45
|
+
padding: '2px 6px', fontSize: '12px', fontFamily: 'var(--font-mono)', outline: 'none',
|
|
46
|
+
},
|
|
47
|
+
'.cm-search input:focus, .cm-search .cm-textfield:focus': { borderColor: '#33afbc' },
|
|
48
|
+
'.cm-search .cm-button, .cm-button': {
|
|
49
|
+
backgroundColor: '#2c313a', border: '1px solid #3e4451', borderRadius: '4px', color: '#bcc2cd',
|
|
50
|
+
padding: '2px 8px', fontSize: '11px', fontFamily: 'var(--font-sans)', cursor: 'pointer',
|
|
51
|
+
backgroundImage: 'none',
|
|
52
|
+
},
|
|
53
|
+
'.cm-search .cm-button:hover, .cm-button:hover': { backgroundColor: '#333842', color: '#e6e6e6' },
|
|
54
|
+
'.cm-search .cm-button:active': { backgroundColor: '#3a3f4b' },
|
|
55
|
+
'.cm-search br': { display: 'none' },
|
|
56
|
+
'.cm-panel.cm-search [name=close]': { color: '#6e7681', cursor: 'pointer', padding: '0 4px' },
|
|
57
|
+
'.cm-panel.cm-search [name=close]:hover': { color: '#e6e6e6' },
|
|
58
|
+
'.cm-searchMatch': { backgroundColor: 'rgba(51, 175, 188, 0.2)', outline: '1px solid rgba(51, 175, 188, 0.4)' },
|
|
59
|
+
'.cm-searchMatch-selected': { backgroundColor: 'rgba(51, 175, 188, 0.35)' },
|
|
37
60
|
}, { dark: true });
|
|
38
61
|
|
|
39
|
-
export function CodeEditor({ content, language, onChange, onSave }) {
|
|
62
|
+
export function CodeEditor({ content, language, onChange, onSave, onCursorChange, viewRef: externalViewRef }) {
|
|
40
63
|
const containerRef = useRef(null);
|
|
41
64
|
const viewRef = useRef(null);
|
|
42
65
|
const langCompartment = useRef(new Compartment());
|
|
43
66
|
const onChangeRef = useRef(onChange);
|
|
44
67
|
const onSaveRef = useRef(onSave);
|
|
68
|
+
const onCursorChangeRef = useRef(onCursorChange);
|
|
45
69
|
onChangeRef.current = onChange;
|
|
46
70
|
onSaveRef.current = onSave;
|
|
71
|
+
onCursorChangeRef.current = onCursorChange;
|
|
47
72
|
|
|
48
73
|
useEffect(() => {
|
|
49
74
|
if (!containerRef.current) return;
|
|
@@ -74,14 +99,20 @@ export function CodeEditor({ content, language, onChange, onSave }) {
|
|
|
74
99
|
if (update.docChanged) {
|
|
75
100
|
onChangeRef.current?.(update.state.doc.toString());
|
|
76
101
|
}
|
|
102
|
+
if (update.selectionSet || update.docChanged) {
|
|
103
|
+
const pos = update.state.selection.main.head;
|
|
104
|
+
const line = update.state.doc.lineAt(pos);
|
|
105
|
+
onCursorChangeRef.current?.({ line: line.number, col: pos - line.from + 1 });
|
|
106
|
+
}
|
|
77
107
|
}),
|
|
78
108
|
],
|
|
79
109
|
});
|
|
80
110
|
|
|
81
111
|
const view = new EditorView({ state, parent: containerRef.current });
|
|
82
112
|
viewRef.current = view;
|
|
113
|
+
if (externalViewRef) externalViewRef.current = view;
|
|
83
114
|
|
|
84
|
-
return () => { view.destroy(); viewRef.current = null; };
|
|
115
|
+
return () => { view.destroy(); viewRef.current = null; if (externalViewRef) externalViewRef.current = null; };
|
|
85
116
|
}, []); // mount once
|
|
86
117
|
|
|
87
118
|
// Update content when file changes externally
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
|
+
|
|
3
|
+
const LANG_LABELS = {
|
|
4
|
+
javascript: 'JavaScript',
|
|
5
|
+
typescript: 'TypeScript',
|
|
6
|
+
css: 'CSS',
|
|
7
|
+
html: 'HTML',
|
|
8
|
+
json: 'JSON',
|
|
9
|
+
markdown: 'Markdown',
|
|
10
|
+
python: 'Python',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export function EditorStatusBar({ cursorPos, language }) {
|
|
14
|
+
return (
|
|
15
|
+
<div className="flex items-center justify-between h-6 px-3 bg-surface-1 border-t border-border-subtle text-2xs font-sans text-text-3 flex-shrink-0 select-none">
|
|
16
|
+
<div className="flex items-center gap-3">
|
|
17
|
+
<span>Ln {cursorPos.line}, Col {cursorPos.col}</span>
|
|
18
|
+
</div>
|
|
19
|
+
<div className="flex items-center gap-3">
|
|
20
|
+
<span className="cursor-default">{LANG_LABELS[language] || language || 'Plain Text'}</span>
|
|
21
|
+
<span>Spaces: 2</span>
|
|
22
|
+
<span>UTF-8</span>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
);
|
|
26
|
+
}
|