@hasna/conversations 0.2.37 → 0.2.39

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/bin/index.js CHANGED
@@ -14914,7 +14914,7 @@ var init_presence = __esm(() => {
14914
14914
  var require_package = __commonJS((exports, module) => {
14915
14915
  module.exports = {
14916
14916
  name: "@hasna/conversations",
14917
- version: "0.2.37",
14917
+ version: "0.2.39",
14918
14918
  description: "Real-time CLI messaging for AI agents",
14919
14919
  type: "module",
14920
14920
  bin: {
@@ -45594,6 +45594,99 @@ var init_projects2 = __esm(() => {
45594
45594
  init_identity();
45595
45595
  });
45596
45596
 
45597
+ // src/mcp/channel.ts
45598
+ function setSessionAgent(agentId) {
45599
+ sessionAgentId = agentId;
45600
+ }
45601
+ function getSessionAgent() {
45602
+ return sessionAgentId || process.env.CONVERSATIONS_AGENT_ID || null;
45603
+ }
45604
+ function registerChannelBridge(server) {
45605
+ server.server.registerCapabilities({
45606
+ experimental: { "claude/channel": {} }
45607
+ });
45608
+ let lastAgentMsgId = 0;
45609
+ let lastSessionMsgId = 0;
45610
+ let pollTimer = null;
45611
+ function getSessionId() {
45612
+ return process.env.CONVERSATIONS_SESSION_ID || null;
45613
+ }
45614
+ function seedLastSeen() {
45615
+ const agent = getSessionAgent();
45616
+ const sid = getSessionId();
45617
+ if (agent) {
45618
+ const latest = readMessages({ to: agent, order: "desc", limit: 1 });
45619
+ if (latest.length > 0)
45620
+ lastAgentMsgId = latest[0].id;
45621
+ }
45622
+ if (sid) {
45623
+ const latest = readMessages({ to: `session:${sid}`, order: "desc", limit: 1 });
45624
+ if (latest.length > 0)
45625
+ lastSessionMsgId = latest[0].id;
45626
+ }
45627
+ }
45628
+ function pushNotification(msg, isDirect) {
45629
+ if (isDirect) {
45630
+ try {
45631
+ markReadByIds([msg.id]);
45632
+ } catch {}
45633
+ }
45634
+ const context = [
45635
+ `From: ${msg.from_agent}`,
45636
+ `Mode: ${isDirect ? "direct (auto-injected, auto-read)" : "dm (passive, check inbox)"}`,
45637
+ `Message ID: ${msg.id}`,
45638
+ msg.space ? `Space: ${msg.space}` : null,
45639
+ msg.priority && msg.priority !== "normal" ? `Priority: ${msg.priority}` : null
45640
+ ].filter(Boolean).join(" | ");
45641
+ const enrichedContent = `[${context}]
45642
+ ${msg.content}`;
45643
+ server.server.notification({
45644
+ method: "notifications/claude/channel",
45645
+ params: {
45646
+ content: enrichedContent,
45647
+ meta: {
45648
+ from: msg.from_agent,
45649
+ message_id: String(msg.id),
45650
+ session_id: msg.session_id,
45651
+ mode: isDirect ? "direct" : "dm",
45652
+ ...msg.space ? { space: msg.space } : {},
45653
+ ...msg.priority && msg.priority !== "normal" ? { priority: msg.priority } : {}
45654
+ }
45655
+ }
45656
+ }).catch(() => {});
45657
+ }
45658
+ function startPolling2() {
45659
+ if (pollTimer)
45660
+ return;
45661
+ seedLastSeen();
45662
+ pollTimer = setInterval(() => {
45663
+ try {
45664
+ const agent = getSessionAgent();
45665
+ const sid = getSessionId();
45666
+ if (agent) {
45667
+ const msgs = readMessages({ to: agent, order: "asc", limit: 20 }).filter((m) => m.id > lastAgentMsgId);
45668
+ for (const msg of msgs) {
45669
+ lastAgentMsgId = msg.id;
45670
+ pushNotification(msg, false);
45671
+ }
45672
+ }
45673
+ if (sid) {
45674
+ const msgs = readMessages({ to: `session:${sid}`, order: "asc", limit: 20 }).filter((m) => m.id > lastSessionMsgId);
45675
+ for (const msg of msgs) {
45676
+ lastSessionMsgId = msg.id;
45677
+ pushNotification(msg, true);
45678
+ }
45679
+ }
45680
+ } catch {}
45681
+ }, POLL_INTERVAL_MS);
45682
+ }
45683
+ setTimeout(() => startPolling2(), 2000);
45684
+ }
45685
+ var POLL_INTERVAL_MS = 1000, sessionAgentId = null;
45686
+ var init_channel = __esm(() => {
45687
+ init_messages();
45688
+ });
45689
+
45597
45690
  // src/mcp/tools/agents.ts
45598
45691
  function registerAgentTools(server, agentFocus, getAgentFocus) {
45599
45692
  server.registerTool("register_agent", {
@@ -45608,6 +45701,7 @@ function registerAgentTools(server, agentFocus, getAgentFocus) {
45608
45701
  const { name, session_id, role, project_id } = args;
45609
45702
  try {
45610
45703
  const result = registerAgent(name, session_id, role, project_id);
45704
+ setSessionAgent(name);
45611
45705
  return {
45612
45706
  content: [{ type: "text", text: JSON.stringify(result) }]
45613
45707
  };
@@ -45631,6 +45725,7 @@ function registerAgentTools(server, agentFocus, getAgentFocus) {
45631
45725
  const { from: fromParam, status } = args;
45632
45726
  const agent = resolveIdentity(fromParam);
45633
45727
  heartbeat(agent, status);
45728
+ setSessionAgent(agent);
45634
45729
  return {
45635
45730
  content: [{ type: "text", text: JSON.stringify({ agent, status: status || "online", heartbeat: true }) }]
45636
45731
  };
@@ -45787,6 +45882,7 @@ var init_agents = __esm(() => {
45787
45882
  init_zod2();
45788
45883
  init_identity();
45789
45884
  init_presence();
45885
+ init_channel();
45790
45886
  init_sessions();
45791
45887
  init_messages();
45792
45888
  });
@@ -46930,93 +47026,96 @@ var init_cloud = __esm(() => {
46930
47026
  CONFLICT_TABLES = new Set(["spaces", "projects", "agent_presence"]);
46931
47027
  });
46932
47028
 
46933
- // src/mcp/channel.ts
46934
- function registerChannelBridge(server, getAgentId, getSessionId) {
46935
- server.server.registerCapabilities({
46936
- experimental: {
46937
- "claude/channel": {}
47029
+ // src/mcp/telegram-channel.ts
47030
+ async function telegramRequest(token, method, params) {
47031
+ const url2 = `https://api.telegram.org/bot${token}/${method}`;
47032
+ const res = await fetch(url2, {
47033
+ method: "POST",
47034
+ headers: { "Content-Type": "application/json" },
47035
+ body: params ? JSON.stringify(params) : undefined
47036
+ });
47037
+ const data = await res.json();
47038
+ if (!data.ok)
47039
+ throw new Error(`Telegram API error: ${data.description}`);
47040
+ return data.result;
47041
+ }
47042
+ function registerTelegramChannel(server) {
47043
+ const token = process.env.TELEGRAM_BOT_TOKEN;
47044
+ if (!token)
47045
+ return;
47046
+ let lastUpdateId = 0;
47047
+ let pollTimer = null;
47048
+ let botUsername = "bot";
47049
+ telegramRequest(token, "getMe").then((me) => {
47050
+ botUsername = me.username || me.first_name || "bot";
47051
+ console.error(`[telegram-channel] connected as @${botUsername}`);
47052
+ }).catch(() => {});
47053
+ server.registerTool("telegram_send", {
47054
+ description: "Send a message to a Telegram chat. Use this to reply to Telegram messages received via the channel bridge.",
47055
+ inputSchema: {
47056
+ chat_id: exports_external2.coerce.number().describe("Telegram chat ID to send to (from the incoming message's chat_id meta)"),
47057
+ text: exports_external2.string().describe("Message text to send"),
47058
+ parse_mode: exports_external2.string().optional().describe("Optional: HTML or MarkdownV2"),
47059
+ reply_to_message_id: exports_external2.coerce.number().optional().describe("Optional: message ID to reply to")
46938
47060
  }
47061
+ }, async (args) => {
47062
+ const result = await telegramRequest(token, "sendMessage", {
47063
+ chat_id: args.chat_id,
47064
+ text: args.text,
47065
+ parse_mode: args.parse_mode,
47066
+ reply_to_message_id: args.reply_to_message_id
47067
+ });
47068
+ return { content: [{ type: "text", text: JSON.stringify(result) }] };
46939
47069
  });
46940
- let lastSeenId = 0;
46941
- let lastSeenSessionId = 0;
46942
- let pollTimer = null;
46943
- function seedLastSeen(agentId, sessionId) {
46944
- const latestAgent = readMessages({ to: agentId, order: "desc", limit: 1 });
46945
- if (latestAgent.length > 0)
46946
- lastSeenId = latestAgent[0].id;
46947
- if (sessionId) {
46948
- const latestSession = readMessages({ to: `session:${sessionId}`, order: "desc", limit: 1 });
46949
- if (latestSession.length > 0)
46950
- lastSeenSessionId = latestSession[0].id;
46951
- }
46952
- }
46953
- function pushNotification(msg) {
46954
- console.error(`[channel-bridge] pushing notification: from=${msg.from_agent}, id=${msg.id}, content=${msg.content.slice(0, 50)}`);
47070
+ function pushNotification(update) {
47071
+ const msg = update.message;
47072
+ if (!msg?.text)
47073
+ return;
47074
+ const from = msg.from?.username || msg.from?.first_name || "unknown";
47075
+ const chatTitle = msg.chat.title || msg.chat.username || String(msg.chat.id);
47076
+ const context = [
47077
+ `From: ${from}`,
47078
+ `Chat: ${chatTitle} (${msg.chat.id})`,
47079
+ `Message ID: ${msg.message_id}`,
47080
+ msg.chat.type !== "private" ? `Type: ${msg.chat.type}` : null
47081
+ ].filter(Boolean).join(" | ");
47082
+ const enrichedContent = `[${context}]
47083
+ ${msg.text}`;
46955
47084
  server.server.notification({
46956
47085
  method: "notifications/claude/channel",
46957
47086
  params: {
46958
- content: msg.content,
47087
+ content: enrichedContent,
46959
47088
  meta: {
46960
- from: msg.from_agent,
46961
- message_id: String(msg.id),
46962
- session_id: msg.session_id,
46963
- ...msg.space ? { space: msg.space } : {},
46964
- ...msg.priority && msg.priority !== "normal" ? { priority: msg.priority } : {}
47089
+ from,
47090
+ chat_id: String(msg.chat.id),
47091
+ message_id: String(msg.message_id),
47092
+ chat_type: msg.chat.type,
47093
+ ...msg.chat.title ? { chat_title: msg.chat.title } : {}
46965
47094
  }
46966
47095
  }
46967
- }).catch((err) => {
46968
- console.error(`[channel-bridge] notification error: ${err}`);
46969
- });
47096
+ }).catch(() => {});
46970
47097
  }
46971
- function startPolling2() {
46972
- if (pollTimer)
46973
- return;
46974
- const agentId = getAgentId();
46975
- const sessionId = getSessionId();
46976
- console.error(`[channel-bridge] startPolling: agentId=${agentId}, sessionId=${sessionId}`);
46977
- if (!agentId && !sessionId) {
46978
- console.error("[channel-bridge] no agentId or sessionId \u2014 not polling");
46979
- return;
46980
- }
46981
- if (agentId)
46982
- seedLastSeen(agentId, sessionId);
46983
- pollTimer = setInterval(() => {
46984
- try {
46985
- const currentAgent = getAgentId();
46986
- const currentSession = getSessionId();
46987
- if (currentAgent) {
46988
- const newAgentMsgs = readMessages({
46989
- to: currentAgent,
46990
- order: "asc",
46991
- limit: 20
46992
- }).filter((m) => m.id > lastSeenId);
46993
- for (const msg of newAgentMsgs) {
46994
- lastSeenId = msg.id;
46995
- pushNotification(msg);
46996
- }
46997
- }
46998
- if (currentSession) {
46999
- const newSessionMsgs = readMessages({
47000
- to: `session:${currentSession}`,
47001
- order: "asc",
47002
- limit: 20
47003
- }).filter((m) => m.id > lastSeenSessionId);
47004
- for (const msg of newSessionMsgs) {
47005
- lastSeenSessionId = msg.id;
47006
- pushNotification(msg);
47007
- }
47008
- }
47009
- } catch {}
47010
- }, POLL_INTERVAL_MS);
47098
+ async function poll() {
47099
+ try {
47100
+ const updates = await telegramRequest(token, "getUpdates", {
47101
+ offset: lastUpdateId + 1,
47102
+ timeout: 1,
47103
+ allowed_updates: ["message"]
47104
+ });
47105
+ for (const update of updates) {
47106
+ lastUpdateId = update.update_id;
47107
+ pushNotification(update);
47108
+ }
47109
+ } catch {}
47011
47110
  }
47012
47111
  setTimeout(() => {
47013
- console.error("[channel-bridge] attempting to start polling...");
47014
- startPolling2();
47112
+ pollTimer = setInterval(() => poll(), POLL_INTERVAL_MS2);
47113
+ console.error("[telegram-channel] polling started");
47015
47114
  }, 2000);
47016
47115
  }
47017
- var POLL_INTERVAL_MS = 1000;
47018
- var init_channel = __esm(() => {
47019
- init_messages();
47116
+ var POLL_INTERVAL_MS2 = 2000;
47117
+ var init_telegram_channel = __esm(() => {
47118
+ init_zod2();
47020
47119
  });
47021
47120
 
47022
47121
  // src/mcp/tools/tmux.ts
@@ -47159,6 +47258,7 @@ var init_mcp2 = __esm(() => {
47159
47258
  init_advanced();
47160
47259
  init_cloud();
47161
47260
  init_channel();
47261
+ init_telegram_channel();
47162
47262
  init_tmux2();
47163
47263
  import__package2 = __toESM(require_package(), 1);
47164
47264
  server = new McpServer({
@@ -47172,7 +47272,8 @@ var init_mcp2 = __esm(() => {
47172
47272
  registerAgentTools(server, agentFocus, getAgentFocus);
47173
47273
  registerAdvancedTools(server, import__package2.default.version);
47174
47274
  registerTmuxTools(server);
47175
- registerChannelBridge(server, () => process.env.CONVERSATIONS_AGENT_ID ?? null, () => process.env.CONVERSATIONS_SESSION_ID ?? null);
47275
+ registerChannelBridge(server);
47276
+ registerTelegramChannel(server);
47176
47277
  isDirectRun = import.meta.url === `file://${process.argv[1]}` || process.argv[1]?.endsWith("mcp.js") || process.argv[1]?.endsWith("mcp.ts");
47177
47278
  if (isDirectRun) {
47178
47279
  startMcpServer().catch((error48) => {
package/bin/mcp.js CHANGED
@@ -42426,6 +42426,97 @@ function registerProjectTools(server) {
42426
42426
  });
42427
42427
  }
42428
42428
 
42429
+ // src/mcp/channel.ts
42430
+ var POLL_INTERVAL_MS = 1000;
42431
+ var sessionAgentId = null;
42432
+ function setSessionAgent(agentId) {
42433
+ sessionAgentId = agentId;
42434
+ }
42435
+ function getSessionAgent() {
42436
+ return sessionAgentId || process.env.CONVERSATIONS_AGENT_ID || null;
42437
+ }
42438
+ function registerChannelBridge(server) {
42439
+ server.server.registerCapabilities({
42440
+ experimental: { "claude/channel": {} }
42441
+ });
42442
+ let lastAgentMsgId = 0;
42443
+ let lastSessionMsgId = 0;
42444
+ let pollTimer = null;
42445
+ function getSessionId() {
42446
+ return process.env.CONVERSATIONS_SESSION_ID || null;
42447
+ }
42448
+ function seedLastSeen() {
42449
+ const agent = getSessionAgent();
42450
+ const sid = getSessionId();
42451
+ if (agent) {
42452
+ const latest = readMessages({ to: agent, order: "desc", limit: 1 });
42453
+ if (latest.length > 0)
42454
+ lastAgentMsgId = latest[0].id;
42455
+ }
42456
+ if (sid) {
42457
+ const latest = readMessages({ to: `session:${sid}`, order: "desc", limit: 1 });
42458
+ if (latest.length > 0)
42459
+ lastSessionMsgId = latest[0].id;
42460
+ }
42461
+ }
42462
+ function pushNotification(msg, isDirect) {
42463
+ if (isDirect) {
42464
+ try {
42465
+ markReadByIds([msg.id]);
42466
+ } catch {}
42467
+ }
42468
+ const context = [
42469
+ `From: ${msg.from_agent}`,
42470
+ `Mode: ${isDirect ? "direct (auto-injected, auto-read)" : "dm (passive, check inbox)"}`,
42471
+ `Message ID: ${msg.id}`,
42472
+ msg.space ? `Space: ${msg.space}` : null,
42473
+ msg.priority && msg.priority !== "normal" ? `Priority: ${msg.priority}` : null
42474
+ ].filter(Boolean).join(" | ");
42475
+ const enrichedContent = `[${context}]
42476
+ ${msg.content}`;
42477
+ server.server.notification({
42478
+ method: "notifications/claude/channel",
42479
+ params: {
42480
+ content: enrichedContent,
42481
+ meta: {
42482
+ from: msg.from_agent,
42483
+ message_id: String(msg.id),
42484
+ session_id: msg.session_id,
42485
+ mode: isDirect ? "direct" : "dm",
42486
+ ...msg.space ? { space: msg.space } : {},
42487
+ ...msg.priority && msg.priority !== "normal" ? { priority: msg.priority } : {}
42488
+ }
42489
+ }
42490
+ }).catch(() => {});
42491
+ }
42492
+ function startPolling() {
42493
+ if (pollTimer)
42494
+ return;
42495
+ seedLastSeen();
42496
+ pollTimer = setInterval(() => {
42497
+ try {
42498
+ const agent = getSessionAgent();
42499
+ const sid = getSessionId();
42500
+ if (agent) {
42501
+ const msgs = readMessages({ to: agent, order: "asc", limit: 20 }).filter((m) => m.id > lastAgentMsgId);
42502
+ for (const msg of msgs) {
42503
+ lastAgentMsgId = msg.id;
42504
+ pushNotification(msg, false);
42505
+ }
42506
+ }
42507
+ if (sid) {
42508
+ const msgs = readMessages({ to: `session:${sid}`, order: "asc", limit: 20 }).filter((m) => m.id > lastSessionMsgId);
42509
+ for (const msg of msgs) {
42510
+ lastSessionMsgId = msg.id;
42511
+ pushNotification(msg, true);
42512
+ }
42513
+ }
42514
+ } catch {}
42515
+ }, POLL_INTERVAL_MS);
42516
+ }
42517
+ setTimeout(() => startPolling(), 2000);
42518
+ }
42519
+
42429
42520
  // src/mcp/tools/agents.ts
42430
42521
  function registerAgentTools(server, agentFocus, getAgentFocus) {
42431
42522
  server.registerTool("register_agent", {
@@ -42440,6 +42531,7 @@ function registerAgentTools(server, agentFocus, getAgentFocus) {
42440
42531
  const { name, session_id, role, project_id } = args;
42441
42532
  try {
42442
42533
  const result = registerAgent(name, session_id, role, project_id);
42534
+ setSessionAgent(name);
42443
42535
  return {
42444
42536
  content: [{ type: "text", text: JSON.stringify(result) }]
42445
42537
  };
@@ -42463,6 +42555,7 @@ function registerAgentTools(server, agentFocus, getAgentFocus) {
42463
42555
  const { from: fromParam, status } = args;
42464
42556
  const agent = resolveIdentity(fromParam);
42465
42557
  heartbeat(agent, status);
42558
+ setSessionAgent(agent);
42466
42559
  return {
42467
42560
  content: [{ type: "text", text: JSON.stringify({ agent, status: status || "online", heartbeat: true }) }]
42468
42561
  };
@@ -44076,89 +44169,92 @@ function formatError2(e) {
44076
44169
  return String(e);
44077
44170
  }
44078
44171
 
44079
- // src/mcp/channel.ts
44080
- var POLL_INTERVAL_MS = 1000;
44081
- function registerChannelBridge(server, getAgentId, getSessionId) {
44082
- server.server.registerCapabilities({
44083
- experimental: {
44084
- "claude/channel": {}
44085
- }
44172
+ // src/mcp/telegram-channel.ts
44173
+ var POLL_INTERVAL_MS2 = 2000;
44174
+ async function telegramRequest(token, method, params) {
44175
+ const url2 = `https://api.telegram.org/bot${token}/${method}`;
44176
+ const res = await fetch(url2, {
44177
+ method: "POST",
44178
+ headers: { "Content-Type": "application/json" },
44179
+ body: params ? JSON.stringify(params) : undefined
44086
44180
  });
44087
- let lastSeenId = 0;
44088
- let lastSeenSessionId = 0;
44181
+ const data = await res.json();
44182
+ if (!data.ok)
44183
+ throw new Error(`Telegram API error: ${data.description}`);
44184
+ return data.result;
44185
+ }
44186
+ function registerTelegramChannel(server) {
44187
+ const token = process.env.TELEGRAM_BOT_TOKEN;
44188
+ if (!token)
44189
+ return;
44190
+ let lastUpdateId = 0;
44089
44191
  let pollTimer = null;
44090
- function seedLastSeen(agentId, sessionId) {
44091
- const latestAgent = readMessages({ to: agentId, order: "desc", limit: 1 });
44092
- if (latestAgent.length > 0)
44093
- lastSeenId = latestAgent[0].id;
44094
- if (sessionId) {
44095
- const latestSession = readMessages({ to: `session:${sessionId}`, order: "desc", limit: 1 });
44096
- if (latestSession.length > 0)
44097
- lastSeenSessionId = latestSession[0].id;
44098
- }
44099
- }
44100
- function pushNotification(msg) {
44101
- console.error(`[channel-bridge] pushing notification: from=${msg.from_agent}, id=${msg.id}, content=${msg.content.slice(0, 50)}`);
44192
+ let botUsername = "bot";
44193
+ telegramRequest(token, "getMe").then((me) => {
44194
+ botUsername = me.username || me.first_name || "bot";
44195
+ console.error(`[telegram-channel] connected as @${botUsername}`);
44196
+ }).catch(() => {});
44197
+ server.registerTool("telegram_send", {
44198
+ description: "Send a message to a Telegram chat. Use this to reply to Telegram messages received via the channel bridge.",
44199
+ inputSchema: {
44200
+ chat_id: exports_external.coerce.number().describe("Telegram chat ID to send to (from the incoming message's chat_id meta)"),
44201
+ text: exports_external.string().describe("Message text to send"),
44202
+ parse_mode: exports_external.string().optional().describe("Optional: HTML or MarkdownV2"),
44203
+ reply_to_message_id: exports_external.coerce.number().optional().describe("Optional: message ID to reply to")
44204
+ }
44205
+ }, async (args) => {
44206
+ const result = await telegramRequest(token, "sendMessage", {
44207
+ chat_id: args.chat_id,
44208
+ text: args.text,
44209
+ parse_mode: args.parse_mode,
44210
+ reply_to_message_id: args.reply_to_message_id
44211
+ });
44212
+ return { content: [{ type: "text", text: JSON.stringify(result) }] };
44213
+ });
44214
+ function pushNotification(update) {
44215
+ const msg = update.message;
44216
+ if (!msg?.text)
44217
+ return;
44218
+ const from = msg.from?.username || msg.from?.first_name || "unknown";
44219
+ const chatTitle = msg.chat.title || msg.chat.username || String(msg.chat.id);
44220
+ const context = [
44221
+ `From: ${from}`,
44222
+ `Chat: ${chatTitle} (${msg.chat.id})`,
44223
+ `Message ID: ${msg.message_id}`,
44224
+ msg.chat.type !== "private" ? `Type: ${msg.chat.type}` : null
44225
+ ].filter(Boolean).join(" | ");
44226
+ const enrichedContent = `[${context}]
44227
+ ${msg.text}`;
44102
44228
  server.server.notification({
44103
44229
  method: "notifications/claude/channel",
44104
44230
  params: {
44105
- content: msg.content,
44231
+ content: enrichedContent,
44106
44232
  meta: {
44107
- from: msg.from_agent,
44108
- message_id: String(msg.id),
44109
- session_id: msg.session_id,
44110
- ...msg.space ? { space: msg.space } : {},
44111
- ...msg.priority && msg.priority !== "normal" ? { priority: msg.priority } : {}
44233
+ from,
44234
+ chat_id: String(msg.chat.id),
44235
+ message_id: String(msg.message_id),
44236
+ chat_type: msg.chat.type,
44237
+ ...msg.chat.title ? { chat_title: msg.chat.title } : {}
44112
44238
  }
44113
44239
  }
44114
- }).catch((err) => {
44115
- console.error(`[channel-bridge] notification error: ${err}`);
44116
- });
44240
+ }).catch(() => {});
44117
44241
  }
44118
- function startPolling() {
44119
- if (pollTimer)
44120
- return;
44121
- const agentId = getAgentId();
44122
- const sessionId = getSessionId();
44123
- console.error(`[channel-bridge] startPolling: agentId=${agentId}, sessionId=${sessionId}`);
44124
- if (!agentId && !sessionId) {
44125
- console.error("[channel-bridge] no agentId or sessionId \u2014 not polling");
44126
- return;
44127
- }
44128
- if (agentId)
44129
- seedLastSeen(agentId, sessionId);
44130
- pollTimer = setInterval(() => {
44131
- try {
44132
- const currentAgent = getAgentId();
44133
- const currentSession = getSessionId();
44134
- if (currentAgent) {
44135
- const newAgentMsgs = readMessages({
44136
- to: currentAgent,
44137
- order: "asc",
44138
- limit: 20
44139
- }).filter((m) => m.id > lastSeenId);
44140
- for (const msg of newAgentMsgs) {
44141
- lastSeenId = msg.id;
44142
- pushNotification(msg);
44143
- }
44144
- }
44145
- if (currentSession) {
44146
- const newSessionMsgs = readMessages({
44147
- to: `session:${currentSession}`,
44148
- order: "asc",
44149
- limit: 20
44150
- }).filter((m) => m.id > lastSeenSessionId);
44151
- for (const msg of newSessionMsgs) {
44152
- lastSeenSessionId = msg.id;
44153
- pushNotification(msg);
44154
- }
44155
- }
44156
- } catch {}
44157
- }, POLL_INTERVAL_MS);
44242
+ async function poll() {
44243
+ try {
44244
+ const updates = await telegramRequest(token, "getUpdates", {
44245
+ offset: lastUpdateId + 1,
44246
+ timeout: 1,
44247
+ allowed_updates: ["message"]
44248
+ });
44249
+ for (const update of updates) {
44250
+ lastUpdateId = update.update_id;
44251
+ pushNotification(update);
44252
+ }
44253
+ } catch {}
44158
44254
  }
44159
44255
  setTimeout(() => {
44160
- console.error("[channel-bridge] attempting to start polling...");
44161
- startPolling();
44256
+ pollTimer = setInterval(() => poll(), POLL_INTERVAL_MS2);
44257
+ console.error("[telegram-channel] polling started");
44162
44258
  }, 2000);
44163
44259
  }
44164
44260
 
@@ -44310,7 +44406,7 @@ function registerTmuxTools(server) {
44310
44406
  // package.json
44311
44407
  var package_default = {
44312
44408
  name: "@hasna/conversations",
44313
- version: "0.2.37",
44409
+ version: "0.2.39",
44314
44410
  description: "Real-time CLI messaging for AI agents",
44315
44411
  type: "module",
44316
44412
  bin: {
@@ -44413,7 +44509,8 @@ registerProjectTools(server);
44413
44509
  registerAgentTools(server, agentFocus, getAgentFocus);
44414
44510
  registerAdvancedTools(server, package_default.version);
44415
44511
  registerTmuxTools(server);
44416
- registerChannelBridge(server, () => process.env.CONVERSATIONS_AGENT_ID ?? null, () => process.env.CONVERSATIONS_SESSION_ID ?? null);
44512
+ registerChannelBridge(server);
44513
+ registerTelegramChannel(server);
44417
44514
  async function startMcpServer() {
44418
44515
  const transport = new StdioServerTransport;
44419
44516
  registerCloudSyncTools(server);
@@ -1,14 +1,21 @@
1
1
  /**
2
2
  * Claude Code channel bridge for conversations MCP server.
3
3
  *
4
- * Declares `experimental['claude/channel']` capability so agent-claude
5
- * can use this server as a channel for inter-session messaging.
4
+ * Two messaging modes:
5
+ * 1. Direct inject (send_to_session) targets session ID, auto-injected
6
+ * via channel notification, auto-marked as read
7
+ * 2. DM (send_message) — targets agent name, sits in inbox unread
8
+ * until the agent checks, also injected if agent is online
6
9
  *
7
- * Polls for new messages addressed to:
8
- * 1. The agent name (CONVERSATIONS_AGENT_ID) standard DMs
9
- * 2. The session ID (session:<uuid>) targeted session injection via send_to_session
10
+ * The bridge figures out who it is from:
11
+ * - The agent that registered/heartbeated in this MCP session
12
+ * - The CONVERSATIONS_SESSION_ID env var (set by agent-claude)
13
+ * - Falls back to CONVERSATIONS_AGENT_ID if set
10
14
  *
11
- * Messages are pushed as `notifications/claude/channel` events.
15
+ * No manual config needed just connect and go.
12
16
  */
13
17
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
14
- export declare function registerChannelBridge(server: McpServer, getAgentId: () => string | null, getSessionId: () => string | null): void;
18
+ /** Called by agent tools when register_agent or heartbeat fires */
19
+ export declare function setSessionAgent(agentId: string): void;
20
+ export declare function getSessionAgent(): string | null;
21
+ export declare function registerChannelBridge(server: McpServer): void;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Telegram channel bridge for conversations MCP server.
3
+ *
4
+ * Polls a Telegram bot for new messages and pushes them as
5
+ * `notifications/claude/channel` events. Also registers a
6
+ * `telegram_send` tool for replying.
7
+ *
8
+ * Requires: TELEGRAM_BOT_TOKEN env var or connect-telegram profile.
9
+ *
10
+ * Usage: Set TELEGRAM_BOT_TOKEN and the bridge auto-starts.
11
+ */
12
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
13
+ export declare function registerTelegramChannel(server: McpServer): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/conversations",
3
- "version": "0.2.37",
3
+ "version": "0.2.39",
4
4
  "description": "Real-time CLI messaging for AI agents",
5
5
  "type": "module",
6
6
  "bin": {