groove-dev 0.27.89 → 0.27.91

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 (29) hide show
  1. package/moe-training/client/parsers/claude-code.js +0 -2
  2. package/moe-training/client/session-attestation.js +2 -1
  3. package/moe-training/client/trajectory-capture.js +6 -0
  4. package/moe-training/test/client/parsers/claude-code.test.js +2 -2
  5. package/node_modules/@groove-dev/cli/package.json +1 -1
  6. package/node_modules/@groove-dev/daemon/package.json +1 -1
  7. package/node_modules/@groove-dev/daemon/src/api.js +16 -9
  8. package/node_modules/@groove-dev/daemon/src/conversations.js +32 -6
  9. package/node_modules/@groove-dev/daemon/src/providers/claude-code.js +1 -1
  10. package/node_modules/@groove-dev/daemon/src/providers/codex.js +1 -1
  11. package/node_modules/@groove-dev/daemon/src/providers/gemini.js +1 -1
  12. package/node_modules/@groove-dev/daemon/src/providers/grok.js +2 -2
  13. package/node_modules/@groove-dev/gui/dist/assets/{index-BKD8JAsV.js → index-MLIZRMj1.js} +1 -1
  14. package/node_modules/@groove-dev/gui/dist/index.html +1 -1
  15. package/node_modules/@groove-dev/gui/package.json +1 -1
  16. package/node_modules/@groove-dev/gui/src/components/chat/chat-view.jsx +1 -3
  17. package/package.json +1 -1
  18. package/packages/cli/package.json +1 -1
  19. package/packages/daemon/package.json +1 -1
  20. package/packages/daemon/src/api.js +16 -9
  21. package/packages/daemon/src/conversations.js +32 -6
  22. package/packages/daemon/src/providers/claude-code.js +1 -1
  23. package/packages/daemon/src/providers/codex.js +1 -1
  24. package/packages/daemon/src/providers/gemini.js +1 -1
  25. package/packages/daemon/src/providers/grok.js +2 -2
  26. package/packages/gui/dist/assets/{index-BKD8JAsV.js → index-MLIZRMj1.js} +1 -1
  27. package/packages/gui/dist/index.html +1 -1
  28. package/packages/gui/package.json +1 -1
  29. package/packages/gui/src/components/chat/chat-view.jsx +1 -3
