groove-dev 0.27.141 → 0.27.143

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.
Files changed (74) hide show
  1. package/CLAUDE.md +7 -0
  2. package/node_modules/@groove-dev/cli/package.json +1 -1
  3. package/node_modules/@groove-dev/daemon/package.json +1 -1
  4. package/node_modules/@groove-dev/daemon/src/api.js +18 -7
  5. package/node_modules/@groove-dev/daemon/src/introducer.js +1 -1
  6. package/node_modules/@groove-dev/daemon/src/journalist.js +3 -2
  7. package/node_modules/@groove-dev/daemon/src/keeper.js +2 -2
  8. package/node_modules/@groove-dev/daemon/src/memory.js +8 -5
  9. package/node_modules/@groove-dev/daemon/src/process.js +5 -16
  10. package/node_modules/@groove-dev/daemon/src/rotator.js +25 -8
  11. package/node_modules/@groove-dev/gui/dist/assets/{codemirror-BQqYnZfL.js → codemirror-BYKpdS2W.js} +10 -10
  12. package/node_modules/@groove-dev/gui/dist/assets/index-CCVvAoQn.css +1 -0
  13. package/node_modules/@groove-dev/gui/dist/assets/index-DGIv_TRm.js +984 -0
  14. package/node_modules/@groove-dev/gui/dist/index.html +3 -3
  15. package/node_modules/@groove-dev/gui/package.json +1 -1
  16. package/node_modules/@groove-dev/gui/src/app.jsx +0 -2
  17. package/node_modules/@groove-dev/gui/src/components/agents/agent-chat.jsx +73 -17
  18. package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +8 -2
  19. package/node_modules/@groove-dev/gui/src/components/agents/agent-file-tree.jsx +5 -6
  20. package/node_modules/@groove-dev/gui/src/components/agents/agent-panel.jsx +79 -5
  21. package/node_modules/@groove-dev/gui/src/components/agents/workspace-mode.jsx +2 -53
  22. package/node_modules/@groove-dev/gui/src/components/dashboard/context-gauges.jsx +111 -0
  23. package/node_modules/@groove-dev/gui/src/components/dashboard/routing-chart.jsx +70 -33
  24. package/node_modules/@groove-dev/gui/src/components/editor/code-editor.jsx +2 -68
  25. package/node_modules/@groove-dev/gui/src/components/editor/selection-menu.jsx +2 -0
  26. package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +1 -2
  27. package/node_modules/@groove-dev/gui/src/components/layout/terminal-panel.jsx +0 -1
  28. package/node_modules/@groove-dev/gui/src/stores/groove.js +3 -3
  29. package/node_modules/@groove-dev/gui/src/views/dashboard.jsx +2 -0
  30. package/node_modules/@groove-dev/gui/src/views/marketplace.jsx +3 -71
  31. package/node_modules/@groove-dev/gui/src/views/models.jsx +5 -6
  32. package/package.json +1 -1
  33. package/packages/cli/package.json +1 -1
  34. package/packages/daemon/package.json +1 -1
  35. package/packages/daemon/src/api.js +18 -7
  36. package/packages/daemon/src/introducer.js +1 -1
  37. package/packages/daemon/src/journalist.js +3 -2
  38. package/packages/daemon/src/keeper.js +2 -2
  39. package/packages/daemon/src/memory.js +8 -5
  40. package/packages/daemon/src/process.js +5 -16
  41. package/packages/daemon/src/rotator.js +25 -8
  42. package/packages/gui/dist/assets/{codemirror-BQqYnZfL.js → codemirror-BYKpdS2W.js} +10 -10
  43. package/packages/gui/dist/assets/index-CCVvAoQn.css +1 -0
  44. package/packages/gui/dist/assets/index-DGIv_TRm.js +984 -0
  45. package/packages/gui/dist/index.html +3 -3
  46. package/packages/gui/package.json +1 -1
  47. package/packages/gui/src/app.jsx +0 -2
  48. package/packages/gui/src/components/agents/agent-chat.jsx +73 -17
  49. package/packages/gui/src/components/agents/agent-feed.jsx +8 -2
  50. package/packages/gui/src/components/agents/agent-file-tree.jsx +5 -6
  51. package/packages/gui/src/components/agents/agent-panel.jsx +79 -5
  52. package/packages/gui/src/components/agents/workspace-mode.jsx +2 -53
  53. package/packages/gui/src/components/dashboard/context-gauges.jsx +111 -0
  54. package/packages/gui/src/components/dashboard/routing-chart.jsx +70 -33
  55. package/packages/gui/src/components/editor/code-editor.jsx +2 -68
  56. package/packages/gui/src/components/editor/selection-menu.jsx +2 -0
  57. package/packages/gui/src/components/layout/activity-bar.jsx +1 -2
  58. package/packages/gui/src/components/layout/terminal-panel.jsx +0 -1
  59. package/packages/gui/src/stores/groove.js +3 -3
  60. package/packages/gui/src/views/dashboard.jsx +2 -0
  61. package/packages/gui/src/views/marketplace.jsx +3 -71
  62. package/packages/gui/src/views/models.jsx +5 -6
  63. package/node_modules/@groove-dev/gui/dist/assets/index-A4e1gIDh.css +0 -1
  64. package/node_modules/@groove-dev/gui/dist/assets/index-P1hsM27-.js +0 -8696
  65. package/node_modules/@groove-dev/gui/src/components/toys/toy-card.jsx +0 -78
  66. package/node_modules/@groove-dev/gui/src/components/toys/toy-creator.jsx +0 -144
  67. package/node_modules/@groove-dev/gui/src/components/toys/toy-launcher.jsx +0 -187
  68. package/node_modules/@groove-dev/gui/src/views/toys.jsx +0 -162
  69. package/packages/gui/dist/assets/index-A4e1gIDh.css +0 -1
  70. package/packages/gui/dist/assets/index-P1hsM27-.js +0 -8696
  71. package/packages/gui/src/components/toys/toy-card.jsx +0 -78
  72. package/packages/gui/src/components/toys/toy-creator.jsx +0 -144
  73. package/packages/gui/src/components/toys/toy-launcher.jsx +0 -187
  74. package/packages/gui/src/views/toys.jsx +0 -162
