groove-dev 0.27.168 → 0.27.169

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 (35) hide show
  1. package/node_modules/@groove-dev/cli/package.json +1 -1
  2. package/node_modules/@groove-dev/daemon/package.json +1 -1
  3. package/node_modules/@groove-dev/daemon/src/index.js +15 -2
  4. package/node_modules/@groove-dev/daemon/src/process.js +1 -1
  5. package/node_modules/@groove-dev/daemon/src/providers/claude-code.js +7 -1
  6. package/node_modules/@groove-dev/daemon/src/registry.js +3 -0
  7. package/node_modules/@groove-dev/daemon/src/state.js +7 -2
  8. package/node_modules/@groove-dev/daemon/src/validate.js +1 -0
  9. package/node_modules/@groove-dev/gui/dist/assets/{index-CSMIQsrG.js → index-BcWo4sq-.js} +140 -140
  10. package/node_modules/@groove-dev/gui/dist/assets/index-BvXojcnr.css +1 -0
  11. package/node_modules/@groove-dev/gui/dist/index.html +2 -2
  12. package/node_modules/@groove-dev/gui/package.json +1 -1
  13. package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +6 -0
  14. package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +30 -1
  15. package/node_modules/@groove-dev/gui/src/components/editor/terminal.jsx +64 -42
  16. package/package.json +1 -1
  17. package/packages/cli/package.json +1 -1
  18. package/packages/daemon/package.json +1 -1
  19. package/packages/daemon/src/index.js +15 -2
  20. package/packages/daemon/src/process.js +1 -1
  21. package/packages/daemon/src/providers/claude-code.js +7 -1
  22. package/packages/daemon/src/registry.js +3 -0
  23. package/packages/daemon/src/state.js +7 -2
  24. package/packages/daemon/src/validate.js +1 -0
  25. package/packages/gui/dist/assets/{index-CSMIQsrG.js → index-BcWo4sq-.js} +140 -140
  26. package/packages/gui/dist/assets/index-BvXojcnr.css +1 -0
  27. package/packages/gui/dist/index.html +2 -2
  28. package/packages/gui/package.json +1 -1
  29. package/packages/gui/src/components/agents/agent-feed.jsx +6 -0
  30. package/packages/gui/src/components/agents/spawn-wizard.jsx +30 -1
  31. package/packages/gui/src/components/editor/terminal.jsx +64 -42
  32. package/node_modules/@groove-dev/gui/dist/assets/index-BJVNpGIp.css +0 -1
  33. package/packages/gui/dist/assets/index-BJVNpGIp.css +0 -1
  34. package/ssh/error.png +0 -0
  35. package/terminal/Screenshot_2026-05-19_at_12.20.15_PM.png +0 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groove-dev/cli",
3
- "version": "0.27.168",
3
+ "version": "0.27.169",
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.168",
3
+ "version": "0.27.169",
4
4
  "description": "GROOVE daemon — agent orchestration engine",
5
5
  "license": "FSL-1.1-Apache-2.0",
6
6
  "type": "module",
@@ -717,9 +717,22 @@ export class Daemon {
717
717
  this._gc();
718
718
  this._gcInterval = setInterval(() => this._gc(), 24 * 60 * 60 * 1000);
719
719
 
720
- // Periodic state save — crash protection (every 30s)
720
+ // Periodic state save — crash protection (every 5s)
721
+ this._lastSavedAgentCount = this.registry.getAll().length;
721
722
  this._stateSaveInterval = setInterval(async () => {
722
- try { this.state.set('agents', this.registry.getAll()); await this.state.save(); } catch {}
723
+ try {
724
+ const agents = this.registry.getAll();
725
+ // Safety: never overwrite a populated state with an empty one.
726
+ // An empty registry while agents existed before signals a bug, not
727
+ // legitimate cleanup — skip the save so .bak stays valid.
728
+ if (agents.length === 0 && this._lastSavedAgentCount > 0) {
729
+ console.warn(`[Groove:State] Skipping save — registry unexpectedly empty (was ${this._lastSavedAgentCount} agents). This prevents silent data loss.`);
730
+ return;
731
+ }
732
+ this._lastSavedAgentCount = agents.length;
733
+ this.state.set('agents', agents);
734
+ await this.state.save();
735
+ } catch {}
723
736
  }, 5000);
724
737
  }
725
738
 
@@ -2526,7 +2526,7 @@ After fixing all issues, run tests (npm test) and build (npm run build) to verif
2526
2526
  locks.release(agentId);
2527
2527
 
2528
2528
  // Build resume command
2529
- const { command: rawCommand, args, env } = provider.buildResumeCommand(sessionId, message, config.model);
2529
+ const { command: rawCommand, args, env } = provider.buildResumeCommand(sessionId, message, config.model, { fast: config.fast });
2530
2530
  const command = resolveProviderCommand(config.provider || 'claude-code') || rawCommand;