@@ -30,7 +30,6 @@ export class ClaudeCodeParser {
30
30
  results.push({
31
31
  type: 'thought',
32
32
  content: block.text || '',
33
- token_count: jsonEvent.message?.usage?.output_tokens || 0,
34
33
  });
35
34
  } else if (block.type === 'tool_use') {
36
35
  this._pendingToolUse.set(block.id, block);
@@ -67,7 +66,6 @@ export class ClaudeCodeParser {
67
66
  return {
68
67
  type: 'resolution',
69
68
  content: typeof jsonEvent.result === 'string' ? jsonEvent.result : JSON.stringify(jsonEvent.result),
70
- token_count: jsonEvent.total_tokens_used || 0,
71
69
  };
72
70
  }
73
71
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { platform, arch, cpus, totalmem, hostname, release, endianness } from 'node:os';
4
4
  import { createHash } from 'node:crypto';
5
+ import { fileURLToPath } from 'node:url';
5
6
  import { generateECDHKeypair, deriveSharedSecret, signEnvelope, computeAppHash } from '../shared/crypto.js';
6
7
 
7
8
  export class SessionAttestation {
@@ -14,7 +15,7 @@ export class SessionAttestation {
14
15
  const keypair = generateECDHKeypair();
15
16
  let appVersionHash = '';
16
17
  try {
17
- appVersionHash = computeAppHash(new URL(import.meta.url).pathname);
18
+ appVersionHash = computeAppHash(fileURLToPath(import.meta.url));
18
19
  } catch {
19
20
  appVersionHash = 'unknown';
20
21
  }
@@ -172,6 +172,12 @@ export class TrajectoryCapture {
172
172
  ev.arguments = this._scrubObject(ev.arguments);
173
173
  }
174
174
 
175
+ if (!ev.token_count || ev.token_count < 2) {
176
+ const text = ev.content || '';
177
+ const argsLen = ev.arguments ? JSON.stringify(ev.arguments).length : 0;
178
+ ev.token_count = Math.max(1, Math.ceil((text.length + argsLen) / 4));
179
+ }
180
+
175
181
  const step = {
176
182
  step: ++ctx.stepCount,
177
183
  type: ev.type,
@@ -17,7 +17,7 @@ describe('ClaudeCodeParser', () => {
17
17
  const result = parser.parseEvent(event);
18
18
  assert.equal(result.type, 'thought');
19
19
  assert.equal(result.content, 'I need to fix the bug');
20
- assert.equal(result.token_count, 50);
20
+ assert.equal(result.token_count, undefined);
21
21
  });
22
22
 
23
23
  it('parses tool_use block as action', () => {
@@ -79,7 +79,7 @@ describe('ClaudeCodeParser', () => {
79
79
  const result = parser.parseEvent(event);
80
80
  assert.equal(result.type, 'resolution');
81
81
  assert.equal(result.content, 'Task completed successfully');
82
- assert.equal(result.token_count, 500);
82
+ assert.equal(result.token_count, undefined);
83
83
  });
84
84
 
85
85
  it('extracts tokens from assistant event', () => {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groove-dev/cli",
3
- "version": "0.27.89",
3
+ "version": "0.27.91",
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.89",
3
+ "version": "0.27.91",
4
4
  "description": "GROOVE daemon — agent orchestration engine",
5
5
  "license": "FSL-1.1-Apache-2.0",
6
6
  "type": "module",
@@ -1115,8 +1115,8 @@ export function createApi(app, daemon) {
1115
1115
  app.post('/api/conversations', async (req, res) => {
1116
1116
  try {
1117
1117
  const { provider, model, title, mode, reasoning_effort, verbosity } = req.body;
1118
- if (!provider || typeof provider !== 'string') {
1119
- return res.status(400).json({ error: 'provider is required' });
1118
+ if (provider && typeof provider !== 'string') {
1119
+ return res.status(400).json({ error: 'provider must be a string' });
1120
1120
  }
1121
1121
  if (mode && mode !== 'api' && mode !== 'agent') {
1122
1122
  return res.status(400).json({ error: 'mode must be "api" or "agent"' });
@@ -4504,8 +4504,10 @@ Keep responses concise. Help them think, don't lecture them about the system the
4504
4504
  }
4505
4505
 
4506
4506
  daemon.config.defaultProvider = provider;
4507
+ daemon.config.defaultChatProvider = provider;
4507
4508
  if (model && typeof model === 'string' && model.length <= 100) {
4508
4509
  daemon.config.defaultModel = model.trim();
4510
+ daemon.config.defaultChatModel = model.trim();
4509
4511
  }
4510
4512
  const { saveConfig } = await import('./firstrun.js');
4511
4513
  saveConfig(daemon.grooveDir, daemon.config);
@@ -4537,15 +4539,20 @@ Keep responses concise. Help them think, don't lecture them about the system the
4537
4539
  saveConfig(daemon.grooveDir, daemon.config);
4538
4540
 
4539
4541
  if (enabled) {
4540
- const userId = ConsentManager.getOrCreateUserId();
4541
- const consent = new ConsentManager();
4542
4542
  try {
4543
- consent.recordConsent(userId, true, '1.0');
4544
- } finally {
4545
- consent.close();
4543
+ const userId = ConsentManager.getOrCreateUserId();
4544
+ const consent = new ConsentManager();
4545
+ try {
4546
+ consent.recordConsent(userId, true, '1.0');
4547
+ } finally {
4548
+ consent.close();
4549
+ }
4550
+ await daemon._initTrajectoryCapture();
4551
+ daemon.state.set('training_enrolled_at', new Date().toISOString());
4552
+ } catch (e) {
4553
+ daemon.config.training_opt_in = false;
4554
+ return res.status(500).json({ error: 'Failed to enable data sharing', detail: e.message });
4546
4555
  }
4547
- await daemon._initTrajectoryCapture();
4548
- daemon.state.set('training_enrolled_at', new Date().toISOString());
4549
4556
  } else {
4550
4557
  if (daemon.trajectoryCapture) {
4551
4558
  try { await daemon.trajectoryCapture.shutdown(); } catch (e) { /* */ }
@@ -55,6 +55,22 @@ export class ConversationManager {
55
55
  return null;
56
56
  }
57
57
 
58
+ _resolveAutoProviderModel(preferredProvider) {
59
+ const priority = ['claude-code', 'codex', 'gemini', 'grok', 'ollama'];
60
+ const candidates = preferredProvider ? [preferredProvider] : priority;
61
+
62
+ for (const pid of candidates) {
63
+ if (!isProviderInstalled(pid)) continue;
64
+ const p = getProvider(pid);
65
+ if (!p) continue;
66
+ const models = p.constructor.models || [];
67
+ const chatModel = models.find((m) => m.type !== 'image') || models[0];
68
+ if (chatModel) return { provider: pid, model: chatModel.id };
69
+ }
70
+
71
+ return { provider: 'claude-code', model: 'claude-sonnet-4-6' };
72
+ }
73
+
58
74
  async create(provider, model, title, mode = 'api', options = {}) {
59
75
  if (!provider && this.daemon.config?.defaultChatProvider) {
60
76
  provider = this.daemon.config.defaultChatProvider;
@@ -63,6 +79,12 @@ export class ConversationManager {
63
79
  model = this.daemon.config.defaultChatModel;
64
80
  }
65
81
 
82
+ if (!provider || !model) {
83
+ const resolved = this._resolveAutoProviderModel(provider);
84
+ if (!provider) provider = resolved.provider;
85
+ if (!model) model = resolved.model;
86
+ }
87
+
66
88
  const id = randomUUID().slice(0, 12);
67
89
  const now = new Date().toISOString();
68
90
 
@@ -328,12 +350,16 @@ export class ConversationManager {
328
350
  let providerName = conv.provider;
329
351
 
330
352
  if (!provider || !isProviderInstalled(conv.provider)) {
331
- const priority = ['claude-code', 'gemini', 'codex', 'ollama'];
332
- const fallbackId = priority.find((p) => isProviderInstalled(p));
333
- if (!fallbackId) throw new Error('No provider available for chat');
334
- provider = getProvider(fallbackId);
335
- providerName = fallbackId;
336
- modelId = null;
353
+ const resolved = this._resolveAutoProviderModel(null);
354
+ provider = getProvider(resolved.provider);
355
+ if (!provider) throw new Error('No provider available for chat');
356
+ providerName = resolved.provider;
357
+ modelId = resolved.model;
358
+ }
359
+
360
+ if (!modelId) {
361
+ const resolved = this._resolveAutoProviderModel(providerName);
362
+ modelId = resolved.model;
337
363
  }
338
364
 
339
365
  // Build messages array for direct API call
@@ -282,7 +282,7 @@ export class ClaudeCodeProvider extends Provider {
282
282
  let finished = false;
283
283
  const finish = () => { if (!finished) { finished = true; onDone(); } };
284
284
  const body = JSON.stringify({
285
- model: model || 'claude-sonnet-4-6',
285
+ model,
286
286
  messages,
287
287
  max_tokens: 8192,
288
288
  stream: true,
@@ -163,7 +163,7 @@ export class CodexProvider extends Provider {
163
163
  const effort = reasoningEffort || 'medium';
164
164
  const verb = verbosity || 'medium';
165
165
  const body = {
166
- model: model || 'gpt-5.5',
166
+ model,
167
167
  input: previousResponseId ? [messages[messages.length - 1]] : messages,
168
168
  stream: true,
169
169
  reasoning: { effort },
@@ -94,7 +94,7 @@ export class GeminiProvider extends Provider {
94
94
  const controller = new AbortController();
95
95
  let finished = false;
96
96
  const finish = () => { if (!finished) { finished = true; onDone(); } };
97
- const m = model || 'gemini-2.5-flash';
97
+ const m = model;
98
98
  const contents = messages.map((msg) => ({
99
99
  role: msg.role === 'assistant' ? 'model' : 'user',
100
100
  parts: [{ text: msg.content }],
@@ -58,7 +58,7 @@ export class GrokProvider extends Provider {
58
58
  getLoopConfig(agent) {
59
59
  return {
60
60
  apiBase: 'https://api.x.ai/v1',
61
- model: agent.model || 'grok-4-1-fast',
61
+ model: agent.model,
62
62
  contextWindow: 131072,
63
63
  temperature: 0.1,
64
64
  maxResponseTokens: 16384,
@@ -97,7 +97,7 @@ export class GrokProvider extends Provider {
97
97
  'Content-Type': 'application/json',
98
98
  },
99
99
  body: JSON.stringify({
100
- model: model || 'grok-4-1-fast',
100
+ model,
101
101
  messages,
102
102
  stream: true,
103
103
  }),