agentgui 1.0.394 → 1.0.395

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 (2) hide show
  1. package/lib/ws-optimizer.js +30 -110
  2. package/package.json +1 -1
@@ -1,6 +1,3 @@
1
- // WebSocket Optimization Module
2
- // Implements batching, rate limiting, compression, deduplication, priority queuing, and monitoring
3
-
4
1
  import zlib from 'zlib';
5
2
 
6
3
  const MESSAGE_PRIORITY = {
@@ -13,7 +10,20 @@ function getPriority(eventType) {
13
10
  if (MESSAGE_PRIORITY.high.includes(eventType)) return 3;
14
11
  if (MESSAGE_PRIORITY.normal.includes(eventType)) return 2;
15
12
  if (MESSAGE_PRIORITY.low.includes(eventType)) return 1;
16
- return 2; // default to normal
13
+ return 2;
14
+ }
15
+
16
+ function getBatchInterval(ws) {
17
+ const BATCH_BY_TIER = { excellent: 16, good: 32, fair: 50, poor: 100, bad: 200 };
18
+ const TIER_ORDER = ['excellent', 'good', 'fair', 'poor', 'bad'];
19
+ const tier = ws.latencyTier || 'good';
20
+ const trend = ws.latencyTrend;
21
+ if (trend === 'rising' || trend === 'falling') {
22
+ const idx = TIER_ORDER.indexOf(tier);
23
+ if (trend === 'rising' && idx < TIER_ORDER.length - 1) return BATCH_BY_TIER[TIER_ORDER[idx + 1]] || 32;
24
+ if (trend === 'falling' && idx > 0) return BATCH_BY_TIER[TIER_ORDER[idx - 1]] || 32;
25
+ }
26
+ return BATCH_BY_TIER[tier] || 32;
17
27
  }
18
28
 
19
29
  class ClientQueue {
@@ -31,151 +41,76 @@ class ClientQueue {
31
41
  }
32
42
 
33
43
  add(data, priority) {
34
- // Deduplication: skip if identical to last message
35
44
  if (this.lastMessage === data) return;
36
45
  this.lastMessage = data;
37
-
38
- if (priority === 3) {
39
- this.highPriority.push(data);
40
- } else if (priority === 2) {
41
- this.normalPriority.push(data);
42
- } else {
43
- this.lowPriority.push(data);
44
- }
45
-
46
- // High priority: flush immediately
47
- if (priority === 3) {
48
- this.flushImmediate();
49
- } else if (!this.timer) {
50
- this.scheduleFlush();
51
- }
46
+ if (priority === 3) this.highPriority.push(data);
47
+ else if (priority === 2) this.normalPriority.push(data);
48
+ else this.lowPriority.push(data);
49
+ if (priority === 3) this.flushImmediate();
50
+ else if (!this.timer) this.scheduleFlush();
52
51
  }
53
52
 
54
53
  scheduleFlush() {
55
54
  const interval = this.ws.latencyTier ? getBatchInterval(this.ws) : 100;
56
- this.timer = setTimeout(() => {
57
- this.timer = null;
58
- this.flush();
59
- }, interval);
55
+ this.timer = setTimeout(() => { this.timer = null; this.flush(); }, interval);
60
56
  }
61
57
 
62
58
  flushImmediate() {
63
- if (this.timer) {
64
- clearTimeout(this.timer);
65
- this.timer = null;
66
- }
59
+ if (this.timer) { clearTimeout(this.timer); this.timer = null; }
67
60
  this.flush();
68
61
  }
69
62
 
70
63
  flush() {
71
64
  if (this.ws.readyState !== 1) return;
72
-
73
65
  const now = Date.now();
74
66
  const windowDuration = now - this.windowStart;
75
-
76
- // Reset rate limit window every second
77
67
  if (windowDuration >= 1000) {
78
68
  this.messageCount = 0;
79
69
  this.bytesSent = 0;
80
70
  this.windowStart = now;
81
71
  this.rateLimitWarned = false;
82
72
  }
83
-
84
- // Collect messages from all priorities (high first)
85
- const batch = [
86
- ...this.highPriority.splice(0),
87
- ...this.normalPriority.splice(0, 10),
88
- ...this.lowPriority.splice(0, 5)
89
- ];
90
-
73
+ const batch = [...this.highPriority.splice(0), ...this.normalPriority.splice(0, 10), ...this.lowPriority.splice(0, 5)];
91
74
  if (batch.length === 0) return;
92
-
93
- // Rate limiting: max 100 msg/sec per client
94
75
  const messagesThisSecond = this.messageCount + batch.length;
95
76
  if (messagesThisSecond > 100) {
96
77
  if (!this.rateLimitWarned) {
97
78
  console.warn(`[ws-optimizer] Client ${this.ws.clientId} rate limited: ${messagesThisSecond} msg/sec`);
98
79
  this.rateLimitWarned = true;
99
80
  }
100
- // Keep high priority, drop some normal/low
101
81
  const allowedCount = 100 - this.messageCount;
102
- if (allowedCount <= 0) {
103
- // Reschedule remaining
104
- this.scheduleFlush();
105
- return;
106
- }
82
+ if (allowedCount <= 0) { this.scheduleFlush(); return; }
107
83
  batch.splice(allowedCount);
108
84
  }
109
-
110
- let payload;
111
- if (batch.length === 1) {
112
- payload = batch[0];
113
- } else {
114
- payload = '[' + batch.join(',') + ']';
115
- }
116
-
117
- // Compression for large payloads (>1KB)
85
+ let payload = batch.length === 1 ? batch[0] : '[' + batch.join(',') + ']';
118
86
  if (payload.length > 1024) {
119
87
  try {
120
88
  const compressed = zlib.gzipSync(Buffer.from(payload), { level: 6 });
121
89
  if (compressed.length < payload.length * 0.9) {
122
- // Send compression hint as separate control message
123
90
  this.ws.send(JSON.stringify({ type: '_compressed', encoding: 'gzip' }));
124
91
  this.ws.send(compressed);
125
- payload = null; // Already sent
92
+ payload = null;
126
93
  }
127
- } catch (e) {
128
- // Fall back to uncompressed
129
- }
94
+ } catch (e) {}
130
95
  }
131
-
132
- if (payload) {
133
- this.ws.send(payload);
134
- }
135
-
96
+ if (payload) this.ws.send(payload);
136
97
  this.messageCount += batch.length;
137
98
  this.bytesSent += (payload ? payload.length : 0);
138
-
139
- // Monitor: warn if >1MB/sec sustained for 3+ seconds
140
99
  if (windowDuration >= 3000 && this.bytesSent > 3 * 1024 * 1024) {
141
100
  const mbps = (this.bytesSent / windowDuration * 1000 / 1024 / 1024).toFixed(2);
142
101
  console.warn(`[ws-optimizer] Client ${this.ws.clientId} high bandwidth: ${mbps} MB/sec`);
143
102
  }
144
-
145
- // If there are remaining low-priority messages, schedule next flush
146
103
  if (this.normalPriority.length > 0 || this.lowPriority.length > 0) {
147
104
  if (!this.timer) this.scheduleFlush();
148
105
  }
149
106
  }
150
107
 
151
108
  drain() {
152
- if (this.timer) {
153
- clearTimeout(this.timer);
154
- this.timer = null;
155
- }
109
+ if (this.timer) { clearTimeout(this.timer); this.timer = null; }
156
110
  this.flush();
157
111
  }
158
112
  }
