groove-dev 0.27.26 → 0.27.28

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 (49) hide show
  1. package/.groove-staging/state.json +3 -0
  2. package/.groove-staging/timeline.json +13 -0
  3. package/CLAUDE.md +0 -10
  4. package/DECENTRALIZED_NET_WP_V1.md +871 -0
  5. package/README.md +28 -0
  6. package/SECURITY_SWEEP.md +228 -0
  7. package/decentralized-net/ACTION_PLAN.md +422 -0
  8. package/node_modules/@groove-dev/cli/package.json +1 -1
  9. package/node_modules/@groove-dev/daemon/package.json +1 -1
  10. package/node_modules/@groove-dev/daemon/src/api.js +99 -0
  11. package/node_modules/@groove-dev/daemon/src/introducer.js +7 -7
  12. package/node_modules/@groove-dev/daemon/src/journalist.js +36 -6
  13. package/node_modules/@groove-dev/daemon/src/memory.js +29 -10
  14. package/node_modules/@groove-dev/daemon/src/process.js +29 -12
  15. package/node_modules/@groove-dev/daemon/src/providers/claude-code.js +26 -1
  16. package/node_modules/@groove-dev/daemon/src/providers/codex.js +34 -11
  17. package/node_modules/@groove-dev/daemon/src/rotator.js +24 -1
  18. package/node_modules/@groove-dev/daemon/test/introducer.test.js +63 -0
  19. package/node_modules/@groove-dev/daemon/test/journalist.test.js +106 -0
  20. package/node_modules/@groove-dev/daemon/test/memory.test.js +49 -0
  21. package/node_modules/@groove-dev/daemon/test/rotator.test.js +99 -0
  22. package/node_modules/@groove-dev/gui/dist/assets/{index-DieCV-v1.js → index-Ch1N9G4Z.js} +1728 -1728
  23. package/node_modules/@groove-dev/gui/dist/index.html +1 -1
  24. package/node_modules/@groove-dev/gui/package.json +1 -1
  25. package/node_modules/@groove-dev/gui/src/components/agents/agent-config.jsx +147 -21
  26. package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +206 -44
  27. package/node_modules/@groove-dev/gui/src/components/marketplace/integration-wizard.jsx +11 -24
  28. package/node_modules/@groove-dev/gui/src/components/marketplace/marketplace-card.jsx +1 -36
  29. package/node_modules/@groove-dev/gui/src/lib/integration-logos.js +39 -0
  30. package/package.json +1 -1
  31. package/packages/cli/package.json +1 -1
  32. package/packages/daemon/package.json +1 -1
  33. package/packages/daemon/src/api.js +99 -0
  34. package/packages/daemon/src/introducer.js +7 -7
  35. package/packages/daemon/src/journalist.js +36 -6
  36. package/packages/daemon/src/memory.js +29 -10
  37. package/packages/daemon/src/process.js +29 -12
  38. package/packages/daemon/src/providers/claude-code.js +26 -1
  39. package/packages/daemon/src/providers/codex.js +34 -11
  40. package/packages/daemon/src/rotator.js +24 -1
  41. package/packages/gui/dist/assets/{index-DieCV-v1.js → index-Ch1N9G4Z.js} +1728 -1728
  42. package/packages/gui/dist/index.html +1 -1
  43. package/packages/gui/package.json +1 -1
  44. package/packages/gui/src/components/agents/agent-config.jsx +147 -21
  45. package/packages/gui/src/components/agents/spawn-wizard.jsx +206 -44
  46. package/packages/gui/src/components/marketplace/integration-wizard.jsx +11 -24
  47. package/packages/gui/src/components/marketplace/marketplace-card.jsx +1 -36
  48. package/packages/gui/src/lib/integration-logos.js +39 -0
  49. package/MUST_FIX_ISSUES.md +0 -305