2531
2531
 
2532
2532
  // Set up log capture
@@ -39,6 +39,7 @@ export class ClaudeCodeProvider extends Provider {
39
39
  static authType = 'subscription';
40
40
  static managesOwnContext = true; // Claude Code compacts context internally (~25-37% → 2-8%)
41
41
  static models = [
42
+ { id: 'claude-opus-4-8', name: 'Claude Opus 4.8', tier: 'heavy', contextWindow: 1_000_000 },
42
43
  { id: 'claude-opus-4-6', name: 'Claude Opus 4.6', tier: 'heavy', contextWindow: 1_000_000 },
43
44
  { id: 'claude-sonnet-4-6', name: 'Claude Sonnet 4.6', tier: 'medium', contextWindow: 200_000 },
44
45
  { id: 'claude-haiku-4-5-20251001', name: 'Claude Haiku 4.5', tier: 'light', contextWindow: 200_000 },
@@ -104,6 +105,10 @@ export class ClaudeCodeProvider extends Provider {
104
105
  args.push('--effort', agent.effort);
105
106
  }
106
107
 
108
+ if (agent.fast) {
109
+ args.push('--fast');
110
+ }
111
+
107
112
  // Pass the initial prompt as positional arg (includes GROOVE context)
108
113
  const fullPrompt = this.buildFullPrompt(agent);
109
114
  if (fullPrompt) {
@@ -117,13 +122,14 @@ export class ClaudeCodeProvider extends Provider {
117
122
  };
118
123
  }
119
124
 
120
- buildResumeCommand(sessionId, prompt, model) {
125
+ buildResumeCommand(sessionId, prompt, model, options = {}) {
121
126
  // Resume a previous session — preserves full conversation history
122
127
  // No cold start, no handoff brief needed
123
128
  const args = ['--resume', sessionId, '--output-format', 'stream-json', '--verbose', '--dangerously-skip-permissions'];
124
129
  const knockSettings = ClaudeCodeProvider._buildKnockSettings();
125
130
  if (knockSettings) args.push('--settings', knockSettings);
126
131
  if (model) args.push('--model', model);
132
+ if (options.fast) args.push('--fast');
127
133
  if (prompt) args.push(prompt);
128
134
  return { command: 'claude', args, env: {} };
129
135
  }
@@ -97,6 +97,9 @@ export class Registry extends EventEmitter {
97
97
  const agent = this.agents.get(id);
98
98
  if (!agent) return false;
99
99
 
100
+ const caller = new Error().stack.split('\n').slice(1, 4).map(l => l.trim()).join(' <- ');
101
+ console.log(`[Groove:Registry] REMOVE agent ${agent.name} (${id}) role=${agent.role} status=${agent.status} team=${agent.teamId} silent=${silent} | ${caller}`);
102
+
100
103
  this.agents.delete(id);
101
104
  if (silent) {
102
105
  this._pendingRemovals.push(id);
@@ -3,7 +3,8 @@
3
3
 
4
4
  import { readFileSync, writeFileSync, existsSync, readdirSync, unlinkSync, renameSync, copyFileSync } from 'fs';
5
5
  import { writeFile } from 'node:fs/promises';
6
- import { resolve } from 'path';
6
+ import { resolve, dirname } from 'path';
7
+ import { randomUUID } from 'crypto';
7
8
 
8
9
  export class StateManager {
9
10
  constructor(grooveDir) {
@@ -39,7 +40,11 @@ export class StateManager {
39
40
  if (existsSync(this.path)) {
40
41
  try { copyFileSync(this.path, this.backupPath); } catch { /* non-fatal */ }
41
42
  }
42
- await writeFile(this.path, JSON.stringify(this.data, null, 2));
43
+ // Atomic write: write to temp file, then rename (rename is atomic on POSIX).
44
+ // Prevents SIGKILL during write from leaving state.json truncated/empty.
45
+ const tmpPath = resolve(dirname(this.path), `state.${randomUUID().slice(0, 8)}.tmp`);
46
+ await writeFile(tmpPath, JSON.stringify(this.data, null, 2));
47
+ renameSync(tmpPath, this.path);
43
48
  }
44
49
 
45
50
  get(key) {
@@ -139,6 +139,7 @@ export function validateAgentConfig(config) {
139
139
  verbosity,
140
140
  effort,
141
141
  routingMode,
142
+ fast: config.fast === true ? true : undefined,
142
143
  labPresetId: (typeof config.labPresetId === 'string' && config.labPresetId.length <= 64) ? config.labPresetId : undefined,
143
144
  keeperTags: Array.isArray(config.keeperTags) ? config.keeperTags.filter(t => typeof t === 'string').slice(0, 20) : undefined,
144
145
  };