@hasna/conversations 0.1.26 → 0.1.27

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
@@ -3305,6 +3305,14 @@ function resolveIdentity(explicit) {
3305
3305
  return envValue;
3306
3306
  return getAutoName();
3307
3307
  }
3308
+ function updateCachedAutoName(newName) {
3309
+ cachedAutoName = newName;
3310
+ try {
3311
+ mkdirSync3(dirname2(AGENT_ID_FILE), { recursive: true });
3312
+ writeFileSync(AGENT_ID_FILE, newName + `
3313
+ `, "utf-8");
3314
+ } catch {}
3315
+ }
3308
3316
  var AGENT_ID_FILE, cachedAutoName = null;
3309
3317
  var init_identity = __esm(() => {
3310
3318
  init_names();
@@ -3344,16 +3352,22 @@ function isActiveSession(lastSeenAt) {
3344
3352
  }
3345
3353
  function registerAgent(name, sessionId, role) {
3346
3354
  const db2 = getDb();
3347
- const existing = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(name);
3355
+ const normalizedName = name.trim().toLowerCase();
3356
+ const existing = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(normalizedName);
3348
3357
  if (existing) {
3349
3358
  const lastSeenAt = existing.last_seen_at;
3350
3359
  const existingSessionId = existing.session_id;
3351
3360
  if (isActiveSession(lastSeenAt) && existingSessionId && existingSessionId !== sessionId) {
3352
3361
  return {
3362
+ conflict: true,
3353
3363
  error: "agent_conflict",
3354
- message: `Agent "${name}" is already active (last seen: ${lastSeenAt}). Wait 30 minutes or use force takeover.`,
3364
+ message: `Agent "${normalizedName}" is already active (last seen: ${lastSeenAt}). Wait 30 minutes or use force takeover.`,
3365
+ existing_id: existing.id,
3366
+ existing_name: normalizedName,
3355
3367
  existing_session_id: existingSessionId,
3356
- last_seen_at: lastSeenAt
3368
+ last_seen_at: lastSeenAt,
3369
+ session_hint: existingSessionId ? existingSessionId.slice(0, 8) : null,
3370
+ working_dir: null
3357
3371
  };
3358
3372
  }
3359
3373
  const tookOver = existingSessionId !== sessionId;
@@ -3361,8 +3375,8 @@ function registerAgent(name, sessionId, role) {
3361
3375
  UPDATE agent_presence
3362
3376
  SET session_id = ?, role = ?, last_seen_at = strftime('%Y-%m-%dT%H:%M:%f', 'now')
3363
3377
  WHERE agent = ?
3364
- `).run(sessionId, role || existing.role || "agent", name);
3365
- const updated = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(name);
3378
+ `).run(sessionId, role || existing.role || "agent", normalizedName);
3379
+ const updated = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(normalizedName);
3366
3380
  return { agent: parsePresence(updated), created: false, took_over: tookOver };
3367
3381
  }
3368
3382
  const id = crypto.randomUUID().slice(0, 8);
@@ -3370,14 +3384,15 @@ function registerAgent(name, sessionId, role) {
3370
3384
  db2.prepare(`
3371
3385
  INSERT INTO agent_presence (id, agent, session_id, role, status, last_seen_at, created_at)
3372
3386
  VALUES (?, ?, ?, ?, 'online', strftime('%Y-%m-%dT%H:%M:%f', 'now'), strftime('%Y-%m-%dT%H:%M:%f', 'now'))
3373
- `).run(id, name, sessionId, resolvedRole);
3374
- const created = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(name);
3387
+ `).run(id, normalizedName, sessionId, resolvedRole);
3388
+ const created = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(normalizedName);
3375
3389
  return { agent: parsePresence(created), created: true, took_over: false };
3376
3390
  }
3377
3391
  function heartbeat(agent, status, metadata, sessionId) {
3378
3392
  const db2 = getDb();
3379
3393
  const metadataJson = metadata ? JSON.stringify(metadata) : null;
3380
3394
  const resolvedStatus = status || "online";
3395
+ const normalizedAgent = agent.trim().toLowerCase();
3381
3396
  const existing = db2.prepare("SELECT id FROM agent_presence WHERE agent = ?").get(agent);
3382
3397
  const id = existing?.id || crypto.randomUUID().slice(0, 8);
3383
3398
  db2.prepare(`
@@ -3388,11 +3403,12 @@ function heartbeat(agent, status, metadata, sessionId) {
3388
3403
  last_seen_at = excluded.last_seen_at,
3389
3404
  session_id = COALESCE(excluded.session_id, agent_presence.session_id),
3390
3405
  metadata = excluded.metadata
3391
- `).run(id, agent, sessionId ?? null, resolvedStatus, metadataJson);
3406
+ `).run(id, normalizedAgent, sessionId ?? null, resolvedStatus, metadataJson);
3392
3407
  }
3393
3408
  function getPresence(agent) {
3394
3409
  const db2 = getDb();
3395
- const row = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(agent);
3410
+ const normalizedAgent = agent.trim().toLowerCase();
3411
+ const row = db2.prepare("SELECT * FROM agent_presence WHERE LOWER(agent) = ?").get(normalizedAgent);
3396
3412
  return row ? parsePresence(row) : null;
3397
3413
  }
3398
3414
  function listAgents(opts) {
@@ -3407,18 +3423,21 @@ function listAgents(opts) {
3407
3423
  }
3408
3424
  function removePresence(agent) {
3409
3425
  const db2 = getDb();
3410
- const result = db2.prepare("DELETE FROM agent_presence WHERE agent = ?").run(agent);
3426
+ const normalizedAgent = agent.trim().toLowerCase();
3427
+ const result = db2.prepare("DELETE FROM agent_presence WHERE LOWER(agent) = ?").run(normalizedAgent);
3411
3428
  return result.changes > 0;
3412
3429
  }
3413
3430
  function renameAgent(oldName, newName) {
3414
3431
  const db2 = getDb();
3415
- const existing = db2.prepare("SELECT agent FROM agent_presence WHERE agent = ?").get(oldName);
3432
+ const normalizedOld = oldName.trim().toLowerCase();
3433
+ const normalizedNew = newName.trim().toLowerCase();
3434
+ const existing = db2.prepare("SELECT agent FROM agent_presence WHERE LOWER(agent) = ?").get(normalizedOld);
3416
3435
  if (!existing)
3417
3436
  return false;
3418
- const conflict = db2.prepare("SELECT agent FROM agent_presence WHERE agent = ?").get(newName);
3437
+ const conflict = db2.prepare("SELECT agent FROM agent_presence WHERE LOWER(agent) = ?").get(normalizedNew);
3419
3438
  if (conflict)
3420
- throw new Error(`Agent "${newName}" already exists`);
3421
- db2.prepare("UPDATE agent_presence SET agent = ? WHERE agent = ?").run(newName, oldName);
3439
+ throw new Error(`Agent "${normalizedNew}" already exists`);
3440
+ db2.prepare("UPDATE agent_presence SET agent = ? WHERE LOWER(agent) = ?").run(normalizedNew, normalizedOld);
3422
3441
  return true;
3423
3442
  }
3424
3443
  var ONLINE_THRESHOLD_SECONDS = 60, CONFLICT_THRESHOLD_SECONDS;
@@ -3571,7 +3590,7 @@ var init_poll = __esm(() => {
3571
3590
  var require_package = __commonJS((exports, module) => {
3572
3591
  module.exports = {
3573
3592
  name: "@hasna/conversations",
3574
- version: "0.1.26",
3593
+ version: "0.1.27",
3575
3594
  description: "Real-time CLI messaging for AI agents",
3576
3595
  type: "module",
3577
3596
  bin: {
@@ -32548,7 +32567,7 @@ var init_mcp2 = __esm(() => {
32548
32567
  content: exports_external.string(),
32549
32568
  from: exports_external.string().optional(),
32550
32569
  priority: exports_external.string().optional(),
32551
- blocking: exports_external.boolean().optional()
32570
+ blocking: exports_external.coerce.boolean().optional()
32552
32571
  }
32553
32572
  }, async (args) => {
32554
32573
  const { from: fromParam, to, content, priority, blocking } = args;
@@ -32572,8 +32591,8 @@ var init_mcp2 = __esm(() => {
32572
32591
  to: exports_external.string().optional(),
32573
32592
  space: exports_external.string().optional(),
32574
32593
  since: exports_external.string().optional(),
32575
- limit: exports_external.number().optional(),
32576
- unread_only: exports_external.boolean().optional()
32594
+ limit: exports_external.coerce.number().optional(),
32595
+ unread_only: exports_external.coerce.boolean().optional()
32577
32596
  }
32578
32597
  }, async (args) => {
32579
32598
  const messages = readMessages(args);
@@ -32596,7 +32615,7 @@ var init_mcp2 = __esm(() => {
32596
32615
  server.registerTool("reply", {
32597
32616
  description: "Reply to a message by ID.",
32598
32617
  inputSchema: {
32599
- message_id: exports_external.number(),
32618
+ message_id: exports_external.coerce.number(),
32600
32619
  content: exports_external.string(),
32601
32620
  from: exports_external.string().optional()
32602
32621
  }
@@ -32627,8 +32646,8 @@ var init_mcp2 = __esm(() => {
32627
32646
  description: "Mark messages read by IDs or all.",
32628
32647
  inputSchema: {
32629
32648
  from: exports_external.string().optional(),
32630
- ids: exports_external.array(exports_external.number()).optional(),
32631
- all: exports_external.boolean().optional()
32649
+ ids: exports_external.array(exports_external.coerce.number()).optional(),
32650
+ all: exports_external.coerce.boolean().optional()
32632
32651
  }
32633
32652
  }, async (args) => {
32634
32653
  const { from: fromParam, ids, all } = args;
@@ -32655,7 +32674,7 @@ var init_mcp2 = __esm(() => {
32655
32674
  space: exports_external.string().optional(),
32656
32675
  from: exports_external.string().optional(),
32657
32676
  to: exports_external.string().optional(),
32658
- limit: exports_external.number().optional()
32677
+ limit: exports_external.coerce.number().optional()
32659
32678
  }
32660
32679
  }, async (args) => {
32661
32680
  const { query, space, from, to, limit } = args;
@@ -32716,7 +32735,7 @@ var init_mcp2 = __esm(() => {
32716
32735
  inputSchema: {
32717
32736
  project_id: exports_external.string().optional(),
32718
32737
  parent_id: exports_external.string().optional(),
32719
- include_archived: exports_external.boolean().optional()
32738
+ include_archived: exports_external.coerce.boolean().optional()
32720
32739
  }
32721
32740
  }, async (args) => {
32722
32741
  const { project_id, parent_id, include_archived } = args;
@@ -32742,7 +32761,7 @@ var init_mcp2 = __esm(() => {
32742
32761
  content: exports_external.string(),
32743
32762
  from: exports_external.string().optional(),
32744
32763
  priority: exports_external.string().optional(),
32745
- blocking: exports_external.boolean().optional()
32764
+ blocking: exports_external.coerce.boolean().optional()
32746
32765
  }
32747
32766
  }, async (args) => {
32748
32767
  const { from: fromParam, space, content, priority, blocking } = args;
@@ -32772,7 +32791,7 @@ var init_mcp2 = __esm(() => {
32772
32791
  inputSchema: {
32773
32792
  space: exports_external.string(),
32774
32793
  since: exports_external.string().optional(),
32775
- limit: exports_external.number().optional()
32794
+ limit: exports_external.coerce.number().optional()
32776
32795
  }
32777
32796
  }, async (args) => {
32778
32797
  const { space, since, limit } = args;
@@ -33082,7 +33101,7 @@ var init_mcp2 = __esm(() => {
33082
33101
  server.registerTool("delete_message", {
33083
33102
  description: "Delete a message (sender only).",
33084
33103
  inputSchema: {
33085
- id: exports_external.number(),
33104
+ id: exports_external.coerce.number(),
33086
33105
  from: exports_external.string().optional()
33087
33106
  }
33088
33107
  }, async (args) => {
@@ -33102,7 +33121,7 @@ var init_mcp2 = __esm(() => {
33102
33121
  server.registerTool("edit_message", {
33103
33122
  description: "Edit message content (sender only).",
33104
33123
  inputSchema: {
33105
- id: exports_external.number(),
33124
+ id: exports_external.coerce.number(),
33106
33125
  content: exports_external.string(),
33107
33126
  from: exports_external.string().optional()
33108
33127
  }
@@ -33123,7 +33142,7 @@ var init_mcp2 = __esm(() => {
33123
33142
  server.registerTool("pin_message", {
33124
33143
  description: "Pin a message.",
33125
33144
  inputSchema: {
33126
- id: exports_external.number()
33145
+ id: exports_external.coerce.number()
33127
33146
  }
33128
33147
  }, async ({ id }) => {
33129
33148
  const msg = pinMessage(id);
@@ -33140,7 +33159,7 @@ var init_mcp2 = __esm(() => {
33140
33159
  server.registerTool("unpin_message", {
33141
33160
  description: "Unpin a message.",
33142
33161
  inputSchema: {
33143
- id: exports_external.number()
33162
+ id: exports_external.coerce.number()
33144
33163
  }
33145
33164
  }, async ({ id }) => {
33146
33165
  const msg = unpinMessage(id);
@@ -33159,7 +33178,7 @@ var init_mcp2 = __esm(() => {
33159
33178
  inputSchema: {
33160
33179
  space: exports_external.string().optional(),
33161
33180
  session_id: exports_external.string().optional(),
33162
- limit: exports_external.number().optional()
33181
+ limit: exports_external.coerce.number().optional()
33163
33182
  }
33164
33183
  }, async (args) => {
33165
33184
  const { space, session_id, limit } = args;
@@ -33199,7 +33218,7 @@ var init_mcp2 = __esm(() => {
33199
33218
  server.registerTool("list_agents", {
33200
33219
  description: "List agents with presence status.",
33201
33220
  inputSchema: {
33202
- online_only: exports_external.boolean().optional()
33221
+ online_only: exports_external.coerce.boolean().optional()
33203
33222
  }
33204
33223
  }, async (args) => {
33205
33224
  const { online_only } = args;
@@ -33266,6 +33285,9 @@ var init_mcp2 = __esm(() => {
33266
33285
  isError: true
33267
33286
  };
33268
33287
  }
33288
+ if (!fromParam) {
33289
+ updateCachedAutoName(newName);
33290
+ }
33269
33291
  return {
33270
33292
  content: [{ type: "text", text: JSON.stringify({ old_name: oldName, new_name: newName, renamed: true }) }]
33271
33293
  };
package/bin/mcp.js CHANGED
@@ -29733,6 +29733,14 @@ function resolveIdentity(explicit) {
29733
29733
  return envValue;
29734
29734
  return getAutoName();
29735
29735
  }
29736
+ function updateCachedAutoName(newName) {
29737
+ cachedAutoName = newName;
29738
+ try {
29739
+ mkdirSync3(dirname2(AGENT_ID_FILE), { recursive: true });
29740
+ writeFileSync(AGENT_ID_FILE, newName + `
29741
+ `, "utf-8");
29742
+ } catch {}
29743
+ }
29736
29744
 
29737
29745
  // src/lib/presence.ts
29738
29746
  init_db();
@@ -29770,16 +29778,22 @@ function isActiveSession(lastSeenAt) {
29770
29778
  }
29771
29779
  function registerAgent(name, sessionId, role) {
29772
29780
  const db2 = getDb();
29773
- const existing = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(name);
29781
+ const normalizedName = name.trim().toLowerCase();
29782
+ const existing = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(normalizedName);
29774
29783
  if (existing) {
29775
29784
  const lastSeenAt = existing.last_seen_at;
29776
29785
  const existingSessionId = existing.session_id;
29777
29786
  if (isActiveSession(lastSeenAt) && existingSessionId && existingSessionId !== sessionId) {
29778
29787
  return {
29788
+ conflict: true,
29779
29789
  error: "agent_conflict",
29780
- message: `Agent "${name}" is already active (last seen: ${lastSeenAt}). Wait 30 minutes or use force takeover.`,
29790
+ message: `Agent "${normalizedName}" is already active (last seen: ${lastSeenAt}). Wait 30 minutes or use force takeover.`,
29791
+ existing_id: existing.id,
29792
+ existing_name: normalizedName,
29781
29793
  existing_session_id: existingSessionId,
29782
- last_seen_at: lastSeenAt
29794
+ last_seen_at: lastSeenAt,
29795
+ session_hint: existingSessionId ? existingSessionId.slice(0, 8) : null,
29796
+ working_dir: null
29783
29797
  };
29784
29798
  }
29785
29799
  const tookOver = existingSessionId !== sessionId;
@@ -29787,8 +29801,8 @@ function registerAgent(name, sessionId, role) {
29787
29801
  UPDATE agent_presence
29788
29802
  SET session_id = ?, role = ?, last_seen_at = strftime('%Y-%m-%dT%H:%M:%f', 'now')
29789
29803
  WHERE agent = ?
29790
- `).run(sessionId, role || existing.role || "agent", name);
29791
- const updated = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(name);
29804
+ `).run(sessionId, role || existing.role || "agent", normalizedName);
29805
+ const updated = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(normalizedName);
29792
29806
  return { agent: parsePresence(updated), created: false, took_over: tookOver };
29793
29807
  }
29794
29808
  const id = crypto.randomUUID().slice(0, 8);
@@ -29796,14 +29810,15 @@ function registerAgent(name, sessionId, role) {
29796
29810
  db2.prepare(`
29797
29811
  INSERT INTO agent_presence (id, agent, session_id, role, status, last_seen_at, created_at)
29798
29812
  VALUES (?, ?, ?, ?, 'online', strftime('%Y-%m-%dT%H:%M:%f', 'now'), strftime('%Y-%m-%dT%H:%M:%f', 'now'))
29799
- `).run(id, name, sessionId, resolvedRole);
29800
- const created = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(name);
29813
+ `).run(id, normalizedName, sessionId, resolvedRole);
29814
+ const created = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(normalizedName);
29801
29815
  return { agent: parsePresence(created), created: true, took_over: false };
29802
29816
  }
29803
29817
  function heartbeat(agent, status, metadata, sessionId) {
29804
29818
  const db2 = getDb();
29805
29819
  const metadataJson = metadata ? JSON.stringify(metadata) : null;
29806
29820
  const resolvedStatus = status || "online";
29821
+ const normalizedAgent = agent.trim().toLowerCase();
29807
29822
  const existing = db2.prepare("SELECT id FROM agent_presence WHERE agent = ?").get(agent);
29808
29823
  const id = existing?.id || crypto.randomUUID().slice(0, 8);
29809
29824
  db2.prepare(`
@@ -29814,7 +29829,7 @@ function heartbeat(agent, status, metadata, sessionId) {
29814
29829
  last_seen_at = excluded.last_seen_at,
29815
29830
  session_id = COALESCE(excluded.session_id, agent_presence.session_id),
29816
29831
  metadata = excluded.metadata
29817
- `).run(id, agent, sessionId ?? null, resolvedStatus, metadataJson);
29832
+ `).run(id, normalizedAgent, sessionId ?? null, resolvedStatus, metadataJson);
29818
29833
  }
29819
29834
  function listAgents(opts) {
29820
29835
  const db2 = getDb();
@@ -29828,24 +29843,27 @@ function listAgents(opts) {
29828
29843
  }
29829
29844
  function removePresence(agent) {
29830
29845
  const db2 = getDb();
29831
- const result = db2.prepare("DELETE FROM agent_presence WHERE agent = ?").run(agent);
29846
+ const normalizedAgent = agent.trim().toLowerCase();
29847
+ const result = db2.prepare("DELETE FROM agent_presence WHERE LOWER(agent) = ?").run(normalizedAgent);
29832
29848
  return result.changes > 0;
29833
29849
  }
29834
29850
  function renameAgent(oldName, newName) {
29835
29851
  const db2 = getDb();
29836
- const existing = db2.prepare("SELECT agent FROM agent_presence WHERE agent = ?").get(oldName);
29852
+ const normalizedOld = oldName.trim().toLowerCase();
29853
+ const normalizedNew = newName.trim().toLowerCase();
29854
+ const existing = db2.prepare("SELECT agent FROM agent_presence WHERE LOWER(agent) = ?").get(normalizedOld);
29837
29855
  if (!existing)
29838
29856
  return false;
29839
- const conflict = db2.prepare("SELECT agent FROM agent_presence WHERE agent = ?").get(newName);
29857
+ const conflict = db2.prepare("SELECT agent FROM agent_presence WHERE LOWER(agent) = ?").get(normalizedNew);
29840
29858
  if (conflict)
29841
- throw new Error(`Agent "${newName}" already exists`);
29842
- db2.prepare("UPDATE agent_presence SET agent = ? WHERE agent = ?").run(newName, oldName);
29859
+ throw new Error(`Agent "${normalizedNew}" already exists`);
29860
+ db2.prepare("UPDATE agent_presence SET agent = ? WHERE LOWER(agent) = ?").run(normalizedNew, normalizedOld);
29843
29861
  return true;
29844
29862
  }
29845
29863
  // package.json
29846
29864
  var package_default = {
29847
29865
  name: "@hasna/conversations",
29848
- version: "0.1.26",
29866
+ version: "0.1.27",
29849
29867
  description: "Real-time CLI messaging for AI agents",
29850
29868
  type: "module",
29851
29869
  bin: {
@@ -29934,7 +29952,7 @@ server.registerTool("send_message", {
29934
29952
  content: exports_external.string(),
29935
29953
  from: exports_external.string().optional(),
29936
29954
  priority: exports_external.string().optional(),
29937
- blocking: exports_external.boolean().optional()
29955
+ blocking: exports_external.coerce.boolean().optional()
29938
29956
  }
29939
29957
  }, async (args) => {
29940
29958
  const { from: fromParam, to, content, priority, blocking } = args;
@@ -29958,8 +29976,8 @@ server.registerTool("read_messages", {
29958
29976
  to: exports_external.string().optional(),
29959
29977
  space: exports_external.string().optional(),
29960
29978
  since: exports_external.string().optional(),
29961
- limit: exports_external.number().optional(),
29962
- unread_only: exports_external.boolean().optional()
29979
+ limit: exports_external.coerce.number().optional(),
29980
+ unread_only: exports_external.coerce.boolean().optional()
29963
29981
  }
29964
29982
  }, async (args) => {
29965
29983
  const messages = readMessages(args);
@@ -29982,7 +30000,7 @@ server.registerTool("list_sessions", {
29982
30000
  server.registerTool("reply", {
29983
30001
  description: "Reply to a message by ID.",
29984
30002
  inputSchema: {
29985
- message_id: exports_external.number(),
30003
+ message_id: exports_external.coerce.number(),
29986
30004
  content: exports_external.string(),
29987
30005
  from: exports_external.string().optional()
29988
30006
  }
@@ -30013,8 +30031,8 @@ server.registerTool("mark_read", {
30013
30031
  description: "Mark messages read by IDs or all.",
30014
30032
  inputSchema: {
30015
30033
  from: exports_external.string().optional(),
30016
- ids: exports_external.array(exports_external.number()).optional(),
30017
- all: exports_external.boolean().optional()
30034
+ ids: exports_external.array(exports_external.coerce.number()).optional(),
30035
+ all: exports_external.coerce.boolean().optional()
30018
30036
  }
30019
30037
  }, async (args) => {
30020
30038
  const { from: fromParam, ids, all } = args;
@@ -30041,7 +30059,7 @@ server.registerTool("search_messages", {
30041
30059
  space: exports_external.string().optional(),
30042
30060
  from: exports_external.string().optional(),
30043
30061
  to: exports_external.string().optional(),
30044
- limit: exports_external.number().optional()
30062
+ limit: exports_external.coerce.number().optional()
30045
30063
  }
30046
30064
  }, async (args) => {
30047
30065
  const { query, space, from, to, limit } = args;
@@ -30102,7 +30120,7 @@ server.registerTool("list_spaces", {
30102
30120
  inputSchema: {
30103
30121
  project_id: exports_external.string().optional(),
30104
30122
  parent_id: exports_external.string().optional(),
30105
- include_archived: exports_external.boolean().optional()
30123
+ include_archived: exports_external.coerce.boolean().optional()
30106
30124
  }
30107
30125
  }, async (args) => {
30108
30126
  const { project_id, parent_id, include_archived } = args;
@@ -30128,7 +30146,7 @@ server.registerTool("send_to_space", {
30128
30146
  content: exports_external.string(),
30129
30147
  from: exports_external.string().optional(),
30130
30148
  priority: exports_external.string().optional(),
30131
- blocking: exports_external.boolean().optional()
30149
+ blocking: exports_external.coerce.boolean().optional()
30132
30150
  }
30133
30151
  }, async (args) => {
30134
30152
  const { from: fromParam, space, content, priority, blocking } = args;
@@ -30158,7 +30176,7 @@ server.registerTool("read_space", {
30158
30176
  inputSchema: {
30159
30177
  space: exports_external.string(),
30160
30178
  since: exports_external.string().optional(),
30161
- limit: exports_external.number().optional()
30179
+ limit: exports_external.coerce.number().optional()
30162
30180
  }
30163
30181
  }, async (args) => {
30164
30182
  const { space, since, limit } = args;
@@ -30468,7 +30486,7 @@ server.registerTool("delete_project", {
30468
30486
  server.registerTool("delete_message", {
30469
30487
  description: "Delete a message (sender only).",
30470
30488
  inputSchema: {
30471
- id: exports_external.number(),
30489
+ id: exports_external.coerce.number(),
30472
30490
  from: exports_external.string().optional()
30473
30491
  }
30474
30492
  }, async (args) => {
@@ -30488,7 +30506,7 @@ server.registerTool("delete_message", {
30488
30506
  server.registerTool("edit_message", {
30489
30507
  description: "Edit message content (sender only).",
30490
30508
  inputSchema: {
30491
- id: exports_external.number(),
30509
+ id: exports_external.coerce.number(),
30492
30510
  content: exports_external.string(),
30493
30511
  from: exports_external.string().optional()
30494
30512
  }
@@ -30509,7 +30527,7 @@ server.registerTool("edit_message", {
30509
30527
  server.registerTool("pin_message", {
30510
30528
  description: "Pin a message.",
30511
30529
  inputSchema: {
30512
- id: exports_external.number()
30530
+ id: exports_external.coerce.number()
30513
30531
  }
30514
30532
  }, async ({ id }) => {
30515
30533
  const msg = pinMessage(id);
@@ -30526,7 +30544,7 @@ server.registerTool("pin_message", {
30526
30544
  server.registerTool("unpin_message", {
30527
30545
  description: "Unpin a message.",
30528
30546
  inputSchema: {
30529
- id: exports_external.number()
30547
+ id: exports_external.coerce.number()
30530
30548
  }
30531
30549
  }, async ({ id }) => {
30532
30550
  const msg = unpinMessage(id);
@@ -30545,7 +30563,7 @@ server.registerTool("get_pinned_messages", {
30545
30563
  inputSchema: {
30546
30564
  space: exports_external.string().optional(),
30547
30565
  session_id: exports_external.string().optional(),
30548
- limit: exports_external.number().optional()
30566
+ limit: exports_external.coerce.number().optional()
30549
30567
  }
30550
30568
  }, async (args) => {
30551
30569
  const { space, session_id, limit } = args;
@@ -30585,7 +30603,7 @@ server.registerTool("heartbeat", {
30585
30603
  server.registerTool("list_agents", {
30586
30604
  description: "List agents with presence status.",
30587
30605
  inputSchema: {
30588
- online_only: exports_external.boolean().optional()
30606
+ online_only: exports_external.coerce.boolean().optional()
30589
30607
  }
30590
30608
  }, async (args) => {
30591
30609
  const { online_only } = args;
@@ -30652,6 +30670,9 @@ server.registerTool("rename_agent", {
30652
30670
  isError: true
30653
30671
  };
30654
30672
  }
30673
+ if (!fromParam) {
30674
+ updateCachedAutoName(newName);
30675
+ }
30655
30676
  return {
30656
30677
  content: [{ type: "text", text: JSON.stringify({ old_name: oldName, new_name: newName, renamed: true }) }]
30657
30678
  };
package/dist/index.d.ts CHANGED
@@ -18,5 +18,5 @@ export { startPolling, useSpaceMessages, } from "./lib/poll.js";
18
18
  export { resolveIdentity, requireIdentity, } from "./lib/identity.js";
19
19
  export { addReaction, removeReaction, getReactions, getReactionSummary, } from "./lib/reactions.js";
20
20
  export { fireWebhooks, } from "./lib/webhooks.js";
21
- export { heartbeat, registerAgent, getPresence, listAgents, removePresence, renameAgent, } from "./lib/presence.js";
21
+ export { heartbeat, registerAgent, isAgentConflict, getPresence, listAgents, removePresence, renameAgent, } from "./lib/presence.js";
22
22
  export type { Message, Session, Space, SpaceInfo, SpaceMember, Project, ProjectInfo, Priority, SendMessageOptions, ReadMessagesOptions, SearchMessagesOptions, AgentPresence, AgentConflictError, RegisterAgentResult, Reaction, Attachment, } from "./types.js";
package/dist/index.js CHANGED
@@ -3455,18 +3455,27 @@ function isActiveSession(lastSeenAt) {
3455
3455
  const nowMs = Date.now();
3456
3456
  return nowMs - lastSeenMs < CONFLICT_THRESHOLD_SECONDS * 1000;
3457
3457
  }
3458
+ function isAgentConflict(result) {
3459
+ return result.conflict === true;
3460
+ }
3458
3461
  function registerAgent(name, sessionId, role) {
3459
3462
  const db2 = getDb();
3460
- const existing = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(name);
3463
+ const normalizedName = name.trim().toLowerCase();
3464
+ const existing = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(normalizedName);
3461
3465
  if (existing) {
3462
3466
  const lastSeenAt = existing.last_seen_at;
3463
3467
  const existingSessionId = existing.session_id;
3464
3468
  if (isActiveSession(lastSeenAt) && existingSessionId && existingSessionId !== sessionId) {
3465
3469
  return {
3470
+ conflict: true,
3466
3471
  error: "agent_conflict",
3467
- message: `Agent "${name}" is already active (last seen: ${lastSeenAt}). Wait 30 minutes or use force takeover.`,
3472
+ message: `Agent "${normalizedName}" is already active (last seen: ${lastSeenAt}). Wait 30 minutes or use force takeover.`,
3473
+ existing_id: existing.id,
3474
+ existing_name: normalizedName,
3468
3475
  existing_session_id: existingSessionId,
3469
- last_seen_at: lastSeenAt
3476
+ last_seen_at: lastSeenAt,
3477
+ session_hint: existingSessionId ? existingSessionId.slice(0, 8) : null,
3478
+ working_dir: null
3470
3479
  };
3471
3480
  }
3472
3481
  const tookOver = existingSessionId !== sessionId;
@@ -3474,8 +3483,8 @@ function registerAgent(name, sessionId, role) {
3474
3483
  UPDATE agent_presence
3475
3484
  SET session_id = ?, role = ?, last_seen_at = strftime('%Y-%m-%dT%H:%M:%f', 'now')
3476
3485
  WHERE agent = ?
3477
- `).run(sessionId, role || existing.role || "agent", name);
3478
- const updated = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(name);
3486
+ `).run(sessionId, role || existing.role || "agent", normalizedName);
3487
+ const updated = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(normalizedName);
3479
3488
  return { agent: parsePresence(updated), created: false, took_over: tookOver };
3480
3489
  }
3481
3490
  const id = crypto.randomUUID().slice(0, 8);
@@ -3483,14 +3492,15 @@ function registerAgent(name, sessionId, role) {
3483
3492
  db2.prepare(`
3484
3493
  INSERT INTO agent_presence (id, agent, session_id, role, status, last_seen_at, created_at)
3485
3494
  VALUES (?, ?, ?, ?, 'online', strftime('%Y-%m-%dT%H:%M:%f', 'now'), strftime('%Y-%m-%dT%H:%M:%f', 'now'))
3486
- `).run(id, name, sessionId, resolvedRole);
3487
- const created = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(name);
3495
+ `).run(id, normalizedName, sessionId, resolvedRole);
3496
+ const created = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(normalizedName);
3488
3497
  return { agent: parsePresence(created), created: true, took_over: false };
3489
3498
  }
3490
3499
  function heartbeat(agent, status, metadata, sessionId) {
3491
3500
  const db2 = getDb();
3492
3501
  const metadataJson = metadata ? JSON.stringify(metadata) : null;
3493
3502
  const resolvedStatus = status || "online";
3503
+ const normalizedAgent = agent.trim().toLowerCase();
3494
3504
  const existing = db2.prepare("SELECT id FROM agent_presence WHERE agent = ?").get(agent);
3495
3505
  const id = existing?.id || crypto.randomUUID().slice(0, 8);
3496
3506
  db2.prepare(`
@@ -3501,11 +3511,12 @@ function heartbeat(agent, status, metadata, sessionId) {
3501
3511
  last_seen_at = excluded.last_seen_at,
3502
3512
  session_id = COALESCE(excluded.session_id, agent_presence.session_id),
3503
3513
  metadata = excluded.metadata
3504
- `).run(id, agent, sessionId ?? null, resolvedStatus, metadataJson);
3514
+ `).run(id, normalizedAgent, sessionId ?? null, resolvedStatus, metadataJson);
3505
3515
  }
3506
3516
  function getPresence(agent) {
3507
3517
  const db2 = getDb();
3508
- const row = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(agent);
3518
+ const normalizedAgent = agent.trim().toLowerCase();
3519
+ const row = db2.prepare("SELECT * FROM agent_presence WHERE LOWER(agent) = ?").get(normalizedAgent);
3509
3520
  return row ? parsePresence(row) : null;
3510
3521
  }
3511
3522
  function listAgents(opts) {
@@ -3520,18 +3531,21 @@ function listAgents(opts) {
3520
3531
  }
3521
3532
  function removePresence(agent) {
3522
3533
  const db2 = getDb();
3523
- const result = db2.prepare("DELETE FROM agent_presence WHERE agent = ?").run(agent);
3534
+ const normalizedAgent = agent.trim().toLowerCase();
3535
+ const result = db2.prepare("DELETE FROM agent_presence WHERE LOWER(agent) = ?").run(normalizedAgent);
3524
3536
  return result.changes > 0;
3525
3537
  }
3526
3538
  function renameAgent(oldName, newName) {
3527
3539
  const db2 = getDb();
3528
- const existing = db2.prepare("SELECT agent FROM agent_presence WHERE agent = ?").get(oldName);
3540
+ const normalizedOld = oldName.trim().toLowerCase();
3541
+ const normalizedNew = newName.trim().toLowerCase();
3542
+ const existing = db2.prepare("SELECT agent FROM agent_presence WHERE LOWER(agent) = ?").get(normalizedOld);
3529
3543
  if (!existing)
3530
3544
  return false;
3531
- const conflict = db2.prepare("SELECT agent FROM agent_presence WHERE agent = ?").get(newName);
3545
+ const conflict = db2.prepare("SELECT agent FROM agent_presence WHERE LOWER(agent) = ?").get(normalizedNew);
3532
3546
  if (conflict)
3533
- throw new Error(`Agent "${newName}" already exists`);
3534
- db2.prepare("UPDATE agent_presence SET agent = ? WHERE agent = ?").run(newName, oldName);
3547
+ throw new Error(`Agent "${normalizedNew}" already exists`);
3548
+ db2.prepare("UPDATE agent_presence SET agent = ? WHERE LOWER(agent) = ?").run(normalizedNew, normalizedOld);
3535
3549
  return true;
3536
3550
  }
3537
3551
  export {
@@ -3562,6 +3576,7 @@ export {
3562
3576
  leaveSpace,
3563
3577
  joinSpace,
3564
3578
  isSpaceMember,
3579
+ isAgentConflict,
3565
3580
  heartbeat,
3566
3581
  getUnreadBlockers,
3567
3582
  getThreadReplies,
@@ -14,6 +14,10 @@ export declare function resolveIdentity(explicit?: string): string;
14
14
  * Throws if no identity is set via flag or env.
15
15
  */
16
16
  export declare function requireIdentity(explicit?: string): string;
17
+ /**
18
+ * Update the cached auto name after a successful rename.
19
+ */
20
+ export declare function updateCachedAutoName(newName: string): void;
17
21
  /**
18
22
  * Reset the cached auto name (for testing).
19
23
  */
@@ -1,4 +1,5 @@
1
1
  import type { AgentPresence, AgentConflictError, RegisterAgentResult } from "../types.js";
2
+ export declare function isAgentConflict(result: RegisterAgentResult | AgentConflictError): result is AgentConflictError;
2
3
  export declare function registerAgent(name: string, sessionId: string, role?: string): RegisterAgentResult | AgentConflictError;
3
4
  export declare function heartbeat(agent: string, status?: string, metadata?: Record<string, unknown>, sessionId?: string): void;
4
5
  export declare function getPresence(agent: string): AgentPresence | null;
package/dist/types.d.ts CHANGED
@@ -123,10 +123,15 @@ export interface AgentPresence {
123
123
  metadata: Record<string, unknown> | null;
124
124
  }
125
125
  export interface AgentConflictError {
126
+ conflict: true;
126
127
  error: "agent_conflict";
127
128
  message: string;
129
+ existing_id: string;
130
+ existing_name: string;
128
131
  existing_session_id: string | null;
129
132
  last_seen_at: string;
133
+ session_hint: string | null;
134
+ working_dir: string | null;
130
135
  }
131
136
  export interface RegisterAgentResult {
132
137
  agent: AgentPresence;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/conversations",
3
- "version": "0.1.26",
3
+ "version": "0.1.27",
4
4
  "description": "Real-time CLI messaging for AI agents",
5
5
  "type": "module",
6
6
  "bin": {