@@ -95,6 +95,7 @@ export class CodexProvider extends Provider {
95
95
  if (agent.prompt) args.push(agent.prompt);
96
96
 
97
97
  this._currentModel = agent.model;
98
+ this._sessionInputTokens = 0;
98
99
 
99
100
  return {
100
101
  command: 'codex',
@@ -109,6 +110,11 @@ export class CodexProvider extends Provider {
109
110
  return { command: 'codex', args, env: {} };
110
111
  }
111
112
 
113
+ _getMaxContext() {
114
+ const model = CodexProvider.models.find((m) => m.id === this._currentModel);
115
+ return model?.maxContext || 200000;
116
+ }
117
+
112
118
  switchModel(agent, newModel) {
113
119
  return false; // Codex doesn't support mid-session model switch
114
120
  }
@@ -175,36 +181,48 @@ export class CodexProvider extends Provider {
175
181
 
176
182
  case 'item.completed': {
177
183
  const item = event.item || {};
184
+
185
+ // Accumulate usage for intermediate context estimation.
186
+ // Codex only reports full contextUsage at turn.completed — without this,
187
+ // the rotator sees stale contextUsage between turns and never triggers.
188
+ if (event.usage) {
189
+ this._sessionInputTokens += event.usage.input_tokens || 0;
190
+ }
191
+
192
+ let result = null;
178
193
  if (item.type === 'agent_message') {
179
- return {
194
+ result = {
180
195
  type: 'activity', subtype: 'assistant',
181
196
  data: [{ type: 'text', text: item.text || '' }],
182
197
  };
183
- }
184
- if (item.type === 'command_execution') {
198
+ } else if (item.type === 'command_execution') {
185
199
  const output = (item.aggregated_output || '').slice(0, 2000);
186
- return {
200
+ result = {
187
201
  type: 'activity', subtype: 'assistant',
188
202
  data: [
189
203
  { type: 'tool_use', id: item.id || 'exec', name: 'Bash', input: { command: item.command } },
190
204
  ...(output ? [{ type: 'text', text: output }] : []),
191
205
  ],
192
206
  };
193
- }
194
- if (item.type === 'todo_list') {
207
+ } else if (item.type === 'todo_list') {
195
208
  const steps = (item.items || []).map((s) => `${s.completed ? '✓' : '○'} ${s.text}`).join('\n');
196
- return {
209
+ result = {
197
210
  type: 'activity', subtype: 'assistant',
198
211
  data: [{ type: 'text', text: steps }],
199
212
  };
200
- }
201
- if (item.type === 'file_edit' || item.type === 'file_write' || item.type === 'file_read') {
202
- return {
213
+ } else if (item.type === 'file_edit' || item.type === 'file_write' || item.type === 'file_read') {
214
+ result = {
203
215
  type: 'activity', subtype: 'assistant',
204
216
  data: [{ type: 'tool_use', id: item.id || 'file', name: item.type === 'file_read' ? 'Read' : item.type === 'file_write' ? 'Write' : 'Edit', input: { path: item.path || item.file || '' } }],
205
217
  };
206
218
  }
207
- return null;
219
+
220
+ // Attach intermediate context estimate so all 7 layers see Codex progress
221
+ if (result && this._sessionInputTokens > 0) {
222
+ result.contextUsage = this._sessionInputTokens / this._getMaxContext();
223
+ }
224
+
225
+ return result;
208
226
  }
209
227
 
210
228
  case 'turn.completed': {
@@ -215,11 +233,15 @@ export class CodexProvider extends Provider {
215
233
  const outputTokens = usage.output_tokens || 0;
216
234
  const cachedTokens = usage.cached_input_tokens || 0;
217
235
  const totalTokens = inputTokens + outputTokens;
236
+ const cacheCreationTokens = cachedTokens > 0 ? Math.max(0, inputTokens - cachedTokens) : 0;
218
237
 
219
238
  const model = CodexProvider.models.find((m) => m.id === this._currentModel);
220
239
  const pricing = model?.pricing;
221
240
  const maxContext = model?.maxContext || 200000;
222
241
 
242
+ // Sync accumulator to actual cumulative value from turn completion
243
+ this._sessionInputTokens = inputTokens;
244
+
223
245
  let estimatedCostUsd = 0;
224
246
  if (pricing) {
225
247
  const newInput = inputTokens - cachedTokens;
@@ -235,6 +257,7 @@ export class CodexProvider extends Provider {
235
257
  inputTokens,
236
258
  outputTokens,
237
259
  cacheReadTokens: cachedTokens,
260
+ cacheCreationTokens,
238
261
  contextUsage: inputTokens / maxContext,
239
262
  estimatedCostUsd,
240
263
  costSource: pricing ? 'calculated' : 'estimated',
@@ -31,6 +31,7 @@ export class Rotator extends EventEmitter {
31
31
  this.rotationHistory = [];
32
32
  this.rotating = new Set();
33
33
  this.lastRotationTime = new Map(); // agentId -> timestamp of last rotation
34
+ this._lastContextState = new Map(); // agentId -> { contextUsage, timestamp }
34
35
  this.enabled = false;
35
36
  this.liveScores = {};
36
37
  this.scoreHistory = {};
@@ -180,6 +181,25 @@ export class Rotator extends EventEmitter {
180
181
  continue;
181
182
  }
182
183
 
184
+ // Stale context fallback — safety net for providers (like Codex) that don't
185
+ // report intermediate contextUsage. If contextUsage hasn't changed in 120+
186
+ // seconds but tokens are being consumed, estimate from total tokens.
187
+ const knownCtx = this._lastContextState.get(agent.id);
188
+ if (!knownCtx || knownCtx.contextUsage !== agent.contextUsage) {
189
+ this._lastContextState.set(agent.id, { contextUsage: agent.contextUsage, timestamp: Date.now() });
190
+ } else if (agent.tokensUsed > 0 && (Date.now() - knownCtx.timestamp) >= 120_000) {
191
+ const providerClass = getProvider(agent.provider)?.constructor;
192
+ const models = providerClass?.models || [];
193
+ const model = models.find((m) => m.id === agent.model) || models[0];
194
+ const maxContext = model?.maxContext || 200000;
195
+ const estimatedContext = agent.tokensUsed / maxContext;
196
+ if (estimatedContext >= HARD_CEILING) {
197
+ console.log(` Rotator: ${agent.name} estimated context ${Math.round(estimatedContext * 100)}% (stale contextUsage fallback)`);
198
+ await this.rotate(agent.id, { reason: 'estimated_context_ceiling' });
199
+ continue;
200
+ }
201
+ }
202
+
183
203
  // Cooldown — skip threshold/quality rotation if recently rotated
184
204
  if (this._isOnCooldown(agent.id)) continue;
185
205
 
@@ -274,7 +294,7 @@ export class Rotator extends EventEmitter {
274
294
  oldTokens: agent.tokensUsed,
275
295
  contextUsage: agent.contextUsage,
276
296
  brief: brief.slice(0, 4000),
277
- }, agent.workingDir);
297
+ }, agent.workingDir, agent.teamId);
278
298
  }
279
299
 
280
300
  const record = {
@@ -312,6 +332,7 @@ export class Rotator extends EventEmitter {
312
332
  workingDir: agent.workingDir,
313
333
  name: agent.name,
314
334
  teamId: agent.teamId,
335
+ isRotation: true,
315
336
  });
316
337
  } catch (spawnErr) {
317
338
  // Spawn failed — re-add old agent so the user can see and retry.
@@ -499,6 +520,7 @@ export class Rotator extends EventEmitter {
499
520
  const naturalCompactions = this.rotationHistory.filter((r) => r.reason === 'natural_compaction').length;
500
521
  const hardCeilingRotations = this.rotationHistory.filter((r) => r.reason === 'hard_ceiling').length;
501
522
  const tokenCeilingRotations = this.rotationHistory.filter((r) => r.reason === 'token_ceiling').length;
523
+ const estimatedCeilingRotations = this.rotationHistory.filter((r) => r.reason === 'estimated_context_ceiling').length;
502
524
  return {
503
525
  enabled: this.enabled,
504
526
  totalRotations,
@@ -508,6 +530,7 @@ export class Rotator extends EventEmitter {
508
530
  naturalCompactions,
509
531
  hardCeilingRotations,
510
532
  tokenCeilingRotations,
533
+ estimatedCeilingRotations,
511
534
  rotating: Array.from(this.rotating),
512
535
  liveScores: this.liveScores,
513
536
  scoreHistory: this.scoreHistory,