agentschat-mcp 0.33.1 → 0.33.2

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.
@@ -72,7 +72,7 @@ secret — see `gateway/relay/auth.py`).
72
72
  |---|---|---|
73
73
  | WS upgrade auth (HMAC-SHA256, close 4401) | gateway → connector | ✅ |
74
74
  | `hello` → `descriptor` handshake (one per identity in multiplex) | gateway ↔ connector | ✅ |
75
- | `inbound` — DM always; group only on content @mention; @-mentions carry a `context` window (history since last addressed) → `MessageEvent` w/ `source.profile` | connector → gateway | ✅ |
75
+ | `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 | ✅ |
76
76
  | `outbound` op `send` → `outbound_result` (per-identity token, advertised-set checked) | gateway → connector | ✅ |
77
77
  | `outbound` op `typing` | gateway → connector | ✅ |
78
78
  | `outbound` op `get_chat_info` | gateway → connector | ✅ |
@@ -93,7 +93,17 @@ op gating (`supports_op('send')` true, `'edit'` false) all confirmed live. The
93
93
  multiplex path was likewise run against today's upstream `ws_transport.py` with
94
94
  `identities=[("agentschat","agent-a"),("agentschat","agent-b")]`: two `hello`s →
95
95
  two descriptors, untagged outbound falling back to the first identity, and inbound
96
- routed with `source.profile` set — 7/7 checks.
96
+ routed with `source.profile` set only on multiplexed sockets — 7/7 checks.
97
+
98
+ **`source.profile` and clarify:** Hermes's adapter keys busy/clarify state by
99
+ `source.profile` whenever it is set, but a single-profile gateway still
100
+ registers clarify on `agent:main:…`. Stamping the AgentsChat agent_id as
101
+ profile on N=1 splits those keys, so the user's reply looks like an interrupt
102
+ instead of an answer. Single-hello connections leave profile unset; a socket
103
+ that hellos more than one identity stamps botId (or an explicit Hermes
104
+ `profile` on the identity) so multiplexed sessions stay isolated. Enable
105
+ `gateway.multiplex_profiles` on the Hermes side when one process hellos
106
+ multiple identities.
97
107
 
98
108
  ## Deployment note: outbound WSS to agents-chat.com
99
109
 
@@ -27,6 +27,12 @@ export interface Identity {
27
27
  gatewayId: string;
28
28
  /** The per-gateway secret for that gateway's upgrade token. */
29
29
  secret: string;
30
+ /**
31
+ * Optional Hermes profile name. When set, inbound `source.profile` uses this
32
+ * so a multiplexed gateway (`gateway.multiplex_profiles`) keys the right
33
+ * session. This is a Hermes profile, NOT the AgentsChat agent_id.
34
+ */
35
+ profile?: string;
30
36
  }
31
37
 
32
38
  export class IdentityTable {
@@ -114,3 +120,26 @@ export function routeInbound(table: IdentityTable, ctx: InboundContext): Identit
114
120
  export function resolveOutbound(table: IdentityTable, botId: string): Identity | null {
115
121
  return table.forBot(botId);
116
122
  }
123
+
124
+ /**
125
+ * Hermes session namespace to stamp on inbound `source.profile`.
126
+ *
127
+ * Hermes's relay adapter keys `_active_sessions` by `source.profile` whenever
128
+ * it is set, while the runner only uses that namespace when
129
+ * `multiplex_profiles` is on. Stamping the AgentsChat agent_id on a
130
+ * single-hello connection (one Hermes, one identity) splits clarify pending
131
+ * (`agent:main:…`) from intercept (`agent:<agent_id>:…`), so the user's
132
+ * answer is treated as an interrupt instead of resolving the prompt.
133
+ *
134
+ * Rules:
135
+ * - explicit `identity.profile` (Hermes profile name) always wins
136
+ * - a gateway connection that hellos MORE THAN ONE identity is multiplexing:
137
+ * stamp botId so those sessions stay isolated
138
+ * - otherwise leave profile unset so keys stay `agent:main` and clarify matches
139
+ */
140
+ export function hermesSourceProfile(id: Identity, frontedCount: number): string | undefined {
141
+ const named = typeof id.profile === "string" ? id.profile.trim() : "";
142
+ if (named) return named;
143
+ if (frontedCount > 1) return id.botId;
144
+ return undefined;
145
+ }
package/connector/run.ts CHANGED
@@ -9,10 +9,12 @@
9
9
  *
10
10
  * Multiplex (N identities, one per Hermes profile/agent):
11
11
  * RELAY_IDENTITIES = JSON array, one entry per identity:
12
- * [{"botId":"<agentschat agent_id>","token":"ac_...","gatewayId":"...","secret":"..."}, ...]
13
- * Each botId is an agentschat agent_id. The connector holds all of them, opens one
14
- * agentschat WS per identity, and routes inbound/outbound by identity — identity A's
15
- * messages never cross to identity B.
12
+ * [{"botId":"<agentschat agent_id>","token":"ac_...","gatewayId":"...","secret":"...","profile":"<hermes profile>"}, ...]
13
+ * Each botId is an agentschat agent_id. Optional `profile` is the Hermes profile
14
+ * name (for gateway.multiplex_profiles). Do NOT put the AgentsChat agent_id in
15
+ * profile — that splits Hermes session keys and breaks clarify. The connector
16
+ * holds all identities, opens one agentschat WS per identity, and routes by
17
+ * identity — identity A's messages never cross to identity B.
16
18
  *
17
19
  * Common:
18
20
  * AGENTCHAT_API_URL REST base (default https://agents-chat.com)
@@ -70,6 +72,7 @@ function resolveIdentities(): Identity[] {
70
72
  token: String(it.token),
71
73
  gatewayId: String(it.gatewayId),
72
74
  secret: String(it.secret),
75
+ ...(it.profile ? { profile: String(it.profile) } : {}),
73
76
  }));
74
77
  }
75
78
  // Single-tenant legacy env.
@@ -27,7 +27,7 @@ import { WebSocketServer, WebSocket } from "ws";
27
27
  import { verifyUpgradeToken, CLOSE_UNAUTHORIZED } from "./auth.ts";
28
28
  import { buildDescriptor, type CapabilityDescriptor } from "./descriptor.ts";
29
29
  import { toWireEvent, type AgentsChatMessage } from "./normalize.ts";
30
- import { IdentityTable, routeInbound, type Identity } from "./identities.ts";
30
+ import { IdentityTable, routeInbound, hermesSourceProfile, type Identity } from "./identities.ts";
31
31
 
32
32
  /** What the connector needs from agentschat to fulfil outbound ops, per identity. */
33
33
  export interface AgentsChatHooks {
@@ -288,10 +288,8 @@ export function startConnector(config: ConnectorConfig): ConnectorHandle {
288
288
  log(`[connector] inbound unaddressed (channel=${(msg as any).channel_id ?? "?"} mentions=${JSON.stringify((msg as any).mentioned_ids ?? [])}) — dropped`);
289
289
  return;
290
290
  }
291
- const event = toWireEvent(msg, "agentschat");
292
- if (!event) return;
293
- // Tag the fronting identity so the gateway keys the right session/profile.
294
- (event.source as any).profile = target.botId;
291
+ const baseEvent = toWireEvent(msg, "agentschat");
292
+ if (!baseEvent) return;
295
293
  if (!isDm) {
296
294
  // Attach "what happened since you were last addressed" so the agent gets
297
295
  // the conversation BETWEEN its @-mentions without being injected into
@@ -304,7 +302,7 @@ export function startConnector(config: ConnectorConfig): ConnectorHandle {
304
302
  try {
305
303
  const ctx = await hooks.getChannelContext(target.botId, msg.channel_id!, since, msg.id);
306
304
  if (ctx && ctx.length) {
307
- event.context = ctx.slice(-10).map((c) => ({
305
+ baseEvent.context = ctx.slice(-10).map((c) => ({
308
306
  text: String(c?.text ?? "").slice(0, 500),
309
307
  source: { user_name: c?.user_name, user_id: c?.user_id },
310
308
  }));
@@ -316,7 +314,13 @@ export function startConnector(config: ConnectorConfig): ConnectorHandle {
316
314
  if (msg.timestamp) lastAddressed.set(key, msg.timestamp);
317
315
  }
318
316
  for (const conn of sockets) {
319
- if (conn.fronted.has(target.botId)) send(conn.ws, { type: "inbound", event });
317
+ if (!conn.fronted.has(target.botId)) continue;
318
+ // Per-connection clone: a single-hello gateway must NOT inherit a
319
+ // profile stamp meant for a multiplexed sibling socket.
320
+ const event = { ...baseEvent, source: { ...baseEvent.source } };
321
+ const profile = hermesSourceProfile(target, conn.fronted.size);
322
+ if (profile) (event.source as any).profile = profile;
323
+ send(conn.ws, { type: "inbound", event });
320
324
  }
321
325
  },
322
326
  connections() {
package/dist/connector.js CHANGED
@@ -158,6 +158,14 @@ function routeInbound(table, ctx) {
158
158
  }
159
159
  return null;
160
160
  }
161
+ function hermesSourceProfile(id, frontedCount) {
162
+ const named = typeof id.profile === "string" ? id.profile.trim() : "";
163
+ if (named)
164
+ return named;
165
+ if (frontedCount > 1)
166
+ return id.botId;
167
+ return;
168
+ }
161
169
 
162
170
  // connector/server.ts
163
171
  function startConnector(config) {
@@ -308,10 +316,9 @@ function startConnector(config) {
308
316
  log(`[connector] inbound unaddressed (channel=${msg.channel_id ?? "?"} mentions=${JSON.stringify(msg.mentioned_ids ?? [])}) — dropped`);
309
317
  return;
310
318
  }
311
- const event = toWireEvent(msg, "agentschat");
312
- if (!event)
319
+ const baseEvent = toWireEvent(msg, "agentschat");
320
+ if (!baseEvent)
313
321
  return;
314
- event.source.profile = target.botId;
315
322
  if (!isDm) {
316
323
  const key = `${target.botId}:${msg.channel_id}`;
317
324
  const since = lastAddressed.get(key);
@@ -319,7 +326,7 @@ function startConnector(config) {
319
326
  try {
320
327
  const ctx = await hooks.getChannelContext(target.botId, msg.channel_id, since, msg.id);
321
328
  if (ctx && ctx.length) {
322
- event.context = ctx.slice(-10).map((c) => ({
329
+ baseEvent.context = ctx.slice(-10).map((c) => ({
323
330
  text: String(c?.text ?? "").slice(0, 500),
324
331
  source: { user_name: c?.user_name, user_id: c?.user_id }
325
332
  }));
@@ -332,8 +339,13 @@ function startConnector(config) {
332
339
  lastAddressed.set(key, msg.timestamp);
333
340
  }
334
341
  for (const conn of sockets) {
335
- if (conn.fronted.has(target.botId))
336
- send(conn.ws, { type: "inbound", event });
342
+ if (!conn.fronted.has(target.botId))
343
+ continue;
344
+ const event = { ...baseEvent, source: { ...baseEvent.source } };
345
+ const profile = hermesSourceProfile(target, conn.fronted.size);
346
+ if (profile)
347
+ event.source.profile = profile;
348
+ send(conn.ws, { type: "inbound", event });
337
349
  }
338
350
  },
339
351
  connections() {
@@ -398,7 +410,8 @@ function resolveIdentities() {
398
410
  agentId: String(it.agentId ?? it.botId),
399
411
  token: String(it.token),
400
412
  gatewayId: String(it.gatewayId),
401
- secret: String(it.secret)
413
+ secret: String(it.secret),
414
+ ...it.profile ? { profile: String(it.profile) } : {}
402
415
  }));
403
416
  }
404
417
  const agentId = need("AGENTCHAT_AGENT_ID");
package/dist/server.js CHANGED
@@ -361,7 +361,7 @@ async function fireGrokWake(msg, cfg) {
361
361
  var package_default = {
362
362
  name: "agentschat-mcp",
363
363
  mcpName: "io.github.swswordholy-tech/agentschat-mcp",
364
- version: "0.33.1",
364
+ version: "0.33.2",
365
365
  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.",
366
366
  type: "module",
367
367
  bin: {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agentschat-mcp",
3
3
  "mcpName": "io.github.swswordholy-tech/agentschat-mcp",
4
- "version": "0.33.1",
4
+ "version": "0.33.2",
5
5
  "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.",
6
6
  "type": "module",
7
7
  "bin": {