groove-dev 0.27.107 → 0.27.108

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 (53) hide show
  1. package/TRAINING_DATA.md +9 -0
  2. package/moe-training/client/envelope-builder.js +5 -0
  3. package/moe-training/client/scrubber.js +1 -1
  4. package/moe-training/client/step-classifier.js +22 -6
  5. package/moe-training/client/trajectory-capture.js +15 -4
  6. package/moe-training/shared/constants.js +2 -0
  7. package/moe-training/shared/envelope-schema.js +1 -1
  8. package/moe-training/test/client/envelope-builder.test.js +32 -0
  9. package/moe-training/test/client/scrubber.test.js +13 -0
  10. package/moe-training/test/client/step-classifier.test.js +96 -7
  11. package/moe-training/test/client/trajectory-capture.test.js +53 -6
  12. package/node_modules/@groove-dev/cli/package.json +1 -1
  13. package/node_modules/@groove-dev/daemon/package.json +1 -1
  14. package/node_modules/@groove-dev/daemon/src/api.js +29 -2
  15. package/node_modules/@groove-dev/daemon/src/process.js +5 -5
  16. package/node_modules/@groove-dev/gui/dist/assets/{index-DkAGIluW.js → index-CEgtSfbG.js} +1748 -1745
  17. package/node_modules/@groove-dev/gui/dist/assets/{index-QwgLRN8B.css → index-_3cJS_UG.css} +1 -1
  18. package/node_modules/@groove-dev/gui/dist/index.html +2 -2
  19. package/node_modules/@groove-dev/gui/package.json +1 -1
  20. package/node_modules/@groove-dev/gui/src/components/layout/command-palette.jsx +2 -1
  21. package/node_modules/@groove-dev/gui/src/components/layout/status-bar.jsx +9 -3
  22. package/node_modules/@groove-dev/gui/src/components/settings/federation-panel.jsx +2 -2
  23. package/node_modules/@groove-dev/gui/src/components/settings/federation-peers.jsx +14 -2
  24. package/node_modules/@groove-dev/gui/src/components/settings/quick-connect.jsx +9 -0
  25. package/node_modules/@groove-dev/gui/src/stores/groove.js +10 -0
  26. package/node_modules/@groove-dev/gui/src/views/federation.jsx +56 -15
  27. package/node_modules/moe-training/client/envelope-builder.js +5 -0
  28. package/node_modules/moe-training/client/scrubber.js +1 -1
  29. package/node_modules/moe-training/client/step-classifier.js +22 -6
  30. package/node_modules/moe-training/client/trajectory-capture.js +15 -4
  31. package/node_modules/moe-training/shared/constants.js +2 -0
  32. package/node_modules/moe-training/shared/envelope-schema.js +1 -1
  33. package/node_modules/moe-training/test/client/envelope-builder.test.js +32 -0
  34. package/node_modules/moe-training/test/client/scrubber.test.js +13 -0
  35. package/node_modules/moe-training/test/client/step-classifier.test.js +96 -7
  36. package/node_modules/moe-training/test/client/trajectory-capture.test.js +53 -6
  37. package/package.json +1 -1
  38. package/packages/cli/package.json +1 -1
  39. package/packages/daemon/package.json +1 -1
  40. package/packages/daemon/src/api.js +29 -2
  41. package/packages/daemon/src/process.js +5 -5
  42. package/packages/gui/dist/assets/{index-DkAGIluW.js → index-CEgtSfbG.js} +1748 -1745
  43. package/packages/gui/dist/assets/{index-QwgLRN8B.css → index-_3cJS_UG.css} +1 -1
  44. package/packages/gui/dist/index.html +2 -2
  45. package/packages/gui/package.json +1 -1
  46. package/packages/gui/src/components/layout/command-palette.jsx +2 -1
  47. package/packages/gui/src/components/layout/status-bar.jsx +9 -3
  48. package/packages/gui/src/components/settings/federation-panel.jsx +2 -2
  49. package/packages/gui/src/components/settings/federation-peers.jsx +14 -2
  50. package/packages/gui/src/components/settings/quick-connect.jsx +9 -0
  51. package/packages/gui/src/stores/groove.js +10 -0
  52. package/packages/gui/src/views/federation.jsx +56 -15
  53. package/ssh/main.js +2253 -0
@@ -29,12 +29,17 @@ function makeCtx(overrides = {}) {
29
29
  session_quality: overrides.quality ?? 80,
30
30
  },