159
113
 
160
- function getBatchInterval(ws) {
161
- const BATCH_BY_TIER = { excellent: 16, good: 32, fair: 50, poor: 100, bad: 200 };
162
- const TIER_ORDER = ['excellent', 'good', 'fair', 'poor', 'bad'];
163
- const tier = ws.latencyTier || 'good';
164
- const trend = ws.latencyTrend;
165
-
166
- if (trend === 'rising' || trend === 'falling') {
167
- const idx = TIER_ORDER.indexOf(tier);
168
- if (trend === 'rising' && idx < TIER_ORDER.length - 1) {
169
- return BATCH_BY_TIER[TIER_ORDER[idx + 1]] || 32;
170
- }
171
- if (trend === 'falling' && idx > 0) {
172
- return BATCH_BY_TIER[TIER_ORDER[idx - 1]] || 32;
173
- }
174
- }
175
-
176
- return BATCH_BY_TIER[tier] || 32;
177
- }
178
-
179
114
  class WSOptimizer {
180
115
  constructor() {
181
116
  this.clientQueues = new Map();
@@ -183,16 +118,13 @@ class WSOptimizer {
183
118
 
184
119
  sendToClient(ws, event) {
185
120
  if (ws.readyState !== 1) return;
186
-
187
121
  let queue = this.clientQueues.get(ws);
188
122
  if (!queue) {
189
123
  queue = new ClientQueue(ws);
190
124
  this.clientQueues.set(ws, queue);
191
125
  }
192
-
193
126
  const data = typeof event === 'string' ? event : JSON.stringify(event);
194
127
  const priority = typeof event === 'object' ? getPriority(event.type) : 2;
195
-
196
128
  queue.add(data, priority);
197
129
  }
198
130
 
@@ -205,30 +137,18 @@ class WSOptimizer {
205
137
  }
206
138
 
207
139
  getStats() {
208
- const stats = {
209
- clients: this.clientQueues.size,
210
- totalBytes: 0,
211
- totalMessages: 0,
212
- highBandwidthClients: []
213
- };
214
-
140
+ const stats = { clients: this.clientQueues.size, totalBytes: 0, totalMessages: 0, highBandwidthClients: [] };
215
141
  for (const [ws, queue] of this.clientQueues.entries()) {
216
142
  stats.totalBytes += queue.bytesSent;
217
143
  stats.totalMessages += queue.messageCount;
218
-
219
144
  const windowDuration = Date.now() - queue.windowStart;
220
145
  if (windowDuration > 0) {
221
146
  const mbps = (queue.bytesSent / windowDuration * 1000 / 1024 / 1024);
222
147
  if (mbps > 1) {
223
- stats.highBandwidthClients.push({
224
- clientId: ws.clientId,
225
- mbps: mbps.toFixed(2),
226
- messages: queue.messageCount
227
- });
148
+ stats.highBandwidthClients.push({ clientId: ws.clientId, mbps: mbps.toFixed(2), messages: queue.messageCount });
228
149
  }
229
150
  }
230
151
  }
231
-
232
152
  return stats;
233
153
  }
234
154
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.394",
3
+ "version": "1.0.395",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",