package/CLAUDE.md CHANGED
@@ -295,3 +295,10 @@ Audit-driven release. Multi-agent orchestration system with 7 coordination layer
295
295
  - Dashboard: routing donut, cache panel, context health gauges
296
296
  - Monitor/QC agent mode (stay active, loop)
297
297
  - Distribution: demo video, HN launch, Twitter content
298
+
299
+ <!-- GROOVE:START -->
300
+ ## GROOVE Orchestration (auto-injected)
301
+ Active agents: 0
302
+ See AGENTS_REGISTRY.md for full agent state.
303
+ **Memory policy:** GROOVE manages project memory automatically. Do not read or write MEMORY.md or .groove/memory/ files directly.
304
+ <!-- GROOVE:END -->
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groove-dev/cli",
3
- "version": "0.27.141",
3
+ "version": "0.27.143",
4
4
  "description": "GROOVE CLI — manage AI coding agents from your terminal",
5
5
  "license": "FSL-1.1-Apache-2.0",
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groove-dev/daemon",
3
- "version": "0.27.141",
3
+ "version": "0.27.143",
4
4
  "description": "GROOVE daemon — agent orchestration engine",
5
5
  "license": "FSL-1.1-Apache-2.0",
6
6
  "type": "module",
@@ -460,13 +460,14 @@ export function createApi(app, daemon) {
460
460
  // Discoveries (error → fix pairs)
461
461
  app.get('/api/memory/discoveries', (req, res) => {
462
462
  const role = req.query.role;
463
+ const teamId = req.query.teamId;
463
464
  const limit = Math.min(parseInt(req.query.limit) || 100, 500);
464
- res.json({ discoveries: daemon.memory.listDiscoveries({ role, limit }) });
465
+ res.json({ discoveries: daemon.memory.listDiscoveries({ role, teamId, limit }) });
465
466
  });
466
467
 
467
468
  app.post('/api/memory/discoveries', (req, res) => {
468
- const { agentId, role, trigger, fix, outcome } = req.body || {};
469
- const result = daemon.memory.addDiscovery({ agentId, role, trigger, fix, outcome });
469
+ const { agentId, role, trigger, fix, outcome, teamId } = req.body || {};
470
+ const result = daemon.memory.addDiscovery({ agentId, role, trigger, fix, outcome, teamId });
470
471
  if (!result.added && result.error) {
471
472
  return res.status(400).json(result);
472
473
  }
@@ -3171,11 +3172,21 @@ Keep responses concise. Help them think, don't lecture them about the system the
3171
3172
 
3172
3173
  function validateFilePath(relPath, projectDir) {
3173
3174
  if (!relPath || typeof relPath !== 'string') return { error: 'path is required' };
3174
- if (relPath.startsWith('/') || relPath.includes('..') || relPath.includes('\0')) {
3175
- return { error: 'Invalid path' };
3175
+ if (relPath.includes('\0')) return { error: 'Invalid path' };
3176
+
3177
+ let fullPath;
3178
+ if (relPath.startsWith('/')) {
3179
+ if (relPath.includes('..')) return { error: 'Invalid path' };
3180
+ if (!relPath.startsWith(projectDir + '/') && relPath !== projectDir) {
3181
+ return { error: 'Path outside project' };
3182
+ }
3183
+ fullPath = relPath;
3184
+ } else {
3185
+ if (relPath.includes('..')) return { error: 'Invalid path' };
3186
+ fullPath = resolve(projectDir, relPath);
3187
+ if (!fullPath.startsWith(projectDir)) return { error: 'Path outside project' };
3176
3188
  }
3177
- const fullPath = resolve(projectDir, relPath);
3178
- if (!fullPath.startsWith(projectDir)) return { error: 'Path outside project' };
3189
+
3179
3190
  // Symlink resolution — ensure real path is also within project
3180
3191
  try {
3181
3192
  const realPath = realpathSync(fullPath);
@@ -437,7 +437,7 @@ export class Introducer {
437
437
  }
438
438
 
439
439
  if (!isLightPlanner && (hasTask || isRotation)) {
440
- const discoveries = this.daemon.memory.getDiscoveriesMarkdown(newAgent.role, 8, 600, newAgent.scope);
440
+ const discoveries = this.daemon.memory.getDiscoveriesMarkdown(newAgent.role, 8, 600, newAgent.scope, newAgent.teamId);
441
441
  if (discoveries) {
442
442
  parts.push(`### Known Fixes for ${newAgent.role} Role\n${discoveries}`);
443
443
  }
@@ -877,7 +877,7 @@ export class Journalist {
877
877
  const entries = agentLog?.entries || [];
878
878
 
879
879
  // Layer 7 memory: discoveries (inline, not pointer — agents lose context with pointers), constraints, specializations
880
- const discoveries = this.daemon.memory?.getDiscoveriesMarkdown(agent.role, 10, 1500) || '';
880
+ const discoveries = this.daemon.memory?.getDiscoveriesMarkdown(agent.role, 10, 1500, agent.scope, agent.teamId) || '';
881
881
  const constraints = this.daemon.memory?.getConstraintsMarkdown(2000) || '';
882
882
  const specialization = this.daemon.memory?.getSpecialization(agent.id);
883
883
  const specLine = specialization?.avgQualityScore != null
@@ -1106,7 +1106,7 @@ export class Journalist {
1106
1106
  if (!thread) return null;
1107
1107
 
1108
1108
  const constraints = this.daemon.memory?.getConstraintsMarkdown(2000) || '';
1109
- const discoveries = this.daemon.memory?.getDiscoveriesMarkdown(agent.role, 5, 1000) || '';
1109
+ const discoveries = this.daemon.memory?.getDiscoveriesMarkdown(agent.role, 5, 1000, agent.scope, agent.teamId) || '';
1110
1110
 
1111
1111
  let prompt = [
1112
1112
  `# Session Context Resume`,
@@ -1352,6 +1352,7 @@ export class Journalist {
1352
1352
  this.daemon.memory.addDiscovery({
1353
1353
  agentId: agent.id,
1354
1354
  role: agent.role,
1355
+ teamId: agent.teamId || null,
1355
1356
  trigger: trigger.slice(0, 300),
1356
1357
  fix: fix.slice(0, 500),
1357
1358
  outcome: 'success',
@@ -254,9 +254,9 @@ export class Keeper {
254
254
  // ── Command parser ────────────────────────────────────────
255
255
 
256
256
  static parseCommand(text) {
257
- const cmdMatch = text.match(/\[(save|append|update|delete|view|doc|link|read|instruct)\]|\b(save|append|update|delete|view|doc|link|read)\b(?=\s+#[\w/.-])/i);
257
+ const cmdMatch = text.match(/\[(save|append|update|delete|view|doc|link|read|instruct)\]/i);
258
258
  if (!cmdMatch) return null;
259
- const command = (cmdMatch[1] || cmdMatch[2]).toLowerCase();
259
+ const command = cmdMatch[1].toLowerCase();
260
260
  const rest = text.slice(cmdMatch.index + cmdMatch[0].length).trim();
261
261
 
262
262
  if (command === 'instruct') {
@@ -261,7 +261,7 @@ export class MemoryStore {
261
261
 
262
262
  // --- Discoveries (error → fix pairs) ---
263
263
 
264
- addDiscovery({ agentId, role, trigger, fix, outcome = 'success' }) {
264
+ addDiscovery({ agentId, role, trigger, fix, outcome = 'success', teamId }) {
265
265
  if (!trigger || !fix) return { added: false, error: 'trigger and fix required' };
266
266
  if (outcome !== 'success') return { added: false, reason: 'only successes stored' };
267
267
 
@@ -277,6 +277,7 @@ export class MemoryStore {
277
277
  ts: new Date().toISOString(),
278
278
  agentId: agentId || null,
279
279
  role: role || 'unknown',
280
+ teamId: teamId || null,
280
281
  trigger: truncate(String(trigger).trim(), 300),
281
282
  fix: truncate(String(fix).trim(), 500),
282
283
  outcome,
@@ -298,7 +299,7 @@ export class MemoryStore {
298
299
  }
299
300
  }
300
301
 
301
- listDiscoveries({ role, limit = 100 } = {}) {
302
+ listDiscoveries({ role, teamId, limit = 100 } = {}) {
302
303
  if (!existsSync(this.discoveriesPath)) return [];
303
304
  try {
304
305
  const lines = readFileSync(this.discoveriesPath, 'utf8').split('\n').filter(Boolean);
@@ -306,7 +307,9 @@ export class MemoryStore {
306
307
  for (const line of lines) {
307
308
  try {
308
309
  const e = JSON.parse(line);
309
- if (!role || e.role === role) entries.push(e);
310
+ if (role && e.role !== role) continue;
311
+ if (teamId && e.teamId && e.teamId !== teamId) continue;
312
+ entries.push(e);
310
313
  } catch { /* skip malformed */ }
311
314
  }
312
315
  return entries.slice(-limit).reverse(); // newest first
@@ -328,8 +331,8 @@ export class MemoryStore {
328
331
  } catch { /* best-effort */ }
329
332
  }
330
333
 
331
- getDiscoveriesMarkdown(role, limit = 20, maxChars = 4000, scope) {
332
- let entries = this.listDiscoveries({ role, limit: limit * 3 });
334
+ getDiscoveriesMarkdown(role, limit = 20, maxChars = 4000, scope, teamId) {
335
+ let entries = this.listDiscoveries({ role, teamId, limit: limit * 3 });
333
336
  if (entries.length === 0) return '';
334
337
 
335
338
  if (scope && Array.isArray(scope) && scope.length > 0) {
@@ -2000,22 +2000,11 @@ For normal file edits within your scope, proceed without review.
2000
2000
  return this.daemon.rotator.rotate(agentId, { additionalPrompt: message });
2001
2001
  }
2002
2002
 
2003
- // Context-aware idle resume: if the agent has been idle long enough for
2004
- // internal compaction to degrade context, spawn fresh with the full
2005
- // conversation thread instead of resuming the compacted session.
2006
- const IDLE_CONTEXT_THRESHOLD_MS = 5 * 60_000; // 5 minutes (matches prompt cache TTL)
2007
- const idleMs = agent.lastActivity
2008
- ? Date.now() - new Date(agent.lastActivity).getTime()
2009
- : Infinity;
2010
-
2011
- if (idleMs > IDLE_CONTEXT_THRESHOLD_MS && this.daemon.journalist) {
2012
- const resumePrompt = this.daemon.journalist.buildConversationResumePrompt(agent, message);
2013
- if (resumePrompt) {
2014
- console.log(`[Groove] Agent ${agent.name} idle ${Math.round(idleMs / 60000)}min — using conversation-thread resume`);
2015
- // Use rotation machinery but with our richer prompt instead of handoff brief
2016
- return this._conversationResume(agentId, agent, resumePrompt);
2017
- }
2018
- }
2003
+ // Conversation-thread resume: available via explicit API call (conversationResume=true)
2004
+ // but NOT automatic. The old 5-min idle threshold was too aggressive — it replaced
2005
+ // every normal --resume with a full re-spawn, losing the Claude Code session.
2006
+ // Normal --resume preserves the actual CC session; conversation-resume is for when
2007
+ // the user explicitly wants a fresh context with the dialogue replayed.
2019
2008
 
2020
2009
  const provider = getProvider(agent.provider || 'claude-code');
2021
2010
  if (!provider?.buildResumeCommand) {
@@ -346,14 +346,31 @@ export class Rotator extends EventEmitter {
346
346
  delete this.scoreHistory[agentId];
347
347
  this.compactionCounts.delete(agentId);
348
348
 
349
- let brief = await journalist.generateHandoffBrief(agent, {
350
- reason: options.reason,
351
- qualityScore: options.qualityScore,
352
- signals: options.signals,
353
- });
354
-
355
- if (options.additionalPrompt) {
356
- brief = brief + '\n\n## User Instruction\n\n' + options.additionalPrompt;
349
+ // Try conversation-thread resume first: replays the actual user↔assistant
350
+ // dialogue from stream-json logs so the new agent picks up with full context
351
+ // instead of a lossy journalist summary. Falls back to the brief if logs are
352
+ // empty or the thread is too short to be useful.
353
+ let brief;
354
+ let usedConversationThread = false;
355
+ if (typeof journalist.buildConversationResumePrompt === 'function') {
356
+ const conversationPrompt = journalist.buildConversationResumePrompt(
357
+ agent,
358
+ options.additionalPrompt || ''
359
+ );
360
+ if (conversationPrompt && conversationPrompt.length > 500) {
361
+ brief = conversationPrompt;
362
+ usedConversationThread = true;
363
+ }
364
+ }
365
+ if (!usedConversationThread) {
366
+ brief = await journalist.generateHandoffBrief(agent, {
367
+ reason: options.reason,
368
+ qualityScore: options.qualityScore,
369
+ signals: options.signals,
370
+ });
371
+ if (options.additionalPrompt) {
372
+ brief = brief + '\n\n## User Instruction\n\n' + options.additionalPrompt;
373
+ }
357
374
  }
358
375
 
359
376
  if (agent.role === 'planner' && !brief.includes('PLANNING ONLY')) {