groove-dev 0.27.37 → 0.27.39
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/README.md +3 -3
- 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 +78 -3
- package/node_modules/@groove-dev/daemon/src/index.js +3 -0
- package/node_modules/@groove-dev/daemon/src/lockmanager.js +44 -0
- package/node_modules/@groove-dev/daemon/src/memory.js +22 -5
- package/node_modules/@groove-dev/daemon/src/preview.js +243 -0
- package/node_modules/@groove-dev/daemon/src/process.js +145 -7
- package/node_modules/@groove-dev/daemon/src/providers/claude-code.js +37 -1
- package/node_modules/@groove-dev/daemon/templates/knock-hook.cjs +44 -0
- package/node_modules/@groove-dev/gui/dist/assets/{index-Df4O6yJI.js → index-BRZ_leqO.js} +3 -3
- 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/onboarding/setup-wizard.jsx +1 -1
- package/node_modules/@groove-dev/gui/src/components/ui/toast.jsx +12 -0
- package/node_modules/@groove-dev/gui/src/stores/groove.js +35 -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 +78 -3
- package/packages/daemon/src/index.js +3 -0
- package/packages/daemon/src/lockmanager.js +44 -0
- package/packages/daemon/src/memory.js +22 -5
- package/packages/daemon/src/preview.js +243 -0
- package/packages/daemon/src/process.js +145 -7
- package/packages/daemon/src/providers/claude-code.js +37 -1
- package/packages/daemon/templates/knock-hook.cjs +44 -0
- package/packages/gui/dist/assets/{index-Df4O6yJI.js → index-BRZ_leqO.js} +3 -3
- package/packages/gui/dist/index.html +1 -1
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/components/onboarding/setup-wizard.jsx +1 -1
- package/packages/gui/src/components/ui/toast.jsx +12 -0
- package/packages/gui/src/stores/groove.js +35 -2
- package/plans/chat-persistence-refactor.md +0 -154
|
@@ -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-BRZ_leqO.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">
|
|
@@ -19,7 +19,7 @@ const PROVIDERS = [
|
|
|
19
19
|
id: 'claude-code',
|
|
20
20
|
name: 'Claude Code',
|
|
21
21
|
subtitle: 'by Anthropic',
|
|
22
|
-
models: ['Opus 4.6', 'Sonnet 4.6', 'Haiku 4.5'],
|
|
22
|
+
models: ['Opus 4.7', 'Opus 4.6', 'Sonnet 4.6', 'Haiku 4.5'],
|
|
23
23
|
authType: 'Subscription or API key',
|
|
24
24
|
authModes: ['subscription', 'apikey'],
|
|
25
25
|
recommended: true,
|
|
@@ -64,6 +64,18 @@ function ToastItem({ toast }) {
|
|
|
64
64
|
<p className="text-xs text-text-3 font-sans mt-0.5">{toast.detail}</p>
|
|
65
65
|
)}
|
|
66
66
|
</div>
|
|
67
|
+
{toast.action?.url && (
|
|
68
|
+
<button
|
|
69
|
+
onClick={(e) => {
|
|
70
|
+
e.stopPropagation();
|
|
71
|
+
try { window.open(toast.action.url, '_blank', 'noopener'); } catch {}
|
|
72
|
+
removeToast(toast.id);
|
|
73
|
+
}}
|
|
74
|
+
className="text-xs font-medium text-accent hover:text-accent-hover bg-surface-5 hover:bg-surface-6 px-3 py-1.5 rounded transition-colors cursor-pointer flex-shrink-0 whitespace-nowrap"
|
|
75
|
+
>
|
|
76
|
+
{toast.action.label || 'Open'}
|
|
77
|
+
</button>
|
|
78
|
+
)}
|
|
67
79
|
<button
|
|
68
80
|
onClick={(e) => { e.stopPropagation(); removeToast(toast.id); }}
|
|
69
81
|
className="p-1.5 text-text-4 hover:text-text-1 hover:bg-surface-5 rounded transition-colors cursor-pointer flex-shrink-0 z-10"
|
|
@@ -381,6 +381,39 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
381
381
|
get().addToast('info', `QC agent ${msg.name} auto-spawned`, 'Auditing phase 1 work');
|
|
382
382
|
break;
|
|
383
383
|
|
|
384
|
+
case 'preview:ready':
|
|
385
|
+
get().addToast(
|
|
386
|
+
'success',
|
|
387
|
+
'Project ready to preview',
|
|
388
|
+
msg.url,
|
|
389
|
+
{ label: 'View Site', url: msg.url },
|
|
390
|
+
);
|
|
391
|
+
break;
|
|
392
|
+
|
|
393
|
+
case 'preview:failed':
|
|
394
|
+
get().addToast(
|
|
395
|
+
'warning',
|
|
396
|
+
'Preview could not launch',
|
|
397
|
+
msg.reason ? String(msg.reason).slice(0, 200) : 'Unknown error',
|
|
398
|
+
);
|
|
399
|
+
break;
|
|
400
|
+
|
|
401
|
+
case 'preview:stopped':
|
|
402
|
+
break;
|
|
403
|
+
|
|
404
|
+
case 'agent:stalled': {
|
|
405
|
+
const name = msg.agentName || msg.agentId;
|
|
406
|
+
const secs = Math.round((msg.silentMs || 0) / 1000);
|
|
407
|
+
get().addToast('warning', `${name} may be stalled`, `No output for ${secs}s — API stream may be hung`);
|
|
408
|
+
break;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
case 'knock:denied': {
|
|
412
|
+
const name = msg.agentName || msg.agentId;
|
|
413
|
+
get().addToast('warning', `${name} blocked`, `${msg.toolName} on ${msg.target} — ${msg.reason || 'scope conflict'}`);
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
416
|
+
|
|
384
417
|
case 'phase2:failed':
|
|
385
418
|
get().addToast('error', `QC agent failed to spawn`, msg.error || 'Unknown error');
|
|
386
419
|
break;
|
|
@@ -725,9 +758,9 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
725
758
|
|
|
726
759
|
// ── Toasts ────────────────────────────────────────────────
|
|
727
760
|
|
|
728
|
-
addToast(type, message, detail) {
|
|
761
|
+
addToast(type, message, detail, action) {
|
|
729
762
|
const id = ++toastCounter;
|
|
730
|
-
set((s) => ({ toasts: [...s.toasts, { id, type, message, detail }] }));
|
|
763
|
+
set((s) => ({ toasts: [...s.toasts, { id, type, message, detail, action }] }));
|
|
731
764
|
},
|
|
732
765
|
removeToast(id) {
|
|
733
766
|
set((s) => ({ toasts: s.toasts.filter((t) => t.id !== id) }));
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
# Chat & Activity Persistence Refactor
|
|
2
|
-
|
|
3
|
-
## Context
|
|
4
|
-
|
|
5
|
-
On 2026-04-14, node positions were failing to persist across team switches. Six
|
|
6
|
-
attempted fixes in `packages/gui/src/views/agents.jsx` all failed because they
|
|
7
|
-
targeted the wrong layer. On-screen debug logging (since removed) revealed the
|
|
8
|
-
actual cause:
|
|
9
|
-
|
|
10
|
-
```
|
|
11
|
-
SAVE.err: Failed to execute 'setItem' on 'Storage': Setting the value of
|
|
12
|
-
'groove:nodePositions:...' exceeded the quota.
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
`localStorage` was full. `chatHistory` (100 msgs/agent) and `activityLog`
|
|
16
|
-
(200 events/agent) were persisted on every agent stdout event and never pruned
|
|
17
|
-
when agents were killed. Over time they consumed the browser's ~5–10 MB quota,
|
|
18
|
-
so every new `setItem` silently failed — including node positions, panel
|
|
19
|
-
widths, and anything else.
|
|
20
|
-
|
|
21
|
-
## What's already shipped
|
|
22
|
-
|
|
23
|
-
Two stopgap fixes are in `main` so the app works today:
|
|
24
|
-
|
|
25
|
-
- `packages/gui/src/stores/groove.js` — on every WebSocket `state` broadcast,
|
|
26
|
-
prune `tokenTimeline`, `chatHistory`, and `activityLog` entries whose agent
|
|
27
|
-
IDs are no longer in the live registry. Keeps localStorage bounded by live
|
|
28
|
-
agents instead of all-agents-ever.
|
|
29
|
-
- `packages/gui/src/views/agents.jsx` `savePositions()` — on quota error,
|
|
30
|
-
drop `groove:chatHistory` + `groove:activityLog` and retry.
|
|
31
|
-
|
|
32
|
-
These are sufficient for normal usage (5–20 concurrent agents with moderate
|
|
33
|
-
output). They do not fix the underlying architectural problem.
|
|
34
|
-
|
|
35
|
-
## Remaining failure modes
|
|
36
|
-
|
|
37
|
-
1. **Single chatty agent hits quota alone.** Messages are count-capped (100)
|
|
38
|
-
but not size-capped. One agent dumping 50 KB tool outputs × 100 = 5 MB,
|
|
39
|
-
quota gone before any prune runs.
|
|
40
|
-
2. **Main-thread stalls.** Every stdout event does a synchronous
|
|
41
|
-
`JSON.stringify` of the full `chatHistory` object + `setItem`. Scales O(n
|
|
42
|
-
agents × m messages). Worse with more agents.
|
|
43
|
-
3. **Browser cache clears wipe history.** `localStorage` is per-origin,
|
|
44
|
-
per-browser. Clearing the Electron webview or switching devices loses all
|
|
45
|
-
conversation state.
|
|
46
|
-
4. **Multi-client divergence.** If/when we support web GUI + Electron hitting
|
|
47
|
-
the same daemon, each has its own localStorage — they'll show different
|
|
48
|
-
chat histories for the same agent.
|
|
49
|
-
5. **No querying.** Want "last message across all agents" or "search for
|
|
50
|
-
'error' in activity"? Not possible without deserializing everything.
|
|
51
|
-
|
|
52
|
-
## Proposed architecture
|
|
53
|
-
|
|
54
|
-
Move chat, activity, and timeline to daemon-side disk persistence. Keep
|
|
55
|
-
`localStorage` for tiny UI state only (node positions, viewport, panel widths,
|
|
56
|
-
`activeTeamId`, `expandedNodes`).
|
|
57
|
-
|
|
58
|
-
### Daemon changes (`packages/daemon/src/`)
|
|
59
|
-
|
|
60
|
-
New module `chatlog.js`:
|
|
61
|
-
- Append-only JSONL files at `.groove/chat/<agentId>.jsonl` and
|
|
62
|
-
`.groove/activity/<agentId>.jsonl`.
|
|
63
|
-
- Each line: `{ t, from, text, type?, subtype? }` for chat;
|
|
64
|
-
`{ t, text, type, subtype }` for activity.
|
|
65
|
-
- Write on every message — fs.appendFile with a 0o600 perm. No in-memory
|
|
66
|
-
buffer needed; kernel page cache handles batching.
|
|
67
|
-
- Add rotation: if a file exceeds ~5 MB, rename to `.1.jsonl` and start fresh.
|
|
68
|
-
Keep at most N=3 rotations. (Protects against a runaway agent eating disk.)
|
|
69
|
-
- On agent kill/completion: leave files alone. GC cleans them later (existing
|
|
70
|
-
`_gc()` in `index.js` already handles `GROOVE_AGENT_LOGS/` — extend to
|
|
71
|
-
`.groove/chat/` and `.groove/activity/`.)
|
|
72
|
-
|
|
73
|
-
New endpoints in `api.js`:
|
|
74
|
-
- `GET /api/agents/:id/chat?cursor=<lineNumber>&limit=100` — returns
|
|
75
|
-
`{ lines: [...], nextCursor, hasMore }`. Paginate from newest backwards
|
|
76
|
-
(like tail -f) so the UI loads the last 100 first.
|
|
77
|
-
- `GET /api/agents/:id/activity?cursor=...&limit=...` — same shape.
|
|
78
|
-
- `DELETE /api/agents/:id/chat` — for future "clear history" UI.
|
|
79
|
-
|
|
80
|
-
Integrate into `process.js` stdout handler:
|
|
81
|
-
- When we currently emit `agent:stdout` / `agent:activity` over WebSocket,
|
|
82
|
-
also call `chatlog.appendChat(agentId, entry)` or
|
|
83
|
-
`chatlog.appendActivity(agentId, entry)`. WebSocket push stays for live
|
|
84
|
-
streaming; JSONL is the durable tail.
|
|
85
|
-
|
|
86
|
-
### GUI changes (`packages/gui/src/`)
|
|
87
|
-
|
|
88
|
-
In `stores/groove.js`:
|
|
89
|
-
- Remove `chatHistory` and `activityLog` from the persisted keys. Kill the
|
|
90
|
-
`persistJSON('groove:chatHistory', ...)` and `persistJSON('groove:activityLog', ...)`
|
|
91
|
-
calls in the stdout handler.
|
|
92
|
-
- Keep them as in-memory state only, scoped to agents the user has opened in
|
|
93
|
-
this session. When user opens an agent panel, hydrate from
|
|
94
|
-
`GET /api/agents/:id/chat` + `GET /api/agents/:id/activity`.
|
|
95
|
-
- When user closes the panel or switches away, drop from memory after a grace
|
|
96
|
-
period (e.g., 5 min LRU). WebSocket push still updates the in-memory copy
|
|
97
|
-
for the active agent.
|
|
98
|
-
- Optional: cap total in-memory messages (e.g., 1000 across all agents) with
|
|
99
|
-
LRU eviction by agent.
|
|
100
|
-
|
|
101
|
-
In `components/agents/agent-chat.jsx` (or wherever chat is rendered):
|
|
102
|
-
- On mount: dispatch `fetchAgentHistory(agentId)` if not already loaded.
|
|
103
|
-
- Show a "Load more" button or scroll-to-top infinite scroll for pagination.
|
|
104
|
-
|
|
105
|
-
### What stays in localStorage
|
|
106
|
-
|
|
107
|
-
- `groove:nodePositions:<teamId>` — tiny, per-team, bounded by agent count.
|
|
108
|
-
- `groove:teamViewports`, `groove:teamOrder`, `groove:activeTeamId`.
|
|
109
|
-
- `groove:detailWidth`, `groove:terminalVisible`, `groove:terminalHeight`,
|
|
110
|
-
`groove:editorSidebarWidth`, `groove:expandedNodes`.
|
|
111
|
-
- `groove:onboardingComplete`, `groove:storeVersion`.
|
|
112
|
-
|
|
113
|
-
None of these individually exceed a few KB. Total <100 KB in the worst case.
|
|
114
|
-
|
|
115
|
-
## Implementation order
|
|
116
|
-
|
|
117
|
-
1. Add `chatlog.js` + wire JSONL writes in `process.js`. Tests: spawn an
|
|
118
|
-
agent, verify `.groove/chat/<id>.jsonl` grows; kill it, restart daemon,
|
|
119
|
-
verify file still there.
|
|
120
|
-
2. Add GET endpoints in `api.js` + GUI fetch helpers. Test: hit endpoint with
|
|
121
|
-
curl, verify cursor pagination.
|
|
122
|
-
3. Add in-memory + lazy-load path in `stores/groove.js`. Keep localStorage
|
|
123
|
-
writes in parallel behind a feature flag (`groove:chatPersist=daemon`) so
|
|
124
|
-
we can A/B before flipping default.
|
|
125
|
-
4. Flip default, remove localStorage writes for chatHistory/activityLog,
|
|
126
|
-
remove the prune-stale-agents stopgap in the `state` handler.
|
|
127
|
-
5. Add GC for `.groove/chat/*.jsonl` and `.groove/activity/*.jsonl` in
|
|
128
|
-
`index.js` `_gc()` (dead agents after N days).
|
|
129
|
-
|
|
130
|
-
## Estimated effort
|
|
131
|
-
|
|
132
|
-
2–3 hours focused work. Bulk is the GUI refactor — the daemon side is a thin
|
|
133
|
-
append-only writer + two endpoints.
|
|
134
|
-
|
|
135
|
-
## Files touched (expected)
|
|
136
|
-
|
|
137
|
-
- `packages/daemon/src/chatlog.js` (new)
|
|
138
|
-
- `packages/daemon/src/process.js` (add appendChat/appendActivity calls)
|
|
139
|
-
- `packages/daemon/src/api.js` (two new GET endpoints)
|
|
140
|
-
- `packages/daemon/src/index.js` (import chatlog; extend `_gc()`)
|
|
141
|
-
- `packages/gui/src/stores/groove.js` (remove persistJSON for chat/activity;
|
|
142
|
-
add fetchAgentHistory action; drop prune-stale-agents stopgap from `state`
|
|
143
|
-
handler)
|
|
144
|
-
- `packages/gui/src/components/agents/agent-chat.jsx` (hydrate on mount)
|
|
145
|
-
- `packages/gui/src/components/agents/agent-feed.jsx` (same for activity)
|
|
146
|
-
|
|
147
|
-
## When to do this
|
|
148
|
-
|
|
149
|
-
Not urgent. The current prune+retry stopgap handles normal usage. Priority
|
|
150
|
-
bumps if:
|
|
151
|
-
- A user reports chat/activity loss that prune-on-broadcast didn't catch.
|
|
152
|
-
- We add multi-client access (web GUI alongside Electron).
|
|
153
|
-
- We add chat search or cross-agent analytics.
|
|
154
|
-
- An agent with huge tool outputs single-handedly hits quota in production.
|