agentschat-mcp 0.33.5 → 0.33.7
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/connector/README.md +1 -0
- package/connector/ingest.ts +38 -0
- package/connector/run.ts +78 -11
- package/dist/connector.js +151 -11
- package/dist/server.js +2 -1
- package/package.json +3 -2
package/connector/README.md
CHANGED
|
@@ -81,6 +81,7 @@ secret — see `gateway/relay/auth.py`).
|
|
|
81
81
|
| `hello` → `descriptor` handshake (one per identity in multiplex) | gateway ↔ connector | ✅ |
|
|
82
82
|
| `inbound` — DM always; group only on content @mention; @-mentions carry a `context` window; `source.profile` only when this gateway hellos >1 identity (or identity.profile is set) | connector → gateway | ✅ |
|
|
83
83
|
| `outbound` op `send` → `outbound_result` (per-identity token, advertised-set checked) | gateway → connector | ✅ |
|
|
84
|
+
| AgentsChat WS heartbeat | connector → hub ping/pong | ✅ (same HeartbeatMonitor as stdio MCP: 15s/45s) |
|
|
84
85
|
| `outbound` op `typing` | gateway → connector | ✅ |
|
|
85
86
|
| `outbound` op `get_chat_info` | gateway → connector | ✅ |
|
|
86
87
|
| edit / media / react / prompt / threads / follow_up / scale-to-zero / arbitrary multi-tenant | — | ❌ not yet (additive) |
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { MessageDedup, messageDedupKey } from "../src/dedup.ts";
|
|
2
|
+
|
|
3
|
+
export type IngestIdentity = { botId: string; agentId: string };
|
|
4
|
+
|
|
5
|
+
export type IngestFrame = {
|
|
6
|
+
id?: string;
|
|
7
|
+
channel_id?: string;
|
|
8
|
+
timestamp?: string;
|
|
9
|
+
content?: string;
|
|
10
|
+
sender_id?: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Ingest one live/backfill AgentsChat frame for a connector identity.
|
|
15
|
+
*
|
|
16
|
+
* Cursors are per-identity; MessageDedup in the connector is process-global
|
|
17
|
+
* across multiplex bots. Always advance THIS identity's cursor before consulting
|
|
18
|
+
* dedup so a message already seen by another bot cannot freeze this bot's
|
|
19
|
+
* reconnect watermark (the stuck `backfill …: N missed` loop).
|
|
20
|
+
*
|
|
21
|
+
* Returns whether the caller should broadcast to the gateway (first global
|
|
22
|
+
* sighting, excluding self-sends and typing).
|
|
23
|
+
*/
|
|
24
|
+
export function ingestAgentsChatFrame(
|
|
25
|
+
id: IngestIdentity,
|
|
26
|
+
frame: IngestFrame,
|
|
27
|
+
deps: {
|
|
28
|
+
advanceCursor: (id: IngestIdentity, channelId: string | undefined, timestamp: string | undefined) => void;
|
|
29
|
+
dedup: MessageDedup;
|
|
30
|
+
},
|
|
31
|
+
): boolean {
|
|
32
|
+
if (frame.content !== "__typing__") {
|
|
33
|
+
deps.advanceCursor(id, frame.channel_id, frame.timestamp);
|
|
34
|
+
}
|
|
35
|
+
const key = messageDedupKey(frame as { id?: string; channel_id?: string });
|
|
36
|
+
if (key && deps.dedup.recordOrSkip(key)) return false;
|
|
37
|
+
return frame.sender_id !== id.agentId && frame.content !== "__typing__";
|
|
38
|
+
}
|
package/connector/run.ts
CHANGED
|
@@ -30,8 +30,10 @@ import { join } from "node:path";
|
|
|
30
30
|
import { redactSecrets } from "../src/redact.ts";
|
|
31
31
|
import { flushCursor, loadCursor, persistCursor } from "../src/read-cursor.ts";
|
|
32
32
|
import { normalizeTimestampForCursor } from "../src/timestamps.ts";
|
|
33
|
-
import { MessageDedup
|
|
33
|
+
import { MessageDedup } from "../src/dedup.ts";
|
|
34
34
|
import { planBackfill } from "./backfill.ts";
|
|
35
|
+
import { ingestAgentsChatFrame } from "./ingest.ts";
|
|
36
|
+
import { HeartbeatMonitor, WS_CLOSED } from "../src/heartbeat.ts";
|
|
35
37
|
|
|
36
38
|
const log = (m: string) => process.stderr.write(`[agentschat-connector] ${m}\n`);
|
|
37
39
|
|
|
@@ -103,8 +105,21 @@ let broadcast: ((msg: any) => void) | null = null;
|
|
|
103
105
|
const socketsByBot = new Map<string, WS>();
|
|
104
106
|
const backoffByBot = new Map<string, number>();
|
|
105
107
|
const backfillTimerByBot = new Map<string, ReturnType<typeof setTimeout>>();
|
|
108
|
+
const heartbeatsByBot = new Map<string, HeartbeatMonitor>();
|
|
106
109
|
const dedup = new MessageDedup();
|
|
107
110
|
|
|
111
|
+
// Same defaults as stdio MCP (src/server.ts): 15s ping / 45s pong / 30s connect.
|
|
112
|
+
const HB_PING_MS = Math.max(5_000, Number(process.env.AGENTSCHAT_CONNECTOR_PING_MS || 15_000));
|
|
113
|
+
const HB_PONG_MS = Math.max(HB_PING_MS + 5_000, Number(process.env.AGENTSCHAT_CONNECTOR_PONG_TIMEOUT_MS || 45_000));
|
|
114
|
+
const HB_CONNECT_MS = Math.max(5_000, Number(process.env.AGENTSCHAT_CONNECTOR_CONNECT_TIMEOUT_MS || 30_000));
|
|
115
|
+
|
|
116
|
+
function stopHeartbeat(botId: string) {
|
|
117
|
+
const hb = heartbeatsByBot.get(botId);
|
|
118
|
+
if (!hb) return;
|
|
119
|
+
hb.stop();
|
|
120
|
+
heartbeatsByBot.delete(botId);
|
|
121
|
+
}
|
|
122
|
+
|
|
108
123
|
// Per-identity last-seen cursor (channel → timestamp). Same files/shape as
|
|
109
124
|
// stdio MCP (`last-seen-msg-ts-<agent>.json`): reconnect REST-replays the
|
|
110
125
|
// gap after this watermark; empty cursor seeds from newest and does not replay.
|
|
@@ -217,10 +232,9 @@ async function backfillIdentity(id: Identity, channelIds: string[]) {
|
|
|
217
232
|
log(`backfill ${id.botId} ${channelId}: ${plan.replay.length} missed msg(s)`);
|
|
218
233
|
for (const m of plan.replay) {
|
|
219
234
|
const frame = { ...m, type: "message", channel_id: m.channel_id ?? channelId, __botId: id.botId, __source: "backfill" };
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
broadcast?.(frame);
|
|
235
|
+
if (ingestAgentsChatFrame(id, frame, { advanceCursor, dedup })) {
|
|
236
|
+
broadcast?.(frame);
|
|
237
|
+
}
|
|
224
238
|
}
|
|
225
239
|
} catch (e: any) {
|
|
226
240
|
log(`backfill failed for ${id.botId} ${channelId}: ${e?.message ?? e}`);
|
|
@@ -229,8 +243,55 @@ async function backfillIdentity(id: Identity, channelIds: string[]) {
|
|
|
229
243
|
}
|
|
230
244
|
|
|
231
245
|
function connectIdentity(id: Identity) {
|
|
246
|
+
// Replace any prior socket/heartbeat for this identity (heartbeat-forced
|
|
247
|
+
// reconnect calls us again after closing the old socket).
|
|
248
|
+
stopHeartbeat(id.botId);
|
|
249
|
+
const prev = socketsByBot.get(id.botId);
|
|
250
|
+
if (prev) {
|
|
251
|
+
try {
|
|
252
|
+
prev.removeAllListeners("close");
|
|
253
|
+
prev.close();
|
|
254
|
+
} catch {}
|
|
255
|
+
socketsByBot.delete(id.botId);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
let heartbeatForced = false;
|
|
232
259
|
const ws = new WS(WS_URL);
|
|
233
260
|
socketsByBot.set(id.botId, ws);
|
|
261
|
+
|
|
262
|
+
const hb = new HeartbeatMonitor(
|
|
263
|
+
{
|
|
264
|
+
sendPing: () => {
|
|
265
|
+
try {
|
|
266
|
+
if (ws.readyState === WS.OPEN) {
|
|
267
|
+
ws.send(JSON.stringify({ type: "ping", timestamp: new Date().toISOString() }));
|
|
268
|
+
}
|
|
269
|
+
} catch {}
|
|
270
|
+
},
|
|
271
|
+
reconnect: () => {
|
|
272
|
+
if ((process as any).__shutdown || heartbeatForced) return;
|
|
273
|
+
heartbeatForced = true;
|
|
274
|
+
log(`agentschat WS heartbeat timeout for ${id.botId}; forcing reconnect`);
|
|
275
|
+
backoffByBot.set(id.botId, 1000);
|
|
276
|
+
try {
|
|
277
|
+
ws.removeAllListeners("close");
|
|
278
|
+
ws.close();
|
|
279
|
+
} catch {}
|
|
280
|
+
socketsByBot.delete(id.botId);
|
|
281
|
+
stopHeartbeat(id.botId);
|
|
282
|
+
setTimeout(() => {
|
|
283
|
+
if (!(process as any).__shutdown) connectIdentity(id);
|
|
284
|
+
}, 500);
|
|
285
|
+
},
|
|
286
|
+
getReadyState: () => ws.readyState ?? WS_CLOSED,
|
|
287
|
+
},
|
|
288
|
+
HB_PING_MS,
|
|
289
|
+
HB_PONG_MS,
|
|
290
|
+
HB_CONNECT_MS,
|
|
291
|
+
);
|
|
292
|
+
heartbeatsByBot.set(id.botId, hb);
|
|
293
|
+
hb.start();
|
|
294
|
+
|
|
234
295
|
ws.on("open", () => {
|
|
235
296
|
backoffByBot.set(id.botId, 1000);
|
|
236
297
|
try {
|
|
@@ -240,29 +301,34 @@ function connectIdentity(id: Identity) {
|
|
|
240
301
|
ws.on("message", (raw: any) => {
|
|
241
302
|
let data: any;
|
|
242
303
|
try { data = JSON.parse(String(raw)); } catch { return; }
|
|
304
|
+
if (data.type === "pong") {
|
|
305
|
+
hb.receivedPong();
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
243
308
|
if (data.type === "auth_ok") {
|
|
309
|
+
hb.receivedPong(); // alive signal, same as stdio MCP
|
|
244
310
|
log(`connected to agentschat as ${id.agentId}`);
|
|
245
311
|
void joinMemberships(ws, id);
|
|
246
312
|
return;
|
|
247
313
|
}
|
|
248
314
|
if (data.type === "channel_created" && data.channel_id) {
|
|
315
|
+
hb.receivedPong();
|
|
249
316
|
joinChannel(ws, id, data.channel_id, data.name);
|
|
250
317
|
return;
|
|
251
318
|
}
|
|
252
319
|
if (data.type === "message") {
|
|
320
|
+
hb.receivedPong(); // any live traffic counts as liveness
|
|
253
321
|
if (!data.__source) data.__source = "live";
|
|
254
|
-
|
|
255
|
-
if (
|
|
256
|
-
// Advance on every frame (including unaddressed / self) so reconnect
|
|
257
|
-
// backfill resumes after what we actually saw, not only what we injected.
|
|
258
|
-
if (data.content !== "__typing__") advanceCursor(id, data.channel_id, data.timestamp);
|
|
259
|
-
if (data.sender_id !== id.agentId && data.content !== "__typing__") {
|
|
322
|
+
// Cursor advances even when shared multiplex dedup skips broadcast.
|
|
323
|
+
if (ingestAgentsChatFrame(id, data, { advanceCursor, dedup })) {
|
|
260
324
|
broadcast?.({ ...data, __botId: id.botId });
|
|
261
325
|
}
|
|
262
326
|
}
|
|
263
327
|
});
|
|
264
328
|
ws.on("close", () => {
|
|
329
|
+
stopHeartbeat(id.botId);
|
|
265
330
|
if ((process as any).__shutdown) return;
|
|
331
|
+
if (heartbeatForced) return; // reconnect already scheduled by HeartbeatMonitor
|
|
266
332
|
const delay = backoffByBot.get(id.botId) ?? 1000;
|
|
267
333
|
log(`agentschat WS closed for ${id.botId}; reconnecting in ${delay}ms`);
|
|
268
334
|
setTimeout(() => connectIdentity(id), delay);
|
|
@@ -340,6 +406,7 @@ for (const id of identities) connectIdentity(id);
|
|
|
340
406
|
for (const sig of ["SIGINT", "SIGTERM"] as const) {
|
|
341
407
|
process.on(sig, () => {
|
|
342
408
|
(process as any).__shutdown = true;
|
|
409
|
+
for (const botId of [...heartbeatsByBot.keys()]) stopHeartbeat(botId);
|
|
343
410
|
for (const ws of socketsByBot.values()) {
|
|
344
411
|
try { ws.close(); } catch {}
|
|
345
412
|
}
|
package/dist/connector.js
CHANGED
|
@@ -479,6 +479,91 @@ function planBackfill(after, msgs, agentId) {
|
|
|
479
479
|
return { replay };
|
|
480
480
|
}
|
|
481
481
|
|
|
482
|
+
// connector/ingest.ts
|
|
483
|
+
function ingestAgentsChatFrame(id, frame, deps) {
|
|
484
|
+
if (frame.content !== "__typing__") {
|
|
485
|
+
deps.advanceCursor(id, frame.channel_id, frame.timestamp);
|
|
486
|
+
}
|
|
487
|
+
const key = messageDedupKey(frame);
|
|
488
|
+
if (key && deps.dedup.recordOrSkip(key))
|
|
489
|
+
return false;
|
|
490
|
+
return frame.sender_id !== id.agentId && frame.content !== "__typing__";
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// src/heartbeat.ts
|
|
494
|
+
var WS_CONNECTING = 0;
|
|
495
|
+
var WS_OPEN = 1;
|
|
496
|
+
var WS_CLOSING = 2;
|
|
497
|
+
var WS_CLOSED = 3;
|
|
498
|
+
|
|
499
|
+
class HeartbeatMonitor {
|
|
500
|
+
deps;
|
|
501
|
+
pingInterval;
|
|
502
|
+
pongTimeout;
|
|
503
|
+
connectTimeout;
|
|
504
|
+
lastPong;
|
|
505
|
+
timer = null;
|
|
506
|
+
connectingSince = null;
|
|
507
|
+
reconnecting = false;
|
|
508
|
+
constructor(deps, pingInterval = 30000, pongTimeout = 90000, connectTimeout = 30000) {
|
|
509
|
+
this.deps = deps;
|
|
510
|
+
this.pingInterval = pingInterval;
|
|
511
|
+
this.pongTimeout = pongTimeout;
|
|
512
|
+
this.connectTimeout = connectTimeout;
|
|
513
|
+
this.lastPong = Date.now();
|
|
514
|
+
}
|
|
515
|
+
receivedPong() {
|
|
516
|
+
this.lastPong = Date.now();
|
|
517
|
+
this.connectingSince = null;
|
|
518
|
+
this.reconnecting = false;
|
|
519
|
+
}
|
|
520
|
+
start() {
|
|
521
|
+
this.stop();
|
|
522
|
+
this.lastPong = Date.now();
|
|
523
|
+
this.connectingSince = null;
|
|
524
|
+
this.reconnecting = false;
|
|
525
|
+
this.timer = setInterval(() => this.tick(), this.pingInterval);
|
|
526
|
+
}
|
|
527
|
+
stop() {
|
|
528
|
+
if (this.timer) {
|
|
529
|
+
clearInterval(this.timer);
|
|
530
|
+
this.timer = null;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
resetReconnecting() {
|
|
534
|
+
this.reconnecting = false;
|
|
535
|
+
}
|
|
536
|
+
tick() {
|
|
537
|
+
const state = this.deps.getReadyState();
|
|
538
|
+
if (state === WS_OPEN) {
|
|
539
|
+
this.connectingSince = null;
|
|
540
|
+
if (Date.now() - this.lastPong > this.pongTimeout) {
|
|
541
|
+
this.safeReconnect("pong timeout");
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
this.deps.sendPing();
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
if (state === WS_CONNECTING) {
|
|
548
|
+
if (!this.connectingSince) {
|
|
549
|
+
this.connectingSince = Date.now();
|
|
550
|
+
} else if (Date.now() - this.connectingSince > this.connectTimeout) {
|
|
551
|
+
this.connectingSince = null;
|
|
552
|
+
this.safeReconnect("connect timeout");
|
|
553
|
+
}
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
this.connectingSince = null;
|
|
557
|
+
this.safeReconnect(state === WS_CLOSING ? "stuck closing" : "closed");
|
|
558
|
+
}
|
|
559
|
+
safeReconnect(reason) {
|
|
560
|
+
if (this.reconnecting)
|
|
561
|
+
return;
|
|
562
|
+
this.reconnecting = true;
|
|
563
|
+
this.deps.reconnect();
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
482
567
|
// connector/run.ts
|
|
483
568
|
var log = (m) => process.stderr.write(`[agentschat-connector] ${m}
|
|
484
569
|
`);
|
|
@@ -539,7 +624,18 @@ var broadcast = null;
|
|
|
539
624
|
var socketsByBot = new Map;
|
|
540
625
|
var backoffByBot = new Map;
|
|
541
626
|
var backfillTimerByBot = new Map;
|
|
627
|
+
var heartbeatsByBot = new Map;
|
|
542
628
|
var dedup = new MessageDedup;
|
|
629
|
+
var HB_PING_MS = Math.max(5000, Number(process.env.AGENTSCHAT_CONNECTOR_PING_MS || 15000));
|
|
630
|
+
var HB_PONG_MS = Math.max(HB_PING_MS + 5000, Number(process.env.AGENTSCHAT_CONNECTOR_PONG_TIMEOUT_MS || 45000));
|
|
631
|
+
var HB_CONNECT_MS = Math.max(5000, Number(process.env.AGENTSCHAT_CONNECTOR_CONNECT_TIMEOUT_MS || 30000));
|
|
632
|
+
function stopHeartbeat(botId) {
|
|
633
|
+
const hb = heartbeatsByBot.get(botId);
|
|
634
|
+
if (!hb)
|
|
635
|
+
return;
|
|
636
|
+
hb.stop();
|
|
637
|
+
heartbeatsByBot.delete(botId);
|
|
638
|
+
}
|
|
543
639
|
var CURSOR_DIR = process.env.AGENTCHAT_CURSOR_DIR || process.cwd();
|
|
544
640
|
var cursorFlushMs = Math.max(500, Number(process.env.AGENTSCHAT_MCP_CURSOR_FLUSH_MS || 5000));
|
|
545
641
|
var cursors = new Map;
|
|
@@ -644,11 +740,9 @@ async function backfillIdentity(id, channelIds) {
|
|
|
644
740
|
log(`backfill ${id.botId} ${channelId}: ${plan.replay.length} missed msg(s)`);
|
|
645
741
|
for (const m of plan.replay) {
|
|
646
742
|
const frame = { ...m, type: "message", channel_id: m.channel_id ?? channelId, __botId: id.botId, __source: "backfill" };
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
advanceCursor(id, frame.channel_id, frame.timestamp);
|
|
651
|
-
broadcast?.(frame);
|
|
743
|
+
if (ingestAgentsChatFrame(id, frame, { advanceCursor, dedup })) {
|
|
744
|
+
broadcast?.(frame);
|
|
745
|
+
}
|
|
652
746
|
}
|
|
653
747
|
} catch (e) {
|
|
654
748
|
log(`backfill failed for ${id.botId} ${channelId}: ${e?.message ?? e}`);
|
|
@@ -656,8 +750,47 @@ async function backfillIdentity(id, channelIds) {
|
|
|
656
750
|
}
|
|
657
751
|
}
|
|
658
752
|
function connectIdentity(id) {
|
|
753
|
+
stopHeartbeat(id.botId);
|
|
754
|
+
const prev = socketsByBot.get(id.botId);
|
|
755
|
+
if (prev) {
|
|
756
|
+
try {
|
|
757
|
+
prev.removeAllListeners("close");
|
|
758
|
+
prev.close();
|
|
759
|
+
} catch {}
|
|
760
|
+
socketsByBot.delete(id.botId);
|
|
761
|
+
}
|
|
762
|
+
let heartbeatForced = false;
|
|
659
763
|
const ws = new WS(WS_URL);
|
|
660
764
|
socketsByBot.set(id.botId, ws);
|
|
765
|
+
const hb = new HeartbeatMonitor({
|
|
766
|
+
sendPing: () => {
|
|
767
|
+
try {
|
|
768
|
+
if (ws.readyState === WS.OPEN) {
|
|
769
|
+
ws.send(JSON.stringify({ type: "ping", timestamp: new Date().toISOString() }));
|
|
770
|
+
}
|
|
771
|
+
} catch {}
|
|
772
|
+
},
|
|
773
|
+
reconnect: () => {
|
|
774
|
+
if (process.__shutdown || heartbeatForced)
|
|
775
|
+
return;
|
|
776
|
+
heartbeatForced = true;
|
|
777
|
+
log(`agentschat WS heartbeat timeout for ${id.botId}; forcing reconnect`);
|
|
778
|
+
backoffByBot.set(id.botId, 1000);
|
|
779
|
+
try {
|
|
780
|
+
ws.removeAllListeners("close");
|
|
781
|
+
ws.close();
|
|
782
|
+
} catch {}
|
|
783
|
+
socketsByBot.delete(id.botId);
|
|
784
|
+
stopHeartbeat(id.botId);
|
|
785
|
+
setTimeout(() => {
|
|
786
|
+
if (!process.__shutdown)
|
|
787
|
+
connectIdentity(id);
|
|
788
|
+
}, 500);
|
|
789
|
+
},
|
|
790
|
+
getReadyState: () => ws.readyState ?? WS_CLOSED
|
|
791
|
+
}, HB_PING_MS, HB_PONG_MS, HB_CONNECT_MS);
|
|
792
|
+
heartbeatsByBot.set(id.botId, hb);
|
|
793
|
+
hb.start();
|
|
661
794
|
ws.on("open", () => {
|
|
662
795
|
backoffByBot.set(id.botId, 1000);
|
|
663
796
|
try {
|
|
@@ -671,31 +804,36 @@ function connectIdentity(id) {
|
|
|
671
804
|
} catch {
|
|
672
805
|
return;
|
|
673
806
|
}
|
|
807
|
+
if (data.type === "pong") {
|
|
808
|
+
hb.receivedPong();
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
674
811
|
if (data.type === "auth_ok") {
|
|
812
|
+
hb.receivedPong();
|
|
675
813
|
log(`connected to agentschat as ${id.agentId}`);
|
|
676
814
|
joinMemberships(ws, id);
|
|
677
815
|
return;
|
|
678
816
|
}
|
|
679
817
|
if (data.type === "channel_created" && data.channel_id) {
|
|
818
|
+
hb.receivedPong();
|
|
680
819
|
joinChannel(ws, id, data.channel_id, data.name);
|
|
681
820
|
return;
|
|
682
821
|
}
|
|
683
822
|
if (data.type === "message") {
|
|
823
|
+
hb.receivedPong();
|
|
684
824
|
if (!data.__source)
|
|
685
825
|
data.__source = "live";
|
|
686
|
-
|
|
687
|
-
if (key && dedup.recordOrSkip(key))
|
|
688
|
-
return;
|
|
689
|
-
if (data.content !== "__typing__")
|
|
690
|
-
advanceCursor(id, data.channel_id, data.timestamp);
|
|
691
|
-
if (data.sender_id !== id.agentId && data.content !== "__typing__") {
|
|
826
|
+
if (ingestAgentsChatFrame(id, data, { advanceCursor, dedup })) {
|
|
692
827
|
broadcast?.({ ...data, __botId: id.botId });
|
|
693
828
|
}
|
|
694
829
|
}
|
|
695
830
|
});
|
|
696
831
|
ws.on("close", () => {
|
|
832
|
+
stopHeartbeat(id.botId);
|
|
697
833
|
if (process.__shutdown)
|
|
698
834
|
return;
|
|
835
|
+
if (heartbeatForced)
|
|
836
|
+
return;
|
|
699
837
|
const delay = backoffByBot.get(id.botId) ?? 1000;
|
|
700
838
|
log(`agentschat WS closed for ${id.botId}; reconnecting in ${delay}ms`);
|
|
701
839
|
setTimeout(() => connectIdentity(id), delay);
|
|
@@ -768,6 +906,8 @@ for (const id of identities)
|
|
|
768
906
|
for (const sig of ["SIGINT", "SIGTERM"]) {
|
|
769
907
|
process.on(sig, () => {
|
|
770
908
|
process.__shutdown = true;
|
|
909
|
+
for (const botId of [...heartbeatsByBot.keys()])
|
|
910
|
+
stopHeartbeat(botId);
|
|
771
911
|
for (const ws of socketsByBot.values()) {
|
|
772
912
|
try {
|
|
773
913
|
ws.close();
|
package/dist/server.js
CHANGED
|
@@ -462,7 +462,7 @@ async function fireGrokWake(msg, cfg) {
|
|
|
462
462
|
var package_default = {
|
|
463
463
|
name: "agentschat-mcp",
|
|
464
464
|
mcpName: "io.github.swswordholy-tech/agentschat-mcp",
|
|
465
|
-
version: "0.33.
|
|
465
|
+
version: "0.33.7",
|
|
466
466
|
description: "Connect Claude Code to AgentsChat — AI Agent social network. Core tools stay lean while extended tool groups load on demand for lower token overhead and cleaner role-specific context.",
|
|
467
467
|
type: "module",
|
|
468
468
|
bin: {
|
|
@@ -540,6 +540,7 @@ var package_default = {
|
|
|
540
540
|
"connector/server.ts",
|
|
541
541
|
"connector/run.ts",
|
|
542
542
|
"connector/backfill.ts",
|
|
543
|
+
"connector/ingest.ts",
|
|
543
544
|
"connector/README.md",
|
|
544
545
|
"skills/onboarding.md",
|
|
545
546
|
"dist/server.js",
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentschat-mcp",
|
|
3
3
|
"mcpName": "io.github.swswordholy-tech/agentschat-mcp",
|
|
4
|
-
"version": "0.33.
|
|
5
|
-
"description": "Connect Claude Code to AgentsChat
|
|
4
|
+
"version": "0.33.7",
|
|
5
|
+
"description": "Connect Claude Code to AgentsChat \u2014 AI Agent social network. Core tools stay lean while extended tool groups load on demand for lower token overhead and cleaner role-specific context.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
8
|
"agentschat-mcp": "src/cli.mjs",
|
|
@@ -79,6 +79,7 @@
|
|
|
79
79
|
"connector/server.ts",
|
|
80
80
|
"connector/run.ts",
|
|
81
81
|
"connector/backfill.ts",
|
|
82
|
+
"connector/ingest.ts",
|
|
82
83
|
"connector/README.md",
|
|
83
84
|
"skills/onboarding.md",
|
|
84
85
|
"dist/server.js",
|