groove-dev 0.27.193 → 0.27.194

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groove-dev/cli",
3
- "version": "0.27.193",
3
+ "version": "0.27.194",
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.193",
3
+ "version": "0.27.194",
4
4
  "description": "GROOVE daemon — agent orchestration engine",
5
5
  "license": "FSL-1.1-Apache-2.0",
6
6
  "type": "module",
@@ -367,7 +367,7 @@ export class ProcessManager {
367
367
  this.daemon = daemon;
368
368
  this.handles = new Map(); // agentId -> { proc, logStream }
369
369
  this.peakContextUsage = new Map(); // agentId -> highest contextUsage seen
370
- this.pendingMessages = new Map(); // agentId -> { message, timestamp }
370
+ this.pendingMessages = new Map(); // agentId -> [{ message, timestamp }, …] FIFO queue
371
371
  this._streamThrottle = new Map(); // agentId -> { timer, pending }
372
372
  this._rotatingAgents = new Set(); // agentIds currently being rotated (rotator wrote handoff)
373
373
  this._stalledAgents = new Set(); // agentIds already flagged as stalled (avoids duplicate broadcasts)
@@ -3222,7 +3222,13 @@ After fixing all issues, run tests (npm test) and build (npm run build) to verif
3222
3222
  queueMessage(agentId, message) {
3223
3223
  const agent = this.daemon.registry.get(agentId);
3224
3224
  const wrapped = agent ? wrapWithRoleReminder(agent.role, message) : message;
3225
- this.pendingMessages.set(agentId, { message: wrapped, timestamp: Date.now() });
3225
+ // Append to a FIFO queue rather than overwriting the slot. Two messages
3226
+ // arriving while an agent is busy — a user chat and an InnerChat message,
3227
+ // or two different agents reaching out — must BOTH survive. The old
3228
+ // single-slot version silently dropped whichever arrived first.
3229
+ const queue = this.pendingMessages.get(agentId) || [];
3230
+ queue.push({ message: wrapped, timestamp: Date.now() });
3231
+ this.pendingMessages.set(agentId, queue);
3226
3232
  if (this.daemon.rotator) {
3227
3233
  this.daemon.rotator.recordUserMessage(agentId);
3228
3234
  }
@@ -3230,9 +3236,13 @@ After fixing all issues, run tests (npm test) and build (npm run build) to verif
3230
3236
  }
3231
3237
 
3232
3238
  consumePendingMessage(agentId) {
3233
- const pending = this.pendingMessages.get(agentId);
3234
- if (pending) this.pendingMessages.delete(agentId);
3235
- return pending || null;
3239
+ const queue = this.pendingMessages.get(agentId);
3240
+ if (!queue || queue.length === 0) return null;
3241
+ this.pendingMessages.delete(agentId);
3242
+ // Deliver everything queued in one turn, in arrival order, clearly
3243
+ // separated so the agent can tell the messages apart.
3244
+ const message = queue.map((q) => q.message).join('\n\n---\n\n');
3245
+ return { message, timestamp: queue[0].timestamp, count: queue.length };
3236
3246
  }
3237
3247
 
3238
3248
  async killAll() {
@@ -0,0 +1,60 @@
1
+ // GROOVE — Pending message queue tests
2
+ // FSL-1.1-Apache-2.0 — see LICENSE
3
+
4
+ import { describe, it, beforeEach, afterEach } from 'node:test';
5
+ import assert from 'node:assert/strict';
6
+ import { ProcessManager } from '../src/process.js';
7
+
8
+ function makeDaemon() {
9
+ return {
10
+ registry: { get: (id) => ({ id, role: 'fullstack', name: id }) },
11
+ rotator: { recordUserMessage() {} },
12
+ broadcast() {},
13
+ };
14
+ }
15
+
16
+ describe('pending message queue', () => {
17
+ let pm;
18
+
19
+ beforeEach(() => { pm = new ProcessManager(makeDaemon()); });
20
+ afterEach(() => { if (pm._stallWatchdog) clearInterval(pm._stallWatchdog); });
21
+
22
+ it('keeps BOTH messages when two arrive while an agent is busy', () => {
23
+ pm.queueMessage('a1', 'message from the user');
24
+ pm.queueMessage('a1', 'question from another agent');
25
+
26
+ const drained = pm.consumePendingMessage('a1');
27
+ assert.equal(drained.count, 2);
28
+ assert.match(drained.message, /message from the user/);
29
+ assert.match(drained.message, /question from another agent/);
30
+ // arrival order preserved
31
+ assert.ok(drained.message.indexOf('user') < drained.message.indexOf('another agent'));
32
+ });
33
+
34
+ it('drains the queue so nothing is delivered twice', () => {
35
+ pm.queueMessage('a1', 'one');
36
+ pm.consumePendingMessage('a1');
37
+ assert.equal(pm.consumePendingMessage('a1'), null);
38
+ });
39
+
40
+ it('returns null when nothing is queued', () => {
41
+ assert.equal(pm.consumePendingMessage('nobody'), null);
42
+ });
43
+
44
+ it('queues are independent per agent', () => {
45
+ pm.queueMessage('a1', 'for a1');
46
+ pm.queueMessage('a2', 'for a2');
47
+ assert.match(pm.consumePendingMessage('a1').message, /for a1/);
48
+ assert.match(pm.consumePendingMessage('a2').message, /for a2/);
49
+ });
50
+
51
+ it('preserves order across three queued messages', () => {
52
+ pm.queueMessage('a1', 'first');
53
+ pm.queueMessage('a1', 'second');
54
+ pm.queueMessage('a1', 'third');
55
+ const { message, count } = pm.consumePendingMessage('a1');
56
+ assert.equal(count, 3);
57
+ assert.ok(message.indexOf('first') < message.indexOf('second'));
58
+ assert.ok(message.indexOf('second') < message.indexOf('third'));
59
+ });
60
+ });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groove-dev/gui",
3
- "version": "0.27.193",
3
+ "version": "0.27.194",
4
4
  "description": "GROOVE GUI — visual agent control plane",
5
5
  "license": "FSL-1.1-Apache-2.0",
6
6
  "type": "module",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "groove-dev",
3
- "version": "0.27.193",
3
+ "version": "0.27.194",
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.193",
3
+ "version": "0.27.194",
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.193",
3
+ "version": "0.27.194",
4
4
  "description": "GROOVE daemon — agent orchestration engine",
5
5
  "license": "FSL-1.1-Apache-2.0",
6
6
  "type": "module",
@@ -367,7 +367,7 @@ export class ProcessManager {
367
367
  this.daemon = daemon;
368
368
  this.handles = new Map(); // agentId -> { proc, logStream }
369
369
  this.peakContextUsage = new Map(); // agentId -> highest contextUsage seen
370
- this.pendingMessages = new Map(); // agentId -> { message, timestamp }
370
+ this.pendingMessages = new Map(); // agentId -> [{ message, timestamp }, …] FIFO queue
371
371
  this._streamThrottle = new Map(); // agentId -> { timer, pending }
372
372
  this._rotatingAgents = new Set(); // agentIds currently being rotated (rotator wrote handoff)
373
373
  this._stalledAgents = new Set(); // agentIds already flagged as stalled (avoids duplicate broadcasts)
@@ -3222,7 +3222,13 @@ After fixing all issues, run tests (npm test) and build (npm run build) to verif
3222
3222
  queueMessage(agentId, message) {
3223
3223
  const agent = this.daemon.registry.get(agentId);
3224
3224
  const wrapped = agent ? wrapWithRoleReminder(agent.role, message) : message;
3225
- this.pendingMessages.set(agentId, { message: wrapped, timestamp: Date.now() });
3225
+ // Append to a FIFO queue rather than overwriting the slot. Two messages
3226
+ // arriving while an agent is busy — a user chat and an InnerChat message,
3227
+ // or two different agents reaching out — must BOTH survive. The old
3228
+ // single-slot version silently dropped whichever arrived first.
3229
+ const queue = this.pendingMessages.get(agentId) || [];
3230
+ queue.push({ message: wrapped, timestamp: Date.now() });
3231
+ this.pendingMessages.set(agentId, queue);
3226
3232
  if (this.daemon.rotator) {
3227
3233
  this.daemon.rotator.recordUserMessage(agentId);
3228
3234
  }
@@ -3230,9 +3236,13 @@ After fixing all issues, run tests (npm test) and build (npm run build) to verif
3230
3236
  }
3231
3237
 
3232
3238
  consumePendingMessage(agentId) {
3233
- const pending = this.pendingMessages.get(agentId);
3234
- if (pending) this.pendingMessages.delete(agentId);
3235
- return pending || null;
3239
+ const queue = this.pendingMessages.get(agentId);
3240
+ if (!queue || queue.length === 0) return null;
3241
+ this.pendingMessages.delete(agentId);
3242
+ // Deliver everything queued in one turn, in arrival order, clearly
3243
+ // separated so the agent can tell the messages apart.
3244
+ const message = queue.map((q) => q.message).join('\n\n---\n\n');
3245
+ return { message, timestamp: queue[0].timestamp, count: queue.length };
3236
3246
  }
3237
3247
 
3238
3248
  async killAll() {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groove-dev/gui",
3
- "version": "0.27.193",
3
+ "version": "0.27.194",
4
4
  "description": "GROOVE GUI — visual agent control plane",
5
5
  "license": "FSL-1.1-Apache-2.0",
6
6
  "type": "module",