@hasna/conversations 0.1.27 → 0.1.29

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
@@ -118,6 +118,7 @@ function getDb() {
118
118
  agent TEXT PRIMARY KEY,
119
119
  session_id TEXT,
120
120
  role TEXT NOT NULL DEFAULT 'agent',
121
+ project_id TEXT,
121
122
  status TEXT NOT NULL DEFAULT 'online',
122
123
  last_seen_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
123
124
  created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
@@ -216,7 +217,11 @@ function getDb() {
216
217
  db.exec("ALTER TABLE agent_presence ADD COLUMN role TEXT NOT NULL DEFAULT 'agent'");
217
218
  }
218
219
  if (!presenceColNames.includes("created_at")) {
219
- db.exec("ALTER TABLE agent_presence ADD COLUMN created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now'))");
220
+ db.exec("ALTER TABLE agent_presence ADD COLUMN created_at TEXT NOT NULL DEFAULT ''");
221
+ db.exec("UPDATE agent_presence SET created_at = last_seen_at WHERE created_at = ''");
222
+ }
223
+ if (!presenceColNames.includes("project_id")) {
224
+ db.exec("ALTER TABLE agent_presence ADD COLUMN project_id TEXT");
220
225
  }
221
226
  const ftsExists = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='messages_fts'").get();
222
227
  if (!ftsExists) {
package/bin/index.js CHANGED
@@ -1972,6 +1972,7 @@ function getDb() {
1972
1972
  agent TEXT PRIMARY KEY,
1973
1973
  session_id TEXT,
1974
1974
  role TEXT NOT NULL DEFAULT 'agent',
1975
+ project_id TEXT,
1975
1976
  status TEXT NOT NULL DEFAULT 'online',
1976
1977
  last_seen_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
1977
1978
  created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
@@ -2070,7 +2071,11 @@ function getDb() {
2070
2071
  db.exec("ALTER TABLE agent_presence ADD COLUMN role TEXT NOT NULL DEFAULT 'agent'");
2071
2072
  }
2072
2073
  if (!presenceColNames.includes("created_at")) {
2073
- db.exec("ALTER TABLE agent_presence ADD COLUMN created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now'))");
2074
+ db.exec("ALTER TABLE agent_presence ADD COLUMN created_at TEXT NOT NULL DEFAULT ''");
2075
+ db.exec("UPDATE agent_presence SET created_at = last_seen_at WHERE created_at = ''");
2076
+ }
2077
+ if (!presenceColNames.includes("project_id")) {
2078
+ db.exec("ALTER TABLE agent_presence ADD COLUMN project_id TEXT");
2074
2079
  }
2075
2080
  const ftsExists = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='messages_fts'").get();
2076
2081
  if (!ftsExists) {
@@ -3338,6 +3343,7 @@ function parsePresence(row) {
3338
3343
  agent: row.agent,
3339
3344
  session_id: row.session_id ?? null,
3340
3345
  role: row.role || "agent",
3346
+ project_id: row.project_id ?? null,
3341
3347
  status: row.status,
3342
3348
  last_seen_at: lastSeenAt,
3343
3349
  created_at: row.created_at || lastSeenAt,
@@ -3350,43 +3356,46 @@ function isActiveSession(lastSeenAt) {
3350
3356
  const nowMs = Date.now();
3351
3357
  return nowMs - lastSeenMs < CONFLICT_THRESHOLD_SECONDS * 1000;
3352
3358
  }
3353
- function registerAgent(name, sessionId, role) {
3359
+ function registerAgent(name, sessionId, role, projectId) {
3354
3360
  const db2 = getDb();
3355
3361
  const normalizedName = name.trim().toLowerCase();
3356
- const existing = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(normalizedName);
3357
- if (existing) {
3358
- const lastSeenAt = existing.last_seen_at;
3359
- const existingSessionId = existing.session_id;
3360
- if (isActiveSession(lastSeenAt) && existingSessionId && existingSessionId !== sessionId) {
3361
- return {
3362
- conflict: true,
3363
- error: "agent_conflict",
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,
3367
- existing_session_id: existingSessionId,
3368
- last_seen_at: lastSeenAt,
3369
- session_hint: existingSessionId ? existingSessionId.slice(0, 8) : null,
3370
- working_dir: null
3371
- };
3372
- }
3373
- const tookOver = existingSessionId !== sessionId;
3362
+ const result = db2.transaction(() => {
3363
+ const existing = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(normalizedName);
3364
+ if (existing) {
3365
+ const lastSeenAt = existing.last_seen_at;
3366
+ const existingSessionId = existing.session_id;
3367
+ if (isActiveSession(lastSeenAt) && existingSessionId && existingSessionId !== sessionId) {
3368
+ return {
3369
+ conflict: true,
3370
+ error: "agent_conflict",
3371
+ message: `Agent "${normalizedName}" is already active (last seen: ${lastSeenAt}). Wait 30 minutes or use force takeover.`,
3372
+ existing_id: existing.id,
3373
+ existing_name: normalizedName,
3374
+ existing_session_id: existingSessionId,
3375
+ last_seen_at: lastSeenAt,
3376
+ session_hint: existingSessionId ? existingSessionId.slice(0, 8) : null,
3377
+ working_dir: null
3378
+ };
3379
+ }
3380
+ const tookOver = existingSessionId !== sessionId;
3381
+ db2.prepare(`
3382
+ UPDATE agent_presence
3383
+ SET session_id = ?, role = ?, project_id = ?, last_seen_at = strftime('%Y-%m-%dT%H:%M:%f', 'now')
3384
+ WHERE agent = ?
3385
+ `).run(sessionId, role || existing.role || "agent", projectId ?? existing.project_id ?? null, normalizedName);
3386
+ const updated = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(normalizedName);
3387
+ return { agent: parsePresence(updated), created: false, took_over: tookOver };
3388
+ }
3389
+ const id = crypto.randomUUID().slice(0, 8);
3390
+ const resolvedRole = role || "agent";
3374
3391
  db2.prepare(`
3375
- UPDATE agent_presence
3376
- SET session_id = ?, role = ?, last_seen_at = strftime('%Y-%m-%dT%H:%M:%f', 'now')
3377
- WHERE agent = ?
3378
- `).run(sessionId, role || existing.role || "agent", normalizedName);
3379
- const updated = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(normalizedName);
3380
- return { agent: parsePresence(updated), created: false, took_over: tookOver };
3381
- }
3382
- const id = crypto.randomUUID().slice(0, 8);
3383
- const resolvedRole = role || "agent";
3384
- db2.prepare(`
3385
- INSERT INTO agent_presence (id, agent, session_id, role, status, last_seen_at, created_at)
3386
- VALUES (?, ?, ?, ?, 'online', strftime('%Y-%m-%dT%H:%M:%f', 'now'), strftime('%Y-%m-%dT%H:%M:%f', 'now'))
3387
- `).run(id, normalizedName, sessionId, resolvedRole);
3388
- const created = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(normalizedName);
3389
- return { agent: parsePresence(created), created: true, took_over: false };
3392
+ INSERT INTO agent_presence (id, agent, session_id, role, project_id, status, last_seen_at, created_at)
3393
+ VALUES (?, ?, ?, ?, ?, 'online', strftime('%Y-%m-%dT%H:%M:%f', 'now'), strftime('%Y-%m-%dT%H:%M:%f', 'now'))
3394
+ `).run(id, normalizedName, sessionId, resolvedRole, projectId ?? null);
3395
+ const created = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(normalizedName);
3396
+ return { agent: parsePresence(created), created: true, took_over: false };
3397
+ }).immediate();
3398
+ return result;
3390
3399
  }
3391
3400
  function heartbeat(agent, status, metadata, sessionId) {
3392
3401
  const db2 = getDb();
@@ -3590,7 +3599,7 @@ var init_poll = __esm(() => {
3590
3599
  var require_package = __commonJS((exports, module) => {
3591
3600
  module.exports = {
3592
3601
  name: "@hasna/conversations",
3593
- version: "0.1.27",
3602
+ version: "0.1.29",
3594
3603
  description: "Real-time CLI messaging for AI agents",
3595
3604
  type: "module",
3596
3605
  bin: {
@@ -33188,15 +33197,16 @@ var init_mcp2 = __esm(() => {
33188
33197
  };
33189
33198
  });
33190
33199
  server.registerTool("register_agent", {
33191
- description: "Register an agent with conflict detection. Returns AgentConflictError if another active session exists (active = heartbeat within last 30 min).",
33200
+ 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.",
33192
33201
  inputSchema: {
33193
33202
  name: exports_external.string(),
33194
33203
  session_id: exports_external.string(),
33195
- role: exports_external.string().optional()
33204
+ role: exports_external.string().optional(),
33205
+ project_id: exports_external.string().optional()
33196
33206
  }
33197
33207
  }, async (args) => {
33198
- const { name, session_id, role } = args;
33199
- const result = registerAgent(name, session_id, role);
33208
+ const { name, session_id, role, project_id } = args;
33209
+ const result = registerAgent(name, session_id, role, project_id);
33200
33210
  return {
33201
33211
  content: [{ type: "text", text: JSON.stringify(result) }]
33202
33212
  };
package/bin/mcp.js CHANGED
@@ -6604,6 +6604,7 @@ function getDb() {
6604
6604
  agent TEXT PRIMARY KEY,
6605
6605
  session_id TEXT,
6606
6606
  role TEXT NOT NULL DEFAULT 'agent',
6607
+ project_id TEXT,
6607
6608
  status TEXT NOT NULL DEFAULT 'online',
6608
6609
  last_seen_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
6609
6610
  created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
@@ -6702,7 +6703,11 @@ function getDb() {
6702
6703
  db.exec("ALTER TABLE agent_presence ADD COLUMN role TEXT NOT NULL DEFAULT 'agent'");
6703
6704
  }
6704
6705
  if (!presenceColNames.includes("created_at")) {
6705
- db.exec("ALTER TABLE agent_presence ADD COLUMN created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now'))");
6706
+ db.exec("ALTER TABLE agent_presence ADD COLUMN created_at TEXT NOT NULL DEFAULT ''");
6707
+ db.exec("UPDATE agent_presence SET created_at = last_seen_at WHERE created_at = ''");
6708
+ }
6709
+ if (!presenceColNames.includes("project_id")) {
6710
+ db.exec("ALTER TABLE agent_presence ADD COLUMN project_id TEXT");
6706
6711
  }
6707
6712
  const ftsExists = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='messages_fts'").get();
6708
6713
  if (!ftsExists) {
@@ -29764,6 +29769,7 @@ function parsePresence(row) {
29764
29769
  agent: row.agent,
29765
29770
  session_id: row.session_id ?? null,
29766
29771
  role: row.role || "agent",
29772
+ project_id: row.project_id ?? null,
29767
29773
  status: row.status,
29768
29774
  last_seen_at: lastSeenAt,
29769
29775
  created_at: row.created_at || lastSeenAt,
@@ -29776,43 +29782,46 @@ function isActiveSession(lastSeenAt) {
29776
29782
  const nowMs = Date.now();
29777
29783
  return nowMs - lastSeenMs < CONFLICT_THRESHOLD_SECONDS * 1000;
29778
29784
  }
29779
- function registerAgent(name, sessionId, role) {
29785
+ function registerAgent(name, sessionId, role, projectId) {
29780
29786
  const db2 = getDb();
29781
29787
  const normalizedName = name.trim().toLowerCase();
29782
- const existing = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(normalizedName);
29783
- if (existing) {
29784
- const lastSeenAt = existing.last_seen_at;
29785
- const existingSessionId = existing.session_id;
29786
- if (isActiveSession(lastSeenAt) && existingSessionId && existingSessionId !== sessionId) {
29787
- return {
29788
- conflict: true,
29789
- error: "agent_conflict",
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,
29793
- existing_session_id: existingSessionId,
29794
- last_seen_at: lastSeenAt,
29795
- session_hint: existingSessionId ? existingSessionId.slice(0, 8) : null,
29796
- working_dir: null
29797
- };
29798
- }
29799
- const tookOver = existingSessionId !== sessionId;
29788
+ const result = db2.transaction(() => {
29789
+ const existing = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(normalizedName);
29790
+ if (existing) {
29791
+ const lastSeenAt = existing.last_seen_at;
29792
+ const existingSessionId = existing.session_id;
29793
+ if (isActiveSession(lastSeenAt) && existingSessionId && existingSessionId !== sessionId) {
29794
+ return {
29795
+ conflict: true,
29796
+ error: "agent_conflict",
29797
+ message: `Agent "${normalizedName}" is already active (last seen: ${lastSeenAt}). Wait 30 minutes or use force takeover.`,
29798
+ existing_id: existing.id,
29799
+ existing_name: normalizedName,
29800
+ existing_session_id: existingSessionId,
29801
+ last_seen_at: lastSeenAt,
29802
+ session_hint: existingSessionId ? existingSessionId.slice(0, 8) : null,
29803
+ working_dir: null
29804
+ };
29805
+ }
29806
+ const tookOver = existingSessionId !== sessionId;
29807
+ db2.prepare(`
29808
+ UPDATE agent_presence
29809
+ SET session_id = ?, role = ?, project_id = ?, last_seen_at = strftime('%Y-%m-%dT%H:%M:%f', 'now')
29810
+ WHERE agent = ?
29811
+ `).run(sessionId, role || existing.role || "agent", projectId ?? existing.project_id ?? null, normalizedName);
29812
+ const updated = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(normalizedName);
29813
+ return { agent: parsePresence(updated), created: false, took_over: tookOver };
29814
+ }
29815
+ const id = crypto.randomUUID().slice(0, 8);
29816
+ const resolvedRole = role || "agent";
29800
29817
  db2.prepare(`
29801
- UPDATE agent_presence
29802
- SET session_id = ?, role = ?, last_seen_at = strftime('%Y-%m-%dT%H:%M:%f', 'now')
29803
- WHERE agent = ?
29804
- `).run(sessionId, role || existing.role || "agent", normalizedName);
29805
- const updated = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(normalizedName);
29806
- return { agent: parsePresence(updated), created: false, took_over: tookOver };
29807
- }
29808
- const id = crypto.randomUUID().slice(0, 8);
29809
- const resolvedRole = role || "agent";
29810
- db2.prepare(`
29811
- INSERT INTO agent_presence (id, agent, session_id, role, status, last_seen_at, created_at)
29812
- VALUES (?, ?, ?, ?, 'online', strftime('%Y-%m-%dT%H:%M:%f', 'now'), strftime('%Y-%m-%dT%H:%M:%f', 'now'))
29813
- `).run(id, normalizedName, sessionId, resolvedRole);
29814
- const created = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(normalizedName);
29815
- return { agent: parsePresence(created), created: true, took_over: false };
29818
+ INSERT INTO agent_presence (id, agent, session_id, role, project_id, status, last_seen_at, created_at)
29819
+ VALUES (?, ?, ?, ?, ?, 'online', strftime('%Y-%m-%dT%H:%M:%f', 'now'), strftime('%Y-%m-%dT%H:%M:%f', 'now'))
29820
+ `).run(id, normalizedName, sessionId, resolvedRole, projectId ?? null);
29821
+ const created = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(normalizedName);
29822
+ return { agent: parsePresence(created), created: true, took_over: false };
29823
+ }).immediate();
29824
+ return result;
29816
29825
  }
29817
29826
  function heartbeat(agent, status, metadata, sessionId) {
29818
29827
  const db2 = getDb();
@@ -29863,7 +29872,7 @@ function renameAgent(oldName, newName) {
29863
29872
  // package.json
29864
29873
  var package_default = {
29865
29874
  name: "@hasna/conversations",
29866
- version: "0.1.27",
29875
+ version: "0.1.29",
29867
29876
  description: "Real-time CLI messaging for AI agents",
29868
29877
  type: "module",
29869
29878
  bin: {
@@ -30573,15 +30582,16 @@ server.registerTool("get_pinned_messages", {
30573
30582
  };
30574
30583
  });
30575
30584
  server.registerTool("register_agent", {
30576
- description: "Register an agent with conflict detection. Returns AgentConflictError if another active session exists (active = heartbeat within last 30 min).",
30585
+ 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.",
30577
30586
  inputSchema: {
30578
30587
  name: exports_external.string(),
30579
30588
  session_id: exports_external.string(),
30580
- role: exports_external.string().optional()
30589
+ role: exports_external.string().optional(),
30590
+ project_id: exports_external.string().optional()
30581
30591
  }
30582
30592
  }, async (args) => {
30583
- const { name, session_id, role } = args;
30584
- const result = registerAgent(name, session_id, role);
30593
+ const { name, session_id, role, project_id } = args;
30594
+ const result = registerAgent(name, session_id, role, project_id);
30585
30595
  return {
30586
30596
  content: [{ type: "text", text: JSON.stringify(result) }]
30587
30597
  };
package/dist/index.js CHANGED
@@ -131,6 +131,7 @@ function getDb() {
131
131
  agent TEXT PRIMARY KEY,
132
132
  session_id TEXT,
133
133
  role TEXT NOT NULL DEFAULT 'agent',
134
+ project_id TEXT,
134
135
  status TEXT NOT NULL DEFAULT 'online',
135
136
  last_seen_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
136
137
  created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
@@ -229,7 +230,11 @@ function getDb() {
229
230
  db.exec("ALTER TABLE agent_presence ADD COLUMN role TEXT NOT NULL DEFAULT 'agent'");
230
231
  }
231
232
  if (!presenceColNames.includes("created_at")) {
232
- db.exec("ALTER TABLE agent_presence ADD COLUMN created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now'))");
233
+ db.exec("ALTER TABLE agent_presence ADD COLUMN created_at TEXT NOT NULL DEFAULT ''");
234
+ db.exec("UPDATE agent_presence SET created_at = last_seen_at WHERE created_at = ''");
235
+ }
236
+ if (!presenceColNames.includes("project_id")) {
237
+ db.exec("ALTER TABLE agent_presence ADD COLUMN project_id TEXT");
233
238
  }
234
239
  const ftsExists = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='messages_fts'").get();
235
240
  if (!ftsExists) {
@@ -3443,6 +3448,7 @@ function parsePresence(row) {
3443
3448
  agent: row.agent,
3444
3449
  session_id: row.session_id ?? null,
3445
3450
  role: row.role || "agent",
3451
+ project_id: row.project_id ?? null,
3446
3452
  status: row.status,
3447
3453
  last_seen_at: lastSeenAt,
3448
3454
  created_at: row.created_at || lastSeenAt,
@@ -3458,43 +3464,46 @@ function isActiveSession(lastSeenAt) {
3458
3464
  function isAgentConflict(result) {
3459
3465
  return result.conflict === true;
3460
3466
  }
3461
- function registerAgent(name, sessionId, role) {
3467
+ function registerAgent(name, sessionId, role, projectId) {
3462
3468
  const db2 = getDb();
3463
3469
  const normalizedName = name.trim().toLowerCase();
3464
- const existing = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(normalizedName);
3465
- if (existing) {
3466
- const lastSeenAt = existing.last_seen_at;
3467
- const existingSessionId = existing.session_id;
3468
- if (isActiveSession(lastSeenAt) && existingSessionId && existingSessionId !== sessionId) {
3469
- return {
3470
- conflict: true,
3471
- error: "agent_conflict",
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,
3475
- existing_session_id: existingSessionId,
3476
- last_seen_at: lastSeenAt,
3477
- session_hint: existingSessionId ? existingSessionId.slice(0, 8) : null,
3478
- working_dir: null
3479
- };
3470
+ const result = db2.transaction(() => {
3471
+ const existing = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(normalizedName);
3472
+ if (existing) {
3473
+ const lastSeenAt = existing.last_seen_at;
3474
+ const existingSessionId = existing.session_id;
3475
+ if (isActiveSession(lastSeenAt) && existingSessionId && existingSessionId !== sessionId) {
3476
+ return {
3477
+ conflict: true,
3478
+ error: "agent_conflict",
3479
+ message: `Agent "${normalizedName}" is already active (last seen: ${lastSeenAt}). Wait 30 minutes or use force takeover.`,
3480
+ existing_id: existing.id,
3481
+ existing_name: normalizedName,
3482
+ existing_session_id: existingSessionId,
3483
+ last_seen_at: lastSeenAt,
3484
+ session_hint: existingSessionId ? existingSessionId.slice(0, 8) : null,
3485
+ working_dir: null
3486
+ };
3487
+ }
3488
+ const tookOver = existingSessionId !== sessionId;
3489
+ db2.prepare(`
3490
+ UPDATE agent_presence
3491
+ SET session_id = ?, role = ?, project_id = ?, last_seen_at = strftime('%Y-%m-%dT%H:%M:%f', 'now')
3492
+ WHERE agent = ?
3493
+ `).run(sessionId, role || existing.role || "agent", projectId ?? existing.project_id ?? null, normalizedName);
3494
+ const updated = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(normalizedName);
3495
+ return { agent: parsePresence(updated), created: false, took_over: tookOver };
3480
3496
  }
3481
- const tookOver = existingSessionId !== sessionId;
3497
+ const id = crypto.randomUUID().slice(0, 8);
3498
+ const resolvedRole = role || "agent";
3482
3499
  db2.prepare(`
3483
- UPDATE agent_presence
3484
- SET session_id = ?, role = ?, last_seen_at = strftime('%Y-%m-%dT%H:%M:%f', 'now')
3485
- WHERE agent = ?
3486
- `).run(sessionId, role || existing.role || "agent", normalizedName);
3487
- const updated = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(normalizedName);
3488
- return { agent: parsePresence(updated), created: false, took_over: tookOver };
3489
- }
3490
- const id = crypto.randomUUID().slice(0, 8);
3491
- const resolvedRole = role || "agent";
3492
- db2.prepare(`
3493
- INSERT INTO agent_presence (id, agent, session_id, role, status, last_seen_at, created_at)
3494
- VALUES (?, ?, ?, ?, 'online', strftime('%Y-%m-%dT%H:%M:%f', 'now'), strftime('%Y-%m-%dT%H:%M:%f', 'now'))
3495
- `).run(id, normalizedName, sessionId, resolvedRole);
3496
- const created = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(normalizedName);
3497
- return { agent: parsePresence(created), created: true, took_over: false };
3500
+ INSERT INTO agent_presence (id, agent, session_id, role, project_id, status, last_seen_at, created_at)
3501
+ VALUES (?, ?, ?, ?, ?, 'online', strftime('%Y-%m-%dT%H:%M:%f', 'now'), strftime('%Y-%m-%dT%H:%M:%f', 'now'))
3502
+ `).run(id, normalizedName, sessionId, resolvedRole, projectId ?? null);
3503
+ const created = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(normalizedName);
3504
+ return { agent: parsePresence(created), created: true, took_over: false };
3505
+ }).immediate();
3506
+ return result;
3498
3507
  }
3499
3508
  function heartbeat(agent, status, metadata, sessionId) {
3500
3509
  const db2 = getDb();
@@ -1,6 +1,6 @@
1
1
  import type { AgentPresence, AgentConflictError, RegisterAgentResult } from "../types.js";
2
2
  export declare function isAgentConflict(result: RegisterAgentResult | AgentConflictError): result is AgentConflictError;
3
- export declare function registerAgent(name: string, sessionId: string, role?: string): RegisterAgentResult | AgentConflictError;
3
+ export declare function registerAgent(name: string, sessionId: string, role?: string, projectId?: string): RegisterAgentResult | AgentConflictError;
4
4
  export declare function heartbeat(agent: string, status?: string, metadata?: Record<string, unknown>, sessionId?: string): void;
5
5
  export declare function getPresence(agent: string): AgentPresence | null;
6
6
  export declare function listAgents(opts?: {
package/dist/types.d.ts CHANGED
@@ -116,6 +116,7 @@ export interface AgentPresence {
116
116
  agent: string;
117
117
  session_id: string | null;
118
118
  role: string;
119
+ project_id: string | null;
119
120
  status: string;
120
121
  last_seen_at: string;
121
122
  created_at: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/conversations",
3
- "version": "0.1.27",
3
+ "version": "0.1.29",
4
4
  "description": "Real-time CLI messaging for AI agents",
5
5
  "type": "module",
6
6
  "bin": {