@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 +6 -1
- package/bin/index.js +50 -40
- package/bin/mcp.js +50 -40
- package/dist/index.js +43 -34
- package/dist/lib/presence.d.ts +1 -1
- package/dist/types.d.ts +1 -0
- package/package.json +1 -1
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
|
|
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
|
|
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
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
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
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
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.
|
|
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
|
|
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
|
|
29783
|
-
|
|
29784
|
-
|
|
29785
|
-
|
|
29786
|
-
|
|
29787
|
-
|
|
29788
|
-
|
|
29789
|
-
|
|
29790
|
-
|
|
29791
|
-
|
|
29792
|
-
|
|
29793
|
-
|
|
29794
|
-
|
|
29795
|
-
|
|
29796
|
-
|
|
29797
|
-
|
|
29798
|
-
|
|
29799
|
-
|
|
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
|
-
|
|
29802
|
-
|
|
29803
|
-
|
|
29804
|
-
|
|
29805
|
-
|
|
29806
|
-
|
|
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.
|
|
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
|
|
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
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
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
|
|
3497
|
+
const id = crypto.randomUUID().slice(0, 8);
|
|
3498
|
+
const resolvedRole = role || "agent";
|
|
3482
3499
|
db2.prepare(`
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
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();
|
package/dist/lib/presence.d.ts
CHANGED
|
@@ -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