31
31
  builder: {
32
- buildSessionClose: (outcome) => ({
33
- envelope_id: 'env_test_close',
34
- session_id: 'sess_test_1',
35
- type: 'SESSION_CLOSE',
36
- outcome,
37
- }),
32
+ _metadata: {},
33
+ updateMetadata(updates) { Object.assign(this._metadata, updates); },
34
+ buildSessionClose: function (outcome) {
35
+ return {
36
+ envelope_id: 'env_test_close',
37
+ session_id: 'sess_test_1',
38
+ type: 'SESSION_CLOSE',
39
+ metadata: { ...this._metadata },
40
+ outcome,
41
+ };
42
+ },
38
43
  },
39
44
  };
40
45
  }
@@ -439,6 +444,48 @@ describe('TrajectoryCapture — _computeQuality', () => {
439
444
  });
440
445
  });
441
446
 
447
+ describe('TrajectoryCapture — domain_tags in SESSION_CLOSE', () => {
448
+ it('domain_tags set on ctx.metadata flow into SESSION_CLOSE via updateMetadata', () => {
449
+ const tc = makeTc();
450
+ const captured = [];
451
+ tc._signAndTransmit = (_sid, envelope) => { captured.push(envelope); };
452
+
453
+ const ctx = makeCtx();
454
+ ctx.metadata.domain_tags = {
455
+ primary: { domain: 'react_frontend', confidence: 0.3 },
456
+ secondary: { domain: 'typescript_node', confidence: 0.25 },
457
+ tertiary: { domain: 'python', confidence: 0 },
458
+ };
459
+
460
+ ctx.builder.updateMetadata({
461
+ domain_tags: ctx.metadata.domain_tags,
462
+ session_quality: ctx.metadata.session_quality,
463
+ });
464
+ const close = ctx.builder.buildSessionClose({
465
+ status: 'SUCCESS', total_steps: 10, total_chunks: 1,
466
+ });
467
+
468
+ assert.ok(close.metadata, 'SESSION_CLOSE must have metadata');
469
+ assert.deepEqual(close.metadata.domain_tags, ctx.metadata.domain_tags);
470
+ });
471
+
472
+ it('SESSION_CLOSE metadata is absent domain_tags when tagger returns null', () => {
473
+ const ctx = makeCtx();
474
+ ctx.metadata.domain_tags = null;
475
+
476
+ ctx.builder.updateMetadata({
477
+ domain_tags: null,
478
+ session_quality: ctx.metadata.session_quality,
479
+ });
480
+ const close = ctx.builder.buildSessionClose({
481
+ status: 'SUCCESS', total_steps: 5, total_chunks: 1,
482
+ });
483
+
484
+ assert.ok(close.metadata, 'SESSION_CLOSE must have metadata');
485
+ assert.equal(close.metadata.domain_tags, null);
486
+ });
487
+ });
488
+
442
489
  describe('TrajectoryCapture — onParsedOutput', () => {
443
490
  function makeEnabledTc() {
444
491
  const tc = makeTc();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "groove-dev",
3
- "version": "0.27.107",
3
+ "version": "0.27.108",
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)",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groove-dev/cli",
3
- "version": "0.27.107",
3
+ "version": "0.27.108",
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.107",
3
+ "version": "0.27.108",
4
4
  "description": "GROOVE daemon — agent orchestration engine",
5
5
  "license": "FSL-1.1-Apache-2.0",
6
6
  "type": "module",
@@ -62,12 +62,12 @@ async function _executeApprovalRetry(daemon, approval) {
62
62
  return;
63
63
  }
64
64
  if (rp.agentId) {
65
- await daemon.processes.sendMessage(rp.agentId, `Your ${rp.type === 'integration_exec' ? 'integration action' : 'upload'} was approved and executed successfully. Result: ${resultText}`);
65
+ await daemon.processes.sendMessage(rp.agentId, `Your ${rp.type === 'integration_exec' ? 'integration action' : 'upload'} was approved and executed successfully. Result: ${resultText}`, 'system');
66
66
  }
67
67
  } catch (err) {
68
68
  console.log(`[Groove] Auto-retry for approval ${approval.id} failed: ${err.message}`);
69
69
  if (rp.agentId) {
70
- daemon.processes.sendMessage(rp.agentId, `Your ${rp.type === 'integration_exec' ? 'integration action' : 'upload'} was approved but execution failed: ${err.message}`).catch(() => {});
70
+ daemon.processes.sendMessage(rp.agentId, `Your ${rp.type === 'integration_exec' ? 'integration action' : 'upload'} was approved but execution failed: ${err.message}`, 'system').catch(() => {});
71
71
  }
72
72
  }
73
73
  }
@@ -4211,6 +4211,33 @@ Keep responses concise. Help them think, don't lecture them about the system the
4211
4211
  res.json(daemon.federation.getStatus());
4212
4212
  });
4213
4213
 
