@hasna/conversations 0.1.30 → 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.
package/bin/hook.js CHANGED
@@ -60,6 +60,7 @@ function getDb() {
60
60
  from_agent TEXT NOT NULL,
61
61
  to_agent TEXT NOT NULL,
62
62
  space TEXT,
63
+ project_id TEXT,
63
64
  content TEXT NOT NULL,
64
65
  priority TEXT NOT NULL DEFAULT 'normal',
65
66
  working_dir TEXT,
@@ -214,6 +215,10 @@ function getDb() {
214
215
  db.exec("ALTER TABLE messages ADD COLUMN reply_to INTEGER REFERENCES messages(id)");
215
216
  db.exec("CREATE INDEX IF NOT EXISTS idx_messages_reply_to ON messages(reply_to)");
216
217
  }
218
+ if (!colNames2.includes("project_id")) {
219
+ db.exec("ALTER TABLE messages ADD COLUMN project_id TEXT");
220
+ db.exec("CREATE INDEX IF NOT EXISTS idx_messages_project ON messages(project_id)");
221
+ }
217
222
  const presenceCols = db.prepare("PRAGMA table_info(agent_presence)").all();
218
223
  const presenceColNames = presenceCols.map((c) => c.name);
219
224
  if (!presenceColNames.includes("id")) {
package/bin/index.js CHANGED
@@ -1914,6 +1914,7 @@ function getDb() {
1914
1914
  from_agent TEXT NOT NULL,
1915
1915
  to_agent TEXT NOT NULL,
1916
1916
  space TEXT,
1917
+ project_id TEXT,
1917
1918
  content TEXT NOT NULL,
1918
1919
  priority TEXT NOT NULL DEFAULT 'normal',
1919
1920
  working_dir TEXT,
@@ -2068,6 +2069,10 @@ function getDb() {
2068
2069
  db.exec("ALTER TABLE messages ADD COLUMN reply_to INTEGER REFERENCES messages(id)");
2069
2070
  db.exec("CREATE INDEX IF NOT EXISTS idx_messages_reply_to ON messages(reply_to)");
2070
2071
  }
2072
+ if (!colNames2.includes("project_id")) {
2073
+ db.exec("ALTER TABLE messages ADD COLUMN project_id TEXT");
2074
+ db.exec("CREATE INDEX IF NOT EXISTS idx_messages_project ON messages(project_id)");
2075
+ }
2071
2076
  const presenceCols = db.prepare("PRAGMA table_info(agent_presence)").all();
2072
2077
  const presenceColNames = presenceCols.map((c) => c.name);
2073
2078
  if (!presenceColNames.includes("id")) {
@@ -2279,11 +2284,11 @@ function sendMessage(opts) {
2279
2284
  const blocking = opts.blocking ? 1 : 0;
2280
2285
  const replyTo = opts.reply_to || null;
2281
2286
  const stmt = db2.prepare(`
2282
- INSERT INTO messages (session_id, from_agent, to_agent, space, content, priority, working_dir, repository, branch, metadata, blocking, reply_to)
2283
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2287
+ INSERT INTO messages (session_id, from_agent, to_agent, space, project_id, content, priority, working_dir, repository, branch, metadata, blocking, reply_to)
2288
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2284
2289
  RETURNING *
2285
2290
  `);
2286
- const row = stmt.get(sessionId, opts.from, opts.to, opts.space || null, opts.content, normalizedPriority, opts.working_dir || null, opts.repository || null, opts.branch || null, metadata, blocking, replyTo);
2291
+ const row = stmt.get(sessionId, opts.from, opts.to, opts.space || null, opts.project_id || null, opts.content, normalizedPriority, opts.working_dir || null, opts.repository || null, opts.branch || null, metadata, blocking, replyTo);
2287
2292
  const message = parseMessage(row);
2288
2293
  if (opts.attachments && opts.attachments.length > 0) {
2289
2294
  const attachmentsDir = join3(getAttachmentsDir(), String(message.id));
@@ -2327,6 +2332,10 @@ function readMessages(opts = {}) {
2327
2332
  conditions.push("space = ?");
2328
2333
  params.push(opts.space);
2329
2334
  }
2335
+ if (opts.project_id) {
2336
+ conditions.push("project_id = ?");
2337
+ params.push(opts.project_id);
2338
+ }
2330
2339
  if (opts.since) {
2331
2340
  conditions.push("created_at > ?");
2332
2341
  params.push(opts.since);
@@ -3613,7 +3622,7 @@ var init_poll = __esm(() => {
3613
3622
  var require_package = __commonJS((exports, module) => {
3614
3623
  module.exports = {
3615
3624
  name: "@hasna/conversations",
3616
- version: "0.1.30",
3625
+ version: "0.1.32",
3617
3626
  description: "Real-time CLI messaging for AI agents",
3618
3627
  type: "module",
3619
3628
  bin: {
@@ -32563,11 +32572,23 @@ __export(exports_mcp, {
32563
32572
  startMcpServer: () => startMcpServer,
32564
32573
  server: () => server
32565
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
+ }
32566
32587
  async function startMcpServer() {
32567
32588
  const transport = new StdioServerTransport;
32568
32589
  await server.connect(transport);
32569
32590
  }
32570
- var import__package, server, isDirectRun;
32591
+ var import__package, server, agentFocus, isDirectRun;
32571
32592
  var init_mcp2 = __esm(() => {
32572
32593
  init_mcp();
32573
32594
  init_stdio2();
@@ -32583,6 +32604,7 @@ var init_mcp2 = __esm(() => {
32583
32604
  name: "conversations",
32584
32605
  version: import__package.default.version
32585
32606
  });
32607
+ agentFocus = new Map;
32586
32608
  server.registerTool("send_message", {
32587
32609
  description: "Send a DM to an agent.",
32588
32610
  inputSchema: {
@@ -32590,17 +32612,19 @@ var init_mcp2 = __esm(() => {
32590
32612
  content: exports_external.string(),
32591
32613
  from: exports_external.string().optional(),
32592
32614
  priority: exports_external.string().optional(),
32593
- blocking: exports_external.coerce.boolean().optional()
32615
+ blocking: exports_external.coerce.boolean().optional(),
32616
+ project_id: exports_external.string().optional()
32594
32617
  }
32595
32618
  }, async (args) => {
32596
- const { from: fromParam, to, content, priority, blocking } = args;
32619
+ const { from: fromParam, to, content, priority, blocking, project_id } = args;
32597
32620
  const from = resolveIdentity(fromParam);
32598
32621
  const msg = sendMessage({
32599
32622
  from,
32600
32623
  to,
32601
32624
  content,
32602
32625
  priority,
32603
- blocking
32626
+ blocking,
32627
+ project_id
32604
32628
  });
32605
32629
  return {
32606
32630
  content: [{ type: "text", text: JSON.stringify(msg) }]
@@ -32613,12 +32637,17 @@ var init_mcp2 = __esm(() => {
32613
32637
  from: exports_external.string().optional(),
32614
32638
  to: exports_external.string().optional(),
32615
32639
  space: exports_external.string().optional(),
32640
+ project_id: exports_external.string().optional(),
32616
32641
  since: exports_external.string().optional(),
32617
32642
  limit: exports_external.coerce.number().optional(),
32618
32643
  unread_only: exports_external.coerce.boolean().optional()
32619
32644
  }
32620
32645
  }, async (args) => {
32621
- 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
+ });
32622
32651
  return {
32623
32652
  content: [{ type: "text", text: JSON.stringify(messages) }]
32624
32653
  };
@@ -33210,6 +33239,58 @@ var init_mcp2 = __esm(() => {
33210
33239
  content: [{ type: "text", text: JSON.stringify(messages) }]
33211
33240
  };
33212
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
+ });
33213
33294
  server.registerTool("register_agent", {
33214
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.",
33215
33296
  inputSchema: {
@@ -33356,6 +33437,9 @@ var init_mcp2 = __esm(() => {
33356
33437
  "pin_message",
33357
33438
  "unpin_message",
33358
33439
  "get_pinned_messages",
33440
+ "set_focus",
33441
+ "get_focus",
33442
+ "unfocus",
33359
33443
  "register_agent",
33360
33444
  "heartbeat",
33361
33445
  "list_agents",
@@ -33402,6 +33486,9 @@ var init_mcp2 = __esm(() => {
33402
33486
  pin_message: "Pin a message. Required: id",
33403
33487
  unpin_message: "Unpin a message. Required: id",
33404
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?",
33405
33492
  register_agent: "Register agent with conflict detection (30min active window). Required: name, session_id. Optional: role?. Returns AgentConflictError if another session is active.",
33406
33493
  heartbeat: "Register/refresh agent presence. Optional: from?, status?(online|busy|idle, default: online)",
33407
33494
  list_agents: "List agents with presence timestamps. Optional: online_only?(only agents seen in last 60s)",
package/bin/mcp.js CHANGED
@@ -6546,6 +6546,7 @@ function getDb() {
6546
6546
  from_agent TEXT NOT NULL,
6547
6547
  to_agent TEXT NOT NULL,
6548
6548
  space TEXT,
6549
+ project_id TEXT,
6549
6550
  content TEXT NOT NULL,
6550
6551
  priority TEXT NOT NULL DEFAULT 'normal',
6551
6552
  working_dir TEXT,
@@ -6700,6 +6701,10 @@ function getDb() {
6700
6701
  db.exec("ALTER TABLE messages ADD COLUMN reply_to INTEGER REFERENCES messages(id)");
6701
6702
  db.exec("CREATE INDEX IF NOT EXISTS idx_messages_reply_to ON messages(reply_to)");
6702
6703
  }
6704
+ if (!colNames2.includes("project_id")) {
6705
+ db.exec("ALTER TABLE messages ADD COLUMN project_id TEXT");
6706
+ db.exec("CREATE INDEX IF NOT EXISTS idx_messages_project ON messages(project_id)");
6707
+ }
6703
6708
  const presenceCols = db.prepare("PRAGMA table_info(agent_presence)").all();
6704
6709
  const presenceColNames = presenceCols.map((c) => c.name);
6705
6710
  if (!presenceColNames.includes("id")) {
@@ -28732,11 +28737,11 @@ function sendMessage(opts) {
28732
28737
  const blocking = opts.blocking ? 1 : 0;
28733
28738
  const replyTo = opts.reply_to || null;
28734
28739
  const stmt = db2.prepare(`
28735
- INSERT INTO messages (session_id, from_agent, to_agent, space, content, priority, working_dir, repository, branch, metadata, blocking, reply_to)
28736
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
28740
+ INSERT INTO messages (session_id, from_agent, to_agent, space, project_id, content, priority, working_dir, repository, branch, metadata, blocking, reply_to)
28741
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
28737
28742
  RETURNING *
28738
28743
  `);
28739
- const row = stmt.get(sessionId, opts.from, opts.to, opts.space || null, opts.content, normalizedPriority, opts.working_dir || null, opts.repository || null, opts.branch || null, metadata, blocking, replyTo);
28744
+ const row = stmt.get(sessionId, opts.from, opts.to, opts.space || null, opts.project_id || null, opts.content, normalizedPriority, opts.working_dir || null, opts.repository || null, opts.branch || null, metadata, blocking, replyTo);
28740
28745
  const message = parseMessage(row);
28741
28746
  if (opts.attachments && opts.attachments.length > 0) {
28742
28747
  const attachmentsDir = join3(getAttachmentsDir(), String(message.id));
@@ -28780,6 +28785,10 @@ function readMessages(opts = {}) {
28780
28785
  conditions.push("space = ?");
28781
28786
  params.push(opts.space);
28782
28787
  }
28788
+ if (opts.project_id) {
28789
+ conditions.push("project_id = ?");
28790
+ params.push(opts.project_id);
28791
+ }
28783
28792
  if (opts.since) {
28784
28793
  conditions.push("created_at > ?");
28785
28794
  params.push(opts.since);
@@ -29854,6 +29863,12 @@ function heartbeat(agent, status, metadata, sessionId) {
29854
29863
  metadata = excluded.metadata
29855
29864
  `).run(id, normalizedAgent, sessionId ?? null, resolvedStatus, metadataJson);
29856
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
+ }
29857
29872
  function listAgents(opts) {
29858
29873
  const db2 = getDb();
29859
29874
  let query = "SELECT * FROM agent_presence";
@@ -29886,7 +29901,7 @@ function renameAgent(oldName, newName) {
29886
29901
  // package.json
29887
29902
  var package_default = {
29888
29903
  name: "@hasna/conversations",
29889
- version: "0.1.30",
29904
+ version: "0.1.32",
29890
29905
  description: "Real-time CLI messaging for AI agents",
29891
29906
  type: "module",
29892
29907
  bin: {
@@ -29968,6 +29983,19 @@ var server = new McpServer({
29968
29983
  name: "conversations",
29969
29984
  version: package_default.version
29970
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
+ }
29971
29999
  server.registerTool("send_message", {
29972
30000
  description: "Send a DM to an agent.",
29973
30001
  inputSchema: {
@@ -29975,17 +30003,19 @@ server.registerTool("send_message", {
29975
30003
  content: exports_external.string(),
29976
30004
  from: exports_external.string().optional(),
29977
30005
  priority: exports_external.string().optional(),
29978
- blocking: exports_external.coerce.boolean().optional()
30006
+ blocking: exports_external.coerce.boolean().optional(),
30007
+ project_id: exports_external.string().optional()
29979
30008
  }
29980
30009
  }, async (args) => {
29981
- const { from: fromParam, to, content, priority, blocking } = args;
30010
+ const { from: fromParam, to, content, priority, blocking, project_id } = args;
29982
30011
  const from = resolveIdentity(fromParam);
29983
30012
  const msg = sendMessage({
29984
30013
  from,
29985
30014
  to,
29986
30015
  content,
29987
30016
  priority,
29988
- blocking
30017
+ blocking,
30018
+ project_id
29989
30019
  });
29990
30020
  return {
29991
30021
  content: [{ type: "text", text: JSON.stringify(msg) }]
@@ -29998,12 +30028,17 @@ server.registerTool("read_messages", {
29998
30028
  from: exports_external.string().optional(),
29999
30029
  to: exports_external.string().optional(),
30000
30030
  space: exports_external.string().optional(),
30031
+ project_id: exports_external.string().optional(),
30001
30032
  since: exports_external.string().optional(),
30002
30033
  limit: exports_external.coerce.number().optional(),
30003
30034
  unread_only: exports_external.coerce.boolean().optional()
30004
30035
  }
30005
30036
  }, async (args) => {
30006
- 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
+ });
30007
30042
  return {
30008
30043
  content: [{ type: "text", text: JSON.stringify(messages) }]
30009
30044
  };
@@ -30595,6 +30630,58 @@ server.registerTool("get_pinned_messages", {
30595
30630
  content: [{ type: "text", text: JSON.stringify(messages) }]
30596
30631
  };
30597
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
+ });
30598
30685
  server.registerTool("register_agent", {
30599
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.",
30600
30687
  inputSchema: {
@@ -30741,6 +30828,9 @@ server.registerTool("search_tools", {
30741
30828
  "pin_message",
30742
30829
  "unpin_message",
30743
30830
  "get_pinned_messages",
30831
+ "set_focus",
30832
+ "get_focus",
30833
+ "unfocus",
30744
30834
  "register_agent",
30745
30835
  "heartbeat",
30746
30836
  "list_agents",
@@ -30787,6 +30877,9 @@ server.registerTool("describe_tools", {
30787
30877
  pin_message: "Pin a message. Required: id",
30788
30878
  unpin_message: "Unpin a message. Required: id",
30789
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?",
30790
30883
  register_agent: "Register agent with conflict detection (30min active window). Required: name, session_id. Optional: role?. Returns AgentConflictError if another session is active.",
30791
30884
  heartbeat: "Register/refresh agent presence. Optional: from?, status?(online|busy|idle, default: online)",
30792
30885
  list_agents: "List agents with presence timestamps. Optional: online_only?(only agents seen in last 60s)",
package/dist/index.js CHANGED
@@ -73,6 +73,7 @@ function getDb() {
73
73
  from_agent TEXT NOT NULL,
74
74
  to_agent TEXT NOT NULL,
75
75
  space TEXT,
76
+ project_id TEXT,
76
77
  content TEXT NOT NULL,
77
78
  priority TEXT NOT NULL DEFAULT 'normal',
78
79
  working_dir TEXT,
@@ -227,6 +228,10 @@ function getDb() {
227
228
  db.exec("ALTER TABLE messages ADD COLUMN reply_to INTEGER REFERENCES messages(id)");
228
229
  db.exec("CREATE INDEX IF NOT EXISTS idx_messages_reply_to ON messages(reply_to)");
229
230
  }
231
+ if (!colNames2.includes("project_id")) {
232
+ db.exec("ALTER TABLE messages ADD COLUMN project_id TEXT");
233
+ db.exec("CREATE INDEX IF NOT EXISTS idx_messages_project ON messages(project_id)");
234
+ }
230
235
  const presenceCols = db.prepare("PRAGMA table_info(agent_presence)").all();
231
236
  const presenceColNames = presenceCols.map((c) => c.name);
232
237
  if (!presenceColNames.includes("id")) {
@@ -2253,11 +2258,11 @@ function sendMessage(opts) {
2253
2258
  const blocking = opts.blocking ? 1 : 0;
2254
2259
  const replyTo = opts.reply_to || null;
2255
2260
  const stmt = db2.prepare(`
2256
- INSERT INTO messages (session_id, from_agent, to_agent, space, content, priority, working_dir, repository, branch, metadata, blocking, reply_to)
2257
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2261
+ INSERT INTO messages (session_id, from_agent, to_agent, space, project_id, content, priority, working_dir, repository, branch, metadata, blocking, reply_to)
2262
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2258
2263
  RETURNING *
2259
2264
  `);
2260
- const row = stmt.get(sessionId, opts.from, opts.to, opts.space || null, opts.content, normalizedPriority, opts.working_dir || null, opts.repository || null, opts.branch || null, metadata, blocking, replyTo);
2265
+ const row = stmt.get(sessionId, opts.from, opts.to, opts.space || null, opts.project_id || null, opts.content, normalizedPriority, opts.working_dir || null, opts.repository || null, opts.branch || null, metadata, blocking, replyTo);
2261
2266
  const message = parseMessage(row);
2262
2267
  if (opts.attachments && opts.attachments.length > 0) {
2263
2268
  const attachmentsDir = join3(getAttachmentsDir(), String(message.id));
@@ -2301,6 +2306,10 @@ function readMessages(opts = {}) {
2301
2306
  conditions.push("space = ?");
2302
2307
  params.push(opts.space);
2303
2308
  }
2309
+ if (opts.project_id) {
2310
+ conditions.push("project_id = ?");
2311
+ params.push(opts.project_id);
2312
+ }
2304
2313
  if (opts.since) {
2305
2314
  conditions.push("created_at > ?");
2306
2315
  params.push(opts.since);
package/dist/types.d.ts CHANGED
@@ -5,6 +5,7 @@ export interface Message {
5
5
  from_agent: string;
6
6
  to_agent: string;
7
7
  space: string | null;
8
+ project_id: string | null;
8
9
  content: string;
9
10
  priority: Priority;
10
11
  working_dir: string | null;
@@ -80,6 +81,7 @@ export interface SendMessageOptions {
80
81
  content: string;
81
82
  session_id?: string;
82
83
  space?: string;
84
+ project_id?: string;
83
85
  priority?: Priority;
84
86
  working_dir?: string;
85
87
  repository?: string;
@@ -97,6 +99,7 @@ export interface ReadMessagesOptions {
97
99
  from?: string;
98
100
  to?: string;
99
101
  space?: string;
102
+ project_id?: string;
100
103
  since?: string;
101
104
  since_id?: number;
102
105
  limit?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/conversations",
3
- "version": "0.1.30",
3
+ "version": "0.1.32",
4
4
  "description": "Real-time CLI messaging for AI agents",
5
5
  "type": "module",
6
6
  "bin": {