@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 +177 -76
- package/bin/mcp.js +171 -74
- package/dist/mcp/channel.d.ts +14 -7
- package/dist/mcp/telegram-channel.d.ts +13 -0
- package/package.json +1 -1
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.
|
|
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
|
|
46935
|
-
|
|
46936
|
-
|
|
46937
|
-
|
|
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
|
-
|
|
46941
|
-
|
|
46942
|
-
|
|
46943
|
-
|
|
46944
|
-
const
|
|
46945
|
-
|
|
46946
|
-
|
|
46947
|
-
|
|
46948
|
-
|
|
46949
|
-
|
|
46950
|
-
|
|
46951
|
-
|
|
46952
|
-
|
|
46953
|
-
|
|
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:
|
|
47087
|
+
content: enrichedContent,
|
|
46959
47088
|
meta: {
|
|
46960
|
-
from
|
|
46961
|
-
|
|
46962
|
-
|
|
46963
|
-
|
|
46964
|
-
...msg.
|
|
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((
|
|
46968
|
-
console.error(`[channel-bridge] notification error: ${err}`);
|
|
46969
|
-
});
|
|
47096
|
+
}).catch(() => {});
|
|
46970
47097
|
}
|
|
46971
|
-
function
|
|
46972
|
-
|
|
46973
|
-
|
|
46974
|
-
|
|
46975
|
-
|
|
46976
|
-
|
|
46977
|
-
|
|
46978
|
-
|
|
46979
|
-
|
|
46980
|
-
|
|
46981
|
-
|
|
46982
|
-
|
|
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
|
-
|
|
47014
|
-
|
|
47112
|
+
pollTimer = setInterval(() => poll(), POLL_INTERVAL_MS2);
|
|
47113
|
+
console.error("[telegram-channel] polling started");
|
|
47015
47114
|
}, 2000);
|
|
47016
47115
|
}
|
|
47017
|
-
var
|
|
47018
|
-
var
|
|
47019
|
-
|
|
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
|
|
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
|
|
44081
|
-
function
|
|
44082
|
-
|
|
44083
|
-
|
|
44084
|
-
|
|
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
|
-
|
|
44088
|
-
|
|
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
|
-
|
|
44091
|
-
|
|
44092
|
-
|
|
44093
|
-
|
|
44094
|
-
|
|
44095
|
-
|
|
44096
|
-
|
|
44097
|
-
|
|
44098
|
-
|
|
44099
|
-
|
|
44100
|
-
|
|
44101
|
-
|
|
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:
|
|
44231
|
+
content: enrichedContent,
|
|
44106
44232
|
meta: {
|
|
44107
|
-
from
|
|
44108
|
-
|
|
44109
|
-
|
|
44110
|
-
|
|
44111
|
-
...msg.
|
|
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((
|
|
44115
|
-
console.error(`[channel-bridge] notification error: ${err}`);
|
|
44116
|
-
});
|
|
44240
|
+
}).catch(() => {});
|
|
44117
44241
|
}
|
|
44118
|
-
function
|
|
44119
|
-
|
|
44120
|
-
|
|
44121
|
-
|
|
44122
|
-
|
|
44123
|
-
|
|
44124
|
-
|
|
44125
|
-
|
|
44126
|
-
|
|
44127
|
-
|
|
44128
|
-
|
|
44129
|
-
|
|
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
|
-
|
|
44161
|
-
|
|
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.
|
|
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
|
|
44512
|
+
registerChannelBridge(server);
|
|
44513
|
+
registerTelegramChannel(server);
|
|
44417
44514
|
async function startMcpServer() {
|
|
44418
44515
|
const transport = new StdioServerTransport;
|
|
44419
44516
|
registerCloudSyncTools(server);
|
package/dist/mcp/channel.d.ts
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Claude Code channel bridge for conversations MCP server.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
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
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
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
|
-
*
|
|
15
|
+
* No manual config needed — just connect and go.
|
|
12
16
|
*/
|
|
13
17
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
14
|
-
|
|
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;
|