@tjamescouch/agentchat 0.2.0 → 0.3.0

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.
package/README.md CHANGED
@@ -40,6 +40,9 @@ agentchat serve --port 8080 --host 127.0.0.1
40
40
 
41
41
  # With message logging (for debugging)
42
42
  agentchat serve --log-messages
43
+
44
+ # Custom message buffer size (replayed to new joiners, default: 20)
45
+ agentchat serve --buffer-size 50
43
46
  ```
44
47
 
45
48
  ### Client
@@ -151,6 +154,12 @@ Messages received via `listen` are JSON lines:
151
154
  {"type":"AGENT_LEFT","channel":"#general","agent":"@abc123","ts":1706889602000}
152
155
  ```
153
156
 
157
+ **Message history replay:** When you join a channel, you receive the last N messages (default 20) with `"replay": true` so you can distinguish history from live messages:
158
+
159
+ ```json
160
+ {"type":"MSG","from":"@abc123","to":"#general","content":"Earlier message","ts":1706889500000,"replay":true}
161
+ ```
162
+
154
163
  ## Protocol
155
164
 
156
165
  AgentChat uses WebSocket with JSON messages.
package/bin/agentchat.js CHANGED
@@ -43,6 +43,7 @@ program
43
43
  .option('--log-messages', 'Log all messages (for debugging)')
44
44
  .option('--cert <file>', 'TLS certificate file (PEM format)')
45
45
  .option('--key <file>', 'TLS private key file (PEM format)')
46
+ .option('--buffer-size <n>', 'Message buffer size per channel for replay on join', '20')
46
47
  .action((options) => {
47
48
  // Validate TLS options (both or neither)
48
49
  if ((options.cert && !options.key) || (!options.cert && options.key)) {
@@ -56,7 +57,8 @@ program
56
57
  name: options.name,
57
58
  logMessages: options.logMessages,
58
59
  cert: options.cert,
59
- key: options.key
60
+ key: options.key,
61
+ messageBufferSize: parseInt(options.bufferSize)
60
62
  });
61
63
  });
62
64
 
package/lib/server.js CHANGED
@@ -38,6 +38,9 @@ export class AgentChatServer {
38
38
  // Rate limiting: 1 message per second per agent
39
39
  this.rateLimitMs = options.rateLimitMs || 1000;
40
40
 
41
+ // Message buffer size per channel (for replay on join)
42
+ this.messageBufferSize = options.messageBufferSize || 20;
43
+
41
44
  // State
42
45
  this.agents = new Map(); // ws -> agent info
43
46
  this.agentById = new Map(); // id -> ws
@@ -62,11 +65,40 @@ export class AgentChatServer {
62
65
  name,
63
66
  inviteOnly,
64
67
  invited: new Set(),
65
- agents: new Set()
68
+ agents: new Set(),
69
+ messageBuffer: [] // Rolling buffer of recent messages
66
70
  });
67
71
  }
68
72
  return this.channels.get(name);
69
73
  }
74
+
75
+ /**
76
+ * Add a message to a channel's buffer (circular buffer)
77
+ */
78
+ _bufferMessage(channel, msg) {
79
+ const ch = this.channels.get(channel);
80
+ if (!ch) return;
81
+
82
+ ch.messageBuffer.push(msg);
83
+
84
+ // Trim to buffer size
85
+ if (ch.messageBuffer.length > this.messageBufferSize) {
86
+ ch.messageBuffer.shift();
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Replay buffered messages to a newly joined agent
92
+ */
93
+ _replayMessages(ws, channel) {
94
+ const ch = this.channels.get(channel);
95
+ if (!ch || ch.messageBuffer.length === 0) return;
96
+
97
+ for (const msg of ch.messageBuffer) {
98
+ // Send with replay flag so client knows it's history
99
+ this._send(ws, { ...msg, replay: true });
100
+ }
101
+ }
70
102
 
71
103
  _log(event, data = {}) {
72
104
  const entry = {
@@ -310,8 +342,11 @@ export class AgentChatServer {
310
342
  channel: msg.channel,
311
343
  agents: agentList
312
344
  }));
345
+
346
+ // Replay recent messages to the joining agent
347
+ this._replayMessages(ws, msg.channel);
313
348
  }
314
-
349
+
315
350
  _handleLeave(ws, msg) {
316
351
  const agent = this.agents.get(ws);
317
352
  if (!agent) {
@@ -376,7 +411,10 @@ export class AgentChatServer {
376
411
 
377
412
  // Broadcast to channel including sender
378
413
  this._broadcast(msg.to, outMsg);
379
-
414
+
415
+ // Buffer the message for replay to future joiners
416
+ this._bufferMessage(msg.to, outMsg);
417
+
380
418
  } else if (isAgent(msg.to)) {
381
419
  // Direct message
382
420
  const targetId = msg.to.slice(1); // remove @
@@ -744,7 +782,8 @@ export function startServer(options = {}) {
744
782
  logMessages: options.logMessages || process.env.LOG_MESSAGES === 'true',
745
783
  cert: options.cert || process.env.TLS_CERT || null,
746
784
  key: options.key || process.env.TLS_KEY || null,
747
- rateLimitMs: options.rateLimitMs || parseInt(process.env.RATE_LIMIT_MS || 1000)
785
+ rateLimitMs: options.rateLimitMs || parseInt(process.env.RATE_LIMIT_MS || 1000),
786
+ messageBufferSize: options.messageBufferSize || parseInt(process.env.MESSAGE_BUFFER_SIZE || 20)
748
787
  };
749
788
 
750
789
  const server = new AgentChatServer(config);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tjamescouch/agentchat",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Real-time IRC-like communication protocol for AI agents",
5
5
  "main": "lib/client.js",
6
6
  "files": [