groove-dev 0.27.39 → 0.27.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/node_modules/@groove-dev/cli/package.json +1 -1
- package/node_modules/@groove-dev/daemon/package.json +1 -1
- package/node_modules/@groove-dev/daemon/src/api.js +13 -8
- package/node_modules/@groove-dev/daemon/src/preview.js +8 -2
- package/node_modules/@groove-dev/daemon/src/rotator.js +18 -3
- package/node_modules/@groove-dev/gui/dist/assets/{index-BRZ_leqO.js → index-zzVaD3-G.js} +2 -2
- package/node_modules/@groove-dev/gui/dist/index.html +1 -1
- package/node_modules/@groove-dev/gui/package.json +1 -1
- package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +2 -2
- package/node_modules/@groove-dev/gui/src/stores/groove.js +7 -2
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/daemon/package.json +1 -1
- package/packages/daemon/src/api.js +13 -8
- package/packages/daemon/src/preview.js +8 -2
- package/packages/daemon/src/rotator.js +18 -3
- package/packages/gui/dist/assets/{index-BRZ_leqO.js → index-zzVaD3-G.js} +2 -2
- package/packages/gui/dist/index.html +1 -1
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/components/layout/activity-bar.jsx +2 -2
- package/packages/gui/src/stores/groove.js +7 -2
|
@@ -6,7 +6,7 @@
|
|
|
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-zzVaD3-G.js"></script>
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/vendor-C0HXlhrU.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/reactflow-BQPfi37R.js">
|
|
12
12
|
<link rel="modulepreload" crossorigin href="/assets/codemirror-BBL3i_JW.js">
|
|
@@ -25,9 +25,9 @@ export function ActivityBar({ activeView, detailPanel, onNavigate, onTogglePanel
|
|
|
25
25
|
|
|
26
26
|
return (
|
|
27
27
|
<nav className="w-12 flex-shrink-0 flex flex-col bg-surface-3 border-r border-border">
|
|
28
|
-
{/* Sidebar header —
|
|
28
|
+
{/* Sidebar header — no border (can't cleanly match BreadcrumbBar border due to h-9 vs h-11) */}
|
|
29
29
|
{darwinTrafficLights && (
|
|
30
|
-
<div className="flex-shrink-0 h-9 flex items-end justify-center pb-
|
|
30
|
+
<div className="flex-shrink-0 h-9 flex items-end justify-center pb-0.5">
|
|
31
31
|
<img src="/favicon.png" alt="Groove" className="h-6 w-6 rounded-full" />
|
|
32
32
|
</div>
|
|
33
33
|
)}
|
|
@@ -1035,8 +1035,13 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
1035
1035
|
thinkingAgents: new Set([...s.thinkingAgents, ...launchedAgents.map((a) => a.id)]),
|
|
1036
1036
|
}));
|
|
1037
1037
|
}
|
|
1038
|
-
// Clean up stale files
|
|
1039
|
-
|
|
1038
|
+
// Clean up stale files — scoped to the launched team so plans in other
|
|
1039
|
+
// teams' workspaces survive. The launch endpoint already unlinks the
|
|
1040
|
+
// exact plan it read; this is a belt-and-suspenders sweep.
|
|
1041
|
+
const launchedTeamId = body?.teamId || result?.teamId || null;
|
|
1042
|
+
if (launchedTeamId) {
|
|
1043
|
+
api.post('/cleanup', { teamId: launchedTeamId }).catch(() => {});
|
|
1044
|
+
}
|
|
1040
1045
|
return result;
|
|
1041
1046
|
} catch (err) {
|
|
1042
1047
|
get().addToast('error', 'Launch failed', err.message);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "groove-dev",
|
|
3
|
-
"version": "0.27.
|
|
3
|
+
"version": "0.27.40",
|
|
4
4
|
"description": "Open-source agent orchestration layer — the AI company OS. Local model agent engine (GGUF/Ollama/llama-server), HuggingFace model browser, MCP integrations (Slack, Gmail, Stripe, 15+), agent scheduling (cron), business roles (CMO, CFO, EA). GUI dashboard, multi-agent coordination, zero cold-start, infinite sessions. Works with Claude Code, Codex, Gemini CLI, Ollama, any local model.",
|
|
5
5
|
"license": "FSL-1.1-Apache-2.0",
|
|
6
6
|
"author": "Groove Dev <hello@groovedev.ai> (https://groovedev.ai)",
|
|
@@ -2868,23 +2868,28 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
2868
2868
|
res.json(result);
|
|
2869
2869
|
});
|
|
2870
2870
|
|
|
2871
|
-
// Clean up stale artifacts
|
|
2871
|
+
// Clean up stale artifacts. Scope to a single team when teamId is provided —
|
|
2872
|
+
// wiping every agent's working dir on a global cleanup would delete other
|
|
2873
|
+
// in-flight teams' unlaunched plans. When called with no teamId, only the
|
|
2874
|
+
// daemon-root plan file is touched (safe baseline).
|
|
2872
2875
|
app.post('/api/cleanup', (req, res) => {
|
|
2876
|
+
const teamId = req.body?.teamId || req.query?.teamId || null;
|
|
2873
2877
|
let cleaned = 0;
|
|
2874
|
-
// Clean recommended-team.json from all known locations
|
|
2875
2878
|
const locations = [resolve(daemon.grooveDir, 'recommended-team.json')];
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
+
|
|
2880
|
+
if (teamId) {
|
|
2881
|
+
// Only agents in this team get their workspace scanned
|
|
2882
|
+
for (const agent of daemon.registry.getAll()) {
|
|
2883
|
+
if (agent.teamId === teamId && agent.workingDir) {
|
|
2884
|
+
locations.push(resolve(agent.workingDir, '.groove', 'recommended-team.json'));
|
|
2885
|
+
}
|
|
2879
2886
|
}
|
|
2880
2887
|
}
|
|
2881
|
-
const defaultDir = daemon.config?.defaultWorkingDir;
|
|
2882
|
-
if (defaultDir) locations.push(resolve(defaultDir, '.groove', 'recommended-team.json'));
|
|
2883
2888
|
|
|
2884
2889
|
for (const p of locations) {
|
|
2885
2890
|
if (existsSync(p)) { try { unlinkSync(p); cleaned++; } catch { /* */ } }
|
|
2886
2891
|
}
|
|
2887
|
-
daemon.audit.log('cleanup', { cleaned });
|
|
2892
|
+
daemon.audit.log('cleanup', { cleaned, teamId });
|
|
2888
2893
|
res.json({ ok: true, cleaned });
|
|
2889
2894
|
});
|
|
2890
2895
|
|
|
@@ -19,6 +19,11 @@ import { lookup as mimeLookup } from './mimetypes.js';
|
|
|
19
19
|
|
|
20
20
|
const READY_TIMEOUT_MS = 60_000; // give dev servers a minute to boot
|
|
21
21
|
const MAX_STDOUT_BYTES = 256 * 1024;
|
|
22
|
+
// Strip CSI/OSC/other ANSI escape sequences — Vite prints URLs with inline
|
|
23
|
+
// bold/color codes (e.g. "http://localhost:\x1b[1m5175\x1b[22m/") which would
|
|
24
|
+
// otherwise break port-number regexes.
|
|
25
|
+
const ANSI_REGEX = /[\u001B\u009B][[\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\d/#&.:=?%@~_]+)*|[a-zA-Z\d]+(?:;[-a-zA-Z\d/#&.:=?%@~_]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]))/g;
|
|
26
|
+
function stripAnsi(s) { return s.replace(ANSI_REGEX, ''); }
|
|
22
27
|
|
|
23
28
|
export class PreviewService {
|
|
24
29
|
constructor(daemon) {
|
|
@@ -168,11 +173,12 @@ export class PreviewService {
|
|
|
168
173
|
};
|
|
169
174
|
|
|
170
175
|
const timer = setTimeout(() => {
|
|
171
|
-
|
|
176
|
+
const tail = stripAnsi(stderrBuf).slice(-400) || stripAnsi(stdoutBuf).slice(-400) || '(no output)';
|
|
177
|
+
finish({ launched: false, reason: `timeout waiting for url in stdout; last output: ${tail}` });
|
|
172
178
|
}, READY_TIMEOUT_MS);
|
|
173
179
|
|
|
174
180
|
const tryMatch = () => {
|
|
175
|
-
const combined = stdoutBuf + '\n' + stderrBuf;
|
|
181
|
+
const combined = stripAnsi(stdoutBuf + '\n' + stderrBuf);
|
|
176
182
|
if (readyText && !combined.includes(readyText)) return;
|
|
177
183
|
const m = combined.match(urlPattern);
|
|
178
184
|
if (!m) return;
|
|
@@ -10,8 +10,10 @@ const DEFAULT_THRESHOLD = 0.65; // For non-self-managing providers (was 0.7
|
|
|
10
10
|
const HARD_CEILING = 0.80; // Force rotate (was 0.85) — only for non-self-managing
|
|
11
11
|
const CHECK_INTERVAL = 15_000;
|
|
12
12
|
const QUALITY_THRESHOLD = 40; // Score below this triggers quality rotation
|
|
13
|
-
const MIN_EVENTS =
|
|
14
|
-
const MIN_AGE_SEC =
|
|
13
|
+
const MIN_EVENTS = 20; // Minimum classifier events before scoring
|
|
14
|
+
const MIN_AGE_SEC = 300; // Minimum agent age before quality rotation (5 min)
|
|
15
|
+
const QUALITY_MIN_TOKENS = 20_000; // Minimum real token work before quality rotation can fire
|
|
16
|
+
const QUALITY_MIN_FILES = 3; // Or: 3 successful file writes proves the agent is productive
|
|
15
17
|
const SCORE_HISTORY_MAX = 40; // ~10 min at 15s intervals
|
|
16
18
|
const COOLDOWN_MS = 5 * 60 * 1000; // 5 minutes between rotations per agent
|
|
17
19
|
const QUALITY_COOLDOWN_MS = 2 * 60 * 1000; // 2 minutes for quality degradation rotations
|
|
@@ -128,7 +130,20 @@ export class Rotator extends EventEmitter {
|
|
|
128
130
|
return { score: 70, signals: {}, hasEnoughData: false, ageSec: Math.round(ageSec), eventCount: events.length };
|
|
129
131
|
}
|
|
130
132
|
|
|
131
|
-
|
|
133
|
+
// Productive-work floor: don't even score an agent that hasn't produced
|
|
134
|
+
// enough to judge. A frontend agent scaffolding a project naturally emits
|
|
135
|
+
// noisy signals (npm install warnings, Write retries) in its first few
|
|
136
|
+
// minutes; killing it mid-scaffold destroys the context it was building.
|
|
137
|
+
// Only allow the score to gate rotation once EITHER substantial tokens
|
|
138
|
+
// have flowed OR the agent has already written multiple files successfully.
|
|
139
|
+
const tokens = agent.tokensUsed || 0;
|
|
140
|
+
const signalsEarly = this.daemon.adaptive.extractSignals(events, agent.scope);
|
|
141
|
+
const filesWritten = signalsEarly.filesWritten || 0;
|
|
142
|
+
if (tokens < QUALITY_MIN_TOKENS && filesWritten < QUALITY_MIN_FILES) {
|
|
143
|
+
return { score: 70, signals: signalsEarly, hasEnoughData: false, ageSec: Math.round(ageSec), eventCount: events.length, reason: 'below_productive_floor' };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const signals = signalsEarly;
|
|
132
147
|
let score = this.daemon.adaptive.scoreSession(signals);
|
|
133
148
|
|
|
134
149
|
if (ageSec > 1800) score -= 5;
|