@hasna/conversations 0.2.37 → 0.2.38

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.38",
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,95 +47026,6 @@ 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": {}
46938
- }
46939
- });
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)}`);
46955
- server.server.notification({
46956
- method: "notifications/claude/channel",
46957
- params: {
46958
- content: msg.content,
46959
- 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 } : {}
46965
- }
46966
- }
46967
- }).catch((err) => {
46968
- console.error(`[channel-bridge] notification error: ${err}`);
46969
- });
46970
- }
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);
47011
- }
47012
- setTimeout(() => {
47013
- console.error("[channel-bridge] attempting to start polling...");
47014
- startPolling2();
47015
- }, 2000);
47016
- }
47017
- var POLL_INTERVAL_MS = 1000;
47018
- var init_channel = __esm(() => {
47019
- init_messages();
47020
- });
47021
-
47022
47029
  // src/mcp/tools/tmux.ts
47023
47030
  function sleep2(ms) {
47024
47031
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -47172,7 +47179,7 @@ var init_mcp2 = __esm(() => {
47172
47179
  registerAgentTools(server, agentFocus, getAgentFocus);
47173
47180
  registerAdvancedTools(server, import__package2.default.version);
47174
47181
  registerTmuxTools(server);
47175
- registerChannelBridge(server, () => process.env.CONVERSATIONS_AGENT_ID ?? null, () => process.env.CONVERSATIONS_SESSION_ID ?? null);
47182
+ registerChannelBridge(server);
47176
47183
  isDirectRun = import.meta.url === `file://${process.argv[1]}` || process.argv[1]?.endsWith("mcp.js") || process.argv[1]?.endsWith("mcp.ts");
47177
47184
  if (isDirectRun) {
47178
47185
  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,92 +44169,6 @@ 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
- }
44086
- });
44087
- let lastSeenId = 0;
44088
- let lastSeenSessionId = 0;
44089
- 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)}`);
44102
- server.server.notification({
44103
- method: "notifications/claude/channel",
44104
- params: {
44105
- content: msg.content,
44106
- 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 } : {}
44112
- }
44113
- }
44114
- }).catch((err) => {
44115
- console.error(`[channel-bridge] notification error: ${err}`);
44116
- });
44117
- }
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);
44158
- }
44159
- setTimeout(() => {
44160
- console.error("[channel-bridge] attempting to start polling...");
44161
- startPolling();
44162
- }, 2000);
44163
- }
44164
-
44165
44172
  // src/cli/commands/tmux.ts
44166
44173
  import { execSync } from "child_process";
44167
44174
  function sleep(ms) {
@@ -44310,7 +44317,7 @@ function registerTmuxTools(server) {
44310
44317
  // package.json
44311
44318
  var package_default = {
44312
44319
  name: "@hasna/conversations",
44313
- version: "0.2.37",
44320
+ version: "0.2.38",
44314
44321
  description: "Real-time CLI messaging for AI agents",
44315
44322
  type: "module",
44316
44323
  bin: {
@@ -44413,7 +44420,7 @@ registerProjectTools(server);
44413
44420
  registerAgentTools(server, agentFocus, getAgentFocus);
44414
44421
  registerAdvancedTools(server, package_default.version);
44415
44422
  registerTmuxTools(server);
44416
- registerChannelBridge(server, () => process.env.CONVERSATIONS_AGENT_ID ?? null, () => process.env.CONVERSATIONS_SESSION_ID ?? null);
44423
+ registerChannelBridge(server);
44417
44424
  async function startMcpServer() {
44418
44425
  const transport = new StdioServerTransport;
44419
44426
  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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/conversations",
3
- "version": "0.2.37",
3
+ "version": "0.2.38",
4
4
  "description": "Real-time CLI messaging for AI agents",
5
5
  "type": "module",
6
6
  "bin": {