4214
+ app.get('/api/federation/test', async (req, res) => {
4215
+ const target = req.query.target;
4216
+ if (!target) return res.status(400).json({ error: 'target required' });
4217
+ const host = target.split(':')[0];
4218
+ const privatePatterns = [
4219
+ /^127\./, /^10\./, /^192\.168\./, /^172\.(1[6-9]|2\d|3[01])\./,
4220
+ /^0\./, /^169\.254\./, /^localhost$/i, /^::1$/, /^\[::1\]$/,
4221
+ /^0\.0\.0\.0$/, /^fc/i, /^fd/i, /^fe80/i,
4222
+ ];
4223
+ if (privatePatterns.some(p => p.test(host))) {
4224
+ return res.status(400).json({ error: 'Private/local addresses are not allowed' });
4225
+ }
4226
+ try {
4227
+ const controller = new AbortController();
4228
+ const timeout = setTimeout(() => controller.abort(), 5000);
4229
+ const resp = await fetch(`http://${target}/api/health`, { signal: controller.signal });
4230
+ clearTimeout(timeout);
4231
+ if (resp.ok) {
4232
+ const data = await resp.json();
4233
+ return res.json({ reachable: true, version: data.version, peerId: data.daemonId, agents: data.agents });
4234
+ }
4235
+ res.json({ reachable: false });
4236
+ } catch {
4237
+ res.json({ reachable: false });
4238
+ }
4239
+ });
4240
+
4214
4241
  // List peers
4215
4242
  app.get('/api/federation/peers', (req, res) => {
4216
4243
  res.json(daemon.federation.getPeers());
@@ -1434,7 +1434,7 @@ For normal file edits within your scope, proceed without review.
1434
1434
  if (existing && (existing.status === 'running' || existing.status === 'starting')) {
1435
1435
  // Agent already active — reuse it instead of spawning a duplicate
1436
1436
  if (config.prompt) {
1437
- this.sendMessage(existing.id, config.prompt).catch((err) => {
1437
+ this.sendMessage(existing.id, config.prompt, 'planner').catch((err) => {
1438
1438
  console.error(`[Groove] Phase 2 reuse message failed for ${existing.name}: ${err.message}`);
1439
1439
  });
1440
1440
  }
@@ -1527,7 +1527,7 @@ For normal file edits within your scope, proceed without review.
1527
1527
  const message = `Your teammate ${completedAgent.name} (${completedAgent.role}) just finished their work.${fileList}${result ? `\n\nTheir summary:\n${result.slice(0, 2000)}` : ''}\n\nPlease audit their changes: verify correctness, check for bugs, run tests if available, and report any issues.`;
1528
1528
 
1529
1529
  // Send message to the QC agent via the instruct flow
1530
- this.sendMessage(qc.id, message).catch((err) => {
1530
+ this.sendMessage(qc.id, message, 'system').catch((err) => {
1531
1531
  console.error(`[Groove] QC auto-trigger failed: ${err.message}`);
1532
1532
  });
1533
1533
 
@@ -1651,7 +1651,7 @@ For normal file edits within your scope, proceed without review.
1651
1651
  if (target.status === 'running') {
1652
1652
  let sent = false;
1653
1653
  if (this.hasAgentLoop(target.id)) {
1654
- this.sendMessage(target.id, message).catch(() => {});
1654
+ this.sendMessage(target.id, message, 'agent').catch(() => {});
1655
1655
  sent = true;
1656
1656
  }
1657
1657
  if (!sent && this.daemon.journalist) {
@@ -2189,7 +2189,7 @@ For normal file edits within your scope, proceed without review.
2189
2189
  * Send a message to a running agent loop.
2190
2190
  * Returns true if the message was sent, false if the agent doesn't have an active loop.
2191
2191
  */
2192
- async sendMessage(agentId, message) {
2192
+ async sendMessage(agentId, message, source = 'user') {
2193
2193
  const handle = this.handles.get(agentId);
2194
2194
  if (!handle?.loop) return false;
2195
2195
 
@@ -2200,7 +2200,7 @@ For normal file edits within your scope, proceed without review.
2200
2200
  const wrapped = agent ? wrapWithRoleReminder(agent.role, message) : message;
2201
2201
 
2202
2202
  if (this.daemon.trajectoryCapture) {
2203
- try { this.daemon.trajectoryCapture.onUserMessage(agentId, message); } catch (e) { /* fail silent */ }
2203
+ try { this.daemon.trajectoryCapture.onUserMessage(agentId, message, source); } catch (e) { /* fail silent */ }
2204
2204
  }
2205
2205
 
2206
2206
  loop.sendMessage(wrapped).catch(() => {});