@hasna/conversations 0.1.31 → 0.1.32

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.
Files changed (3) hide show
  1. package/bin/index.js +78 -3
  2. package/bin/mcp.js +83 -2
  3. package/package.json +1 -1
package/bin/index.js CHANGED
@@ -3622,7 +3622,7 @@ var init_poll = __esm(() => {
3622
3622
  var require_package = __commonJS((exports, module) => {
3623
3623
  module.exports = {
3624
3624
  name: "@hasna/conversations",
3625
- version: "0.1.31",
3625
+ version: "0.1.32",
3626
3626
  description: "Real-time CLI messaging for AI agents",
3627
3627
  type: "module",
3628
3628
  bin: {
@@ -32572,11 +32572,23 @@ __export(exports_mcp, {
32572
32572
  startMcpServer: () => startMcpServer,
32573
32573
  server: () => server
32574
32574
  });
32575
+ function getAgentFocus(agentId) {
32576
+ if (agentFocus.has(agentId))
32577
+ return agentFocus.get(agentId).project_id;
32578
+ const presence = getPresence(agentId);
32579
+ return presence?.project_id ?? null;
32580
+ }
32581
+ function resolveProjectId(explicitProjectId, agentId) {
32582
+ if (explicitProjectId)
32583
+ return explicitProjectId;
32584
+ const focused = getAgentFocus(agentId);
32585
+ return focused ?? undefined;
32586
+ }
32575
32587
  async function startMcpServer() {
32576
32588
  const transport = new StdioServerTransport;
32577
32589
  await server.connect(transport);
32578
32590
  }
32579
- var import__package, server, isDirectRun;
32591
+ var import__package, server, agentFocus, isDirectRun;
32580
32592
  var init_mcp2 = __esm(() => {
32581
32593
  init_mcp();
32582
32594
  init_stdio2();
@@ -32592,6 +32604,7 @@ var init_mcp2 = __esm(() => {
32592
32604
  name: "conversations",
32593
32605
  version: import__package.default.version
32594
32606
  });
32607
+ agentFocus = new Map;
32595
32608
  server.registerTool("send_message", {
32596
32609
  description: "Send a DM to an agent.",
32597
32610
  inputSchema: {
@@ -32630,7 +32643,11 @@ var init_mcp2 = __esm(() => {
32630
32643
  unread_only: exports_external.coerce.boolean().optional()
32631
32644
  }
32632
32645
  }, async (args) => {
32633
- const messages = readMessages(args);
32646
+ const agent = resolveIdentity(args.from);
32647
+ const messages = readMessages({
32648
+ ...args,
32649
+ project_id: args.project_id ?? resolveProjectId(undefined, agent)
32650
+ });
32634
32651
  return {
32635
32652
  content: [{ type: "text", text: JSON.stringify(messages) }]
32636
32653
  };
@@ -33222,6 +33239,58 @@ var init_mcp2 = __esm(() => {
33222
33239
  content: [{ type: "text", text: JSON.stringify(messages) }]
33223
33240
  };
33224
33241
  });
33242
+ server.registerTool("set_focus", {
33243
+ description: "Set agent focus to a project. All read-heavy tools will default to this project scope. Stores in MCP session memory AND updates agent_presence.project_id in DB.",
33244
+ inputSchema: {
33245
+ project_id: exports_external.string(),
33246
+ from: exports_external.string().optional()
33247
+ }
33248
+ }, async (args) => {
33249
+ const { project_id, from: fromParam } = args;
33250
+ const agent = resolveIdentity(fromParam);
33251
+ agentFocus.set(agent, { project_id });
33252
+ const db2 = (await Promise.resolve().then(() => (init_db(), exports_db))).getDb();
33253
+ db2.prepare("UPDATE agent_presence SET project_id = ? WHERE agent = ?").run(project_id, agent);
33254
+ return {
33255
+ content: [{ type: "text", text: JSON.stringify({ agent, focused: true, project_id }) }]
33256
+ };
33257
+ });
33258
+ server.registerTool("get_focus", {
33259
+ description: "Get the current focus state for an agent. Returns session focus, DB project_id, and effective project_id used for filtering.",
33260
+ inputSchema: {
33261
+ from: exports_external.string().optional()
33262
+ }
33263
+ }, async (args) => {
33264
+ const agent = resolveIdentity(args.from);
33265
+ const sessionFocus = agentFocus.get(agent) ?? null;
33266
+ const presence = getPresence(agent);
33267
+ const effective = getAgentFocus(agent);
33268
+ return {
33269
+ content: [{
33270
+ type: "text",
33271
+ text: JSON.stringify({
33272
+ agent,
33273
+ session_focus: sessionFocus?.project_id ?? null,
33274
+ db_project_id: presence?.project_id ?? null,
33275
+ effective_project_id: effective
33276
+ })
33277
+ }]
33278
+ };
33279
+ });
33280
+ server.registerTool("unfocus", {
33281
+ description: "Clear agent focus. Removes session focus and clears agent_presence.project_id in DB.",
33282
+ inputSchema: {
33283
+ from: exports_external.string().optional()
33284
+ }
33285
+ }, async (args) => {
33286
+ const agent = resolveIdentity(args.from);
33287
+ agentFocus.delete(agent);
33288
+ const db2 = (await Promise.resolve().then(() => (init_db(), exports_db))).getDb();
33289
+ db2.prepare("UPDATE agent_presence SET project_id = NULL WHERE agent = ?").run(agent);
33290
+ return {
33291
+ content: [{ type: "text", text: JSON.stringify({ agent, focused: false, project_id: null }) }]
33292
+ };
33293
+ });
33225
33294
  server.registerTool("register_agent", {
33226
33295
  description: "Register an agent with conflict detection. Returns AgentConflictError if another active session exists (active = heartbeat within last 30 min). Optional project_id locks agent to a project for the session.",
33227
33296
  inputSchema: {
@@ -33368,6 +33437,9 @@ var init_mcp2 = __esm(() => {
33368
33437
  "pin_message",
33369
33438
  "unpin_message",
33370
33439
  "get_pinned_messages",
33440
+ "set_focus",
33441
+ "get_focus",
33442
+ "unfocus",
33371
33443
  "register_agent",
33372
33444
  "heartbeat",
33373
33445
  "list_agents",
@@ -33414,6 +33486,9 @@ var init_mcp2 = __esm(() => {
33414
33486
  pin_message: "Pin a message. Required: id",
33415
33487
  unpin_message: "Unpin a message. Required: id",
33416
33488
  get_pinned_messages: "Get pinned messages. Optional: space?, session_id?, limit?",
33489
+ set_focus: "Set agent focus to a project. All read tools default to this scope. Required: project_id. Optional: from?",
33490
+ get_focus: "Get current focus: session focus, DB project_id, effective project_id. Optional: from?",
33491
+ unfocus: "Clear agent focus (session + DB). Optional: from?",
33417
33492
  register_agent: "Register agent with conflict detection (30min active window). Required: name, session_id. Optional: role?. Returns AgentConflictError if another session is active.",
33418
33493
  heartbeat: "Register/refresh agent presence. Optional: from?, status?(online|busy|idle, default: online)",
33419
33494
  list_agents: "List agents with presence timestamps. Optional: online_only?(only agents seen in last 60s)",
package/bin/mcp.js CHANGED
@@ -29863,6 +29863,12 @@ function heartbeat(agent, status, metadata, sessionId) {
29863
29863
  metadata = excluded.metadata
29864
29864
  `).run(id, normalizedAgent, sessionId ?? null, resolvedStatus, metadataJson);
29865
29865
  }
29866
+ function getPresence(agent) {
29867
+ const db2 = getDb();
29868
+ const normalizedAgent = agent.trim().toLowerCase();
29869
+ const row = db2.prepare("SELECT * FROM agent_presence WHERE LOWER(agent) = ?").get(normalizedAgent);
29870
+ return row ? parsePresence(row) : null;
29871
+ }
29866
29872
  function listAgents(opts) {
29867
29873
  const db2 = getDb();
29868
29874
  let query = "SELECT * FROM agent_presence";
@@ -29895,7 +29901,7 @@ function renameAgent(oldName, newName) {
29895
29901
  // package.json
29896
29902
  var package_default = {
29897
29903
  name: "@hasna/conversations",
29898
- version: "0.1.31",
29904
+ version: "0.1.32",
29899
29905
  description: "Real-time CLI messaging for AI agents",
29900
29906
  type: "module",
29901
29907
  bin: {
@@ -29977,6 +29983,19 @@ var server = new McpServer({
29977
29983
  name: "conversations",
29978
29984
  version: package_default.version
29979
29985
  });
29986
+ var agentFocus = new Map;
29987
+ function getAgentFocus(agentId) {
29988
+ if (agentFocus.has(agentId))
29989
+ return agentFocus.get(agentId).project_id;
29990
+ const presence = getPresence(agentId);
29991
+ return presence?.project_id ?? null;
29992
+ }
29993
+ function resolveProjectId(explicitProjectId, agentId) {
29994
+ if (explicitProjectId)
29995
+ return explicitProjectId;
29996
+ const focused = getAgentFocus(agentId);
29997
+ return focused ?? undefined;
29998
+ }
29980
29999
  server.registerTool("send_message", {
29981
30000
  description: "Send a DM to an agent.",
29982
30001
  inputSchema: {
@@ -30015,7 +30034,11 @@ server.registerTool("read_messages", {
30015
30034
  unread_only: exports_external.coerce.boolean().optional()
30016
30035
  }
30017
30036
  }, async (args) => {
30018
- const messages = readMessages(args);
30037
+ const agent = resolveIdentity(args.from);
30038
+ const messages = readMessages({
30039
+ ...args,
30040
+ project_id: args.project_id ?? resolveProjectId(undefined, agent)
30041
+ });
30019
30042
  return {
30020
30043
  content: [{ type: "text", text: JSON.stringify(messages) }]
30021
30044
  };
@@ -30607,6 +30630,58 @@ server.registerTool("get_pinned_messages", {
30607
30630
  content: [{ type: "text", text: JSON.stringify(messages) }]
30608
30631
  };
30609
30632
  });
30633
+ server.registerTool("set_focus", {
30634
+ description: "Set agent focus to a project. All read-heavy tools will default to this project scope. Stores in MCP session memory AND updates agent_presence.project_id in DB.",
30635
+ inputSchema: {
30636
+ project_id: exports_external.string(),
30637
+ from: exports_external.string().optional()
30638
+ }
30639
+ }, async (args) => {
30640
+ const { project_id, from: fromParam } = args;
30641
+ const agent = resolveIdentity(fromParam);
30642
+ agentFocus.set(agent, { project_id });
30643
+ const db2 = (await Promise.resolve().then(() => (init_db(), exports_db))).getDb();
30644
+ db2.prepare("UPDATE agent_presence SET project_id = ? WHERE agent = ?").run(project_id, agent);
30645
+ return {
30646
+ content: [{ type: "text", text: JSON.stringify({ agent, focused: true, project_id }) }]
30647
+ };
30648
+ });
30649
+ server.registerTool("get_focus", {
30650
+ description: "Get the current focus state for an agent. Returns session focus, DB project_id, and effective project_id used for filtering.",
30651
+ inputSchema: {
30652
+ from: exports_external.string().optional()
30653
+ }
30654
+ }, async (args) => {
30655
+ const agent = resolveIdentity(args.from);
30656
+ const sessionFocus = agentFocus.get(agent) ?? null;
30657
+ const presence = getPresence(agent);
30658
+ const effective = getAgentFocus(agent);
30659
+ return {
30660
+ content: [{
30661
+ type: "text",
30662
+ text: JSON.stringify({
30663
+ agent,
30664
+ session_focus: sessionFocus?.project_id ?? null,
30665
+ db_project_id: presence?.project_id ?? null,
30666
+ effective_project_id: effective
30667
+ })
30668
+ }]
30669
+ };
30670
+ });
30671
+ server.registerTool("unfocus", {
30672
+ description: "Clear agent focus. Removes session focus and clears agent_presence.project_id in DB.",
30673
+ inputSchema: {
30674
+ from: exports_external.string().optional()
30675
+ }
30676
+ }, async (args) => {
30677
+ const agent = resolveIdentity(args.from);
30678
+ agentFocus.delete(agent);
30679
+ const db2 = (await Promise.resolve().then(() => (init_db(), exports_db))).getDb();
30680
+ db2.prepare("UPDATE agent_presence SET project_id = NULL WHERE agent = ?").run(agent);
30681
+ return {
30682
+ content: [{ type: "text", text: JSON.stringify({ agent, focused: false, project_id: null }) }]
30683
+ };
30684
+ });
30610
30685
  server.registerTool("register_agent", {
30611
30686
  description: "Register an agent with conflict detection. Returns AgentConflictError if another active session exists (active = heartbeat within last 30 min). Optional project_id locks agent to a project for the session.",
30612
30687
  inputSchema: {
@@ -30753,6 +30828,9 @@ server.registerTool("search_tools", {
30753
30828
  "pin_message",
30754
30829
  "unpin_message",
30755
30830
  "get_pinned_messages",
30831
+ "set_focus",
30832
+ "get_focus",
30833
+ "unfocus",
30756
30834
  "register_agent",
30757
30835
  "heartbeat",
30758
30836
  "list_agents",
@@ -30799,6 +30877,9 @@ server.registerTool("describe_tools", {
30799
30877
  pin_message: "Pin a message. Required: id",
30800
30878
  unpin_message: "Unpin a message. Required: id",
30801
30879
  get_pinned_messages: "Get pinned messages. Optional: space?, session_id?, limit?",
30880
+ set_focus: "Set agent focus to a project. All read tools default to this scope. Required: project_id. Optional: from?",
30881
+ get_focus: "Get current focus: session focus, DB project_id, effective project_id. Optional: from?",
30882
+ unfocus: "Clear agent focus (session + DB). Optional: from?",
30802
30883
  register_agent: "Register agent with conflict detection (30min active window). Required: name, session_id. Optional: role?. Returns AgentConflictError if another session is active.",
30803
30884
  heartbeat: "Register/refresh agent presence. Optional: from?, status?(online|busy|idle, default: online)",
30804
30885
  list_agents: "List agents with presence timestamps. Optional: online_only?(only agents seen in last 60s)",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/conversations",
3
- "version": "0.1.31",
3
+ "version": "0.1.32",
4
4
  "description": "Real-time CLI messaging for AI agents",
5
5
  "type": "module",
6
6
  "bin": {