@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 +53 -31
- package/bin/mcp.js +51 -30
- package/dist/index.d.ts +1 -1
- package/dist/index.js +29 -14
- package/dist/lib/identity.d.ts +4 -0
- package/dist/lib/presence.d.ts +1 -0
- package/dist/types.d.ts +5 -0
- package/package.json +1 -1
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
|
|
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 "${
|
|
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",
|
|
3365
|
-
const updated = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(
|
|
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,
|
|
3374
|
-
const created = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(
|
|
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,
|
|
3406
|
+
`).run(id, normalizedAgent, sessionId ?? null, resolvedStatus, metadataJson);
|
|
3392
3407
|
}
|
|
3393
3408
|
function getPresence(agent) {
|
|
3394
3409
|
const db2 = getDb();
|
|
3395
|
-
const
|
|
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
|
|
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
|
|
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(
|
|
3437
|
+
const conflict = db2.prepare("SELECT agent FROM agent_presence WHERE LOWER(agent) = ?").get(normalizedNew);
|
|
3419
3438
|
if (conflict)
|
|
3420
|
-
throw new Error(`Agent "${
|
|
3421
|
-
db2.prepare("UPDATE agent_presence SET agent = ? WHERE agent = ?").run(
|
|
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.
|
|
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
|
|
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 "${
|
|
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",
|
|
29791
|
-
const updated = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(
|
|
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,
|
|
29800
|
-
const created = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(
|
|
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,
|
|
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
|
|
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
|
|
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(
|
|
29857
|
+
const conflict = db2.prepare("SELECT agent FROM agent_presence WHERE LOWER(agent) = ?").get(normalizedNew);
|
|
29840
29858
|
if (conflict)
|
|
29841
|
-
throw new Error(`Agent "${
|
|
29842
|
-
db2.prepare("UPDATE agent_presence SET agent = ? WHERE agent = ?").run(
|
|
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.
|
|
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
|
|
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 "${
|
|
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",
|
|
3478
|
-
const updated = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(
|
|
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,
|
|
3487
|
-
const created = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(
|
|
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,
|
|
3514
|
+
`).run(id, normalizedAgent, sessionId ?? null, resolvedStatus, metadataJson);
|
|
3505
3515
|
}
|
|
3506
3516
|
function getPresence(agent) {
|
|
3507
3517
|
const db2 = getDb();
|
|
3508
|
-
const
|
|
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
|
|
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
|
|
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(
|
|
3545
|
+
const conflict = db2.prepare("SELECT agent FROM agent_presence WHERE LOWER(agent) = ?").get(normalizedNew);
|
|
3532
3546
|
if (conflict)
|
|
3533
|
-
throw new Error(`Agent "${
|
|
3534
|
-
db2.prepare("UPDATE agent_presence SET agent = ? WHERE agent = ?").run(
|
|
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,
|
package/dist/lib/identity.d.ts
CHANGED
|
@@ -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
|
*/
|
package/dist/lib/presence.d.ts
CHANGED
|
@@ -